diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 65051223c157d..6f934f7a3fd91 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4739,9 +4739,9 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) char *p; char *end; - uint32_t string_placeholder_count; + uint32_t placeholder_count; - string_placeholder_count = 0; + placeholder_count = 0; p = Z_STRVAL_P(format_string); end = p + Z_STRLEN_P(format_string); @@ -4757,13 +4757,14 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) } switch (*q) { - case 's': - string_placeholder_count++; - break; - case '%': - break; - default: - return FAILURE; + case 's': + case 'd': + placeholder_count++; + break; + case '%': + break; + default: + return FAILURE; } p = q; @@ -4771,7 +4772,7 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) } /* Bail out if the number of placeholders does not match the number of values. */ - if (string_placeholder_count != (args->children - 1)) { + if (placeholder_count != (args->children - 1)) { return FAILURE; } @@ -4785,27 +4786,22 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) znode *elements = NULL; - if (string_placeholder_count > 0) { - elements = safe_emalloc(sizeof(*elements), string_placeholder_count, 0); + if (placeholder_count > 0) { + elements = safe_emalloc(sizeof(*elements), placeholder_count, 0); } /* Compile the value expressions first for error handling that is consistent * with a function call: Values that fail to convert to a string may emit errors. */ - for (uint32_t i = 0; i < string_placeholder_count; i++) { + for (uint32_t i = 0; i < placeholder_count; i++) { zend_compile_expr(elements + i, args->child[1 + i]); - if (elements[i].op_type == IS_CONST) { - if (Z_TYPE(elements[i].u.constant) != IS_ARRAY) { - convert_to_string(&elements[i].u.constant); - } - } } uint32_t rope_elements = 0; uint32_t rope_init_lineno = -1; zend_op *opline = NULL; - string_placeholder_count = 0; + placeholder_count = 0; p = Z_STRVAL_P(format_string); end = p + Z_STRLEN_P(format_string); char *offset = p; @@ -4817,7 +4813,7 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) char *q = p + 1; ZEND_ASSERT(q < end); - ZEND_ASSERT(*q == 's' || *q == '%'); + ZEND_ASSERT(*q == 's' || *q == 'd' || *q == '%'); if (*q == '%') { /* Optimization to not create a dedicated rope element for the literal '%': @@ -4837,21 +4833,32 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) opline = zend_compile_rope_add(result, rope_elements++, &const_node); } - if (*q == 's') { - /* Perform the cast of constant arrays when actually evaluating corresponding placeholder - * for correct error reporting. - */ - if (elements[string_placeholder_count].op_type == IS_CONST) { - if (Z_TYPE(elements[string_placeholder_count].u.constant) == IS_ARRAY) { - zend_emit_op_tmp(&elements[string_placeholder_count], ZEND_CAST, &elements[string_placeholder_count], NULL)->extended_value = IS_STRING; - } + if (*q != '%') { + switch (*q) { + case 's': + /* Perform the cast of constants when actually evaluating the corresponding placeholder + * for correct error reporting. + */ + if (elements[placeholder_count].op_type == IS_CONST) { + if (Z_TYPE(elements[placeholder_count].u.constant) == IS_ARRAY) { + zend_emit_op_tmp(&elements[placeholder_count], ZEND_CAST, &elements[placeholder_count], NULL)->extended_value = IS_STRING; + } else { + convert_to_string(&elements[placeholder_count].u.constant); + } + } + break; + case 'd': + zend_emit_op_tmp(&elements[placeholder_count], ZEND_CAST, &elements[placeholder_count], NULL)->extended_value = IS_LONG; + break; + EMPTY_SWITCH_DEFAULT_CASE(); } + if (rope_elements == 0) { rope_init_lineno = get_next_op_number(); } - opline = zend_compile_rope_add(result, rope_elements++, &elements[string_placeholder_count]); + opline = zend_compile_rope_add(result, rope_elements++, &elements[placeholder_count]); - string_placeholder_count++; + placeholder_count++; } p = q; diff --git a/ext/standard/tests/strings/sprintf_rope_optimization_001.phpt b/ext/standard/tests/strings/sprintf_rope_optimization_001.phpt index 91fdddfe029d4..634e575729d6f 100644 --- a/ext/standard/tests/strings/sprintf_rope_optimization_001.phpt +++ b/ext/standard/tests/strings/sprintf_rope_optimization_001.phpt @@ -100,6 +100,10 @@ try { var_dump(sprintf()); } catch (\Throwable $e) {echo $e, PHP_EOL; } echo PHP_EOL; +try { + var_dump(sprintf('%s-%s-%s', true, false, true)); +} catch (\Throwable $e) {echo $e, PHP_EOL; } echo PHP_EOL; + echo "Done"; ?> --EXPECTF-- @@ -173,4 +177,6 @@ Stack trace: #0 %s(97): sprintf() #1 {main} +string(4) "1--1" + Done diff --git a/ext/standard/tests/strings/sprintf_rope_optimization_003.phpt b/ext/standard/tests/strings/sprintf_rope_optimization_003.phpt new file mode 100644 index 0000000000000..9a0d819b2771e --- /dev/null +++ b/ext/standard/tests/strings/sprintf_rope_optimization_003.phpt @@ -0,0 +1,134 @@ +--TEST-- +Test sprintf() function : Rope Optimization for '%d'. +--FILE-- + +--EXPECTF-- +string(2) "42" + +string(8) "42/-1337" + +string(10) "42/-1337/3" + + +Warning: Object of class stdClass could not be converted to int in %s on line 33 +string(12) "42/-1337/3/1" + +string(2) "1/" + +string(2) "/1" + +string(3) "/1/" + +string(13) "42/-1337/1/42" + +string(8) "0/53/1/3" + +Called +Called +Called + +Warning: Object of class Foo could not be converted to int in %s on line 57 + +Warning: Object of class Foo could not be converted to int in %s on line 57 + +Warning: Object of class Foo could not be converted to int in %s on line 57 +string(5) "1/1/1" + +string(5) "0/0/0" + +string(42) "9223372036854775807/0/-9223372036854775808" +string(24) "2147483647/0/-2147483648" + +string(5) "1/0/1" + +string(3) "1/0" + +string(1) "0" + +Done diff --git a/ext/standard/tests/strings/sprintf_rope_optimization_004.phpt b/ext/standard/tests/strings/sprintf_rope_optimization_004.phpt new file mode 100644 index 0000000000000..a1c937aeb2c2e --- /dev/null +++ b/ext/standard/tests/strings/sprintf_rope_optimization_004.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test sprintf() function : Rope Optimization for '%d' with GMP objects +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECTF-- +string(63) "42/-1337/4089650035136921599/1000000000000000000000000000000000" + +Done