|
28 | 28 | #include "zend_API.h" |
29 | 29 | #include "zend_exceptions.h" |
30 | 30 | #include "zend_interfaces.h" |
| 31 | +#include "zend_types.h" |
31 | 32 | #include "zend_virtual_cwd.h" |
32 | 33 | #include "zend_multibyte.h" |
33 | 34 | #include "zend_language_scanner.h" |
@@ -4968,6 +4969,53 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args) |
4968 | 4969 | return SUCCESS; |
4969 | 4970 | } |
4970 | 4971 |
|
| 4972 | +static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) /* {{{ */ |
| 4973 | +{ |
| 4974 | + /* Special case: printf with a single constant string argument and no format specifiers. |
| 4975 | + * In this case, just emit ECHO and return the string length if needed. */ |
| 4976 | + if (args->children == 1) { |
| 4977 | + zend_eval_const_expr(&args->child[0]); |
| 4978 | + if (args->child[0]->kind != ZEND_AST_ZVAL) { |
| 4979 | + return FAILURE; |
| 4980 | + } |
| 4981 | + zval *format_string = zend_ast_get_zval(args->child[0]); |
| 4982 | + if (Z_TYPE_P(format_string) != IS_STRING) { |
| 4983 | + return FAILURE; |
| 4984 | + } |
| 4985 | + /* Check if there are any format specifiers */ |
| 4986 | + if (!memchr(Z_STRVAL_P(format_string), '%', Z_STRLEN_P(format_string))) { |
| 4987 | + /* No format specifiers - just emit ECHO and return string length */ |
| 4988 | + znode format_node; |
| 4989 | + zend_compile_expr(&format_node, args->child[0]); |
| 4990 | + zend_emit_op(NULL, ZEND_ECHO, &format_node, NULL); |
| 4991 | + |
| 4992 | + /* Return the string length as a constant if the result is used */ |
| 4993 | + result->op_type = IS_CONST; |
| 4994 | + ZVAL_LONG(&result->u.constant, Z_STRLEN_P(format_string)); |
| 4995 | + return SUCCESS; |
| 4996 | + } |
| 4997 | + } |
| 4998 | + |
| 4999 | + /* Fall back to sprintf optimization for format strings with specifiers */ |
| 5000 | + znode rope_result; |
| 5001 | + if (zend_compile_func_sprintf(&rope_result, args) != SUCCESS) { |
| 5002 | + return FAILURE; |
| 5003 | + } |
| 5004 | + |
| 5005 | + /* printf() returns the amount of bytes written, so just an ECHO of the |
| 5006 | + * resulting sprintf() optimisation might not be enough. At this early |
| 5007 | + * stage we can't detect if the result is actually used, so we just emit |
| 5008 | + * the opcodes and let them be cleaned up by the dead code elimination |
| 5009 | + * pass in the Zend Optimizer if the result of the printf() is in fact |
| 5010 | + * unused */ |
| 5011 | + znode copy; |
| 5012 | + zend_emit_op_tmp(©, ZEND_COPY_TMP, &rope_result, NULL); |
| 5013 | + zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL); |
| 5014 | + zend_emit_op_tmp(result, ZEND_STRLEN, ©, NULL); |
| 5015 | + |
| 5016 | + return SUCCESS; |
| 5017 | +} |
| 5018 | + |
4971 | 5019 | static zend_result zend_compile_func_clone(znode *result, zend_ast_list *args) |
4972 | 5020 | { |
4973 | 5021 | znode arg_node; |
@@ -5050,6 +5098,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string * |
5050 | 5098 | return zend_compile_func_array_key_exists(result, args); |
5051 | 5099 | } else if (zend_string_equals_literal(lcname, "sprintf")) { |
5052 | 5100 | return zend_compile_func_sprintf(result, args); |
| 5101 | + } else if (zend_string_equals_literal(lcname, "printf")) { |
| 5102 | + return zend_compile_func_printf(result, args); |
5053 | 5103 | } else if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_CLONE))) { |
5054 | 5104 | return zend_compile_func_clone(result, args); |
5055 | 5105 | } else { |
|
0 commit comments