@@ -4712,6 +4712,153 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args
4712
4712
}
4713
4713
/* }}} */
4714
4714
4715
+ static zend_op * zend_compile_rope_add (znode * result , uint32_t num , znode * elem_node );
4716
+ static zend_op * zend_compile_rope_add_ex (zend_op * opline , znode * result , uint32_t num , znode * elem_node );
4717
+ static void zend_compile_rope_finalize (znode * result , uint32_t j , zend_op * init_opline , zend_op * opline );
4718
+
4719
+ static zend_result zend_compile_func_sprintf (znode * result , zend_ast_list * args ) /* {{{ */
4720
+ {
4721
+ if (args -> children < 1 ) {
4722
+ return FAILURE ;
4723
+ }
4724
+
4725
+ zend_eval_const_expr (& args -> child [0 ]);
4726
+ if (args -> child [0 ]-> kind != ZEND_AST_ZVAL ) {
4727
+ return FAILURE ;
4728
+ }
4729
+
4730
+ zval * format_string = zend_ast_get_zval (args -> child [0 ]);
4731
+ if (Z_TYPE_P (format_string ) != IS_STRING ) {
4732
+ return FAILURE ;
4733
+ }
4734
+ if (Z_STRLEN_P (format_string ) >= 256 ) {
4735
+ return FAILURE ;
4736
+ }
4737
+
4738
+ char * p ;
4739
+ char * end ;
4740
+ uint32_t string_placeholder_count ;
4741
+
4742
+ string_placeholder_count = 0 ;
4743
+ p = Z_STRVAL_P (format_string );
4744
+ end = p + Z_STRLEN_P (format_string );
4745
+
4746
+ for (;;) {
4747
+ p = memchr (p , '%' , end - p );
4748
+ if (!p ) {
4749
+ break ;
4750
+ }
4751
+
4752
+ char * q = p + 1 ;
4753
+ if (q == end ) {
4754
+ return FAILURE ;
4755
+ }
4756
+
4757
+ switch (* q ) {
4758
+ case 's' :
4759
+ string_placeholder_count ++ ;
4760
+ break ;
4761
+ case '%' :
4762
+ break ;
4763
+ default :
4764
+ return FAILURE ;
4765
+ }
4766
+
4767
+ p = q ;
4768
+ p ++ ;
4769
+ }
4770
+
4771
+ /* Bail out if the number of placeholders does not match the number of values. */
4772
+ if (string_placeholder_count != (args -> children - 1 )) {
4773
+ return FAILURE ;
4774
+ }
4775
+
4776
+ znode * elements = NULL ;
4777
+
4778
+ if (string_placeholder_count > 0 ) {
4779
+ elements = safe_emalloc (sizeof (* elements ), string_placeholder_count , 0 );
4780
+ }
4781
+
4782
+ /* Compile the value expressions first for error handling that is consistent
4783
+ * with a function call: Values that fail to convert to a string may emit errors.
4784
+ */
4785
+ for (size_t i = 0 ; i < string_placeholder_count ; i ++ ) {
4786
+ zend_compile_expr (elements + i , args -> child [1 + i ]);
4787
+ if (elements [i ].op_type == IS_CONST ) {
4788
+ if (Z_TYPE (elements [i ].u .constant ) == IS_ARRAY ) {
4789
+ zend_emit_op_tmp (& elements [i ], ZEND_CAST , & elements [i ], NULL )-> extended_value = IS_STRING ;
4790
+ } else {
4791
+ convert_to_string (& elements [i ].u .constant );
4792
+ }
4793
+ }
4794
+ }
4795
+
4796
+ uint32_t rope_elements = 0 ;
4797
+ uint32_t rope_init_lineno = get_next_op_number ();
4798
+ zend_op * opline = NULL ;
4799
+
4800
+ string_placeholder_count = 0 ;
4801
+ p = Z_STRVAL_P (format_string );
4802
+ end = p + Z_STRLEN_P (format_string );
4803
+ char * offset = p ;
4804
+ for (;;) {
4805
+ p = memchr (p , '%' , end - p );
4806
+ if (!p ) {
4807
+ break ;
4808
+ }
4809
+
4810
+ char * q = p + 1 ;
4811
+ ZEND_ASSERT (q < end );
4812
+ ZEND_ASSERT (* q == 's' || * q == '%' );
4813
+
4814
+ if (* q == '%' ) {
4815
+ /* Optimization to not create a dedicated rope element for the literal '%':
4816
+ * Include the first '%' within the "constant" part instead of dropping the
4817
+ * full placeholder.
4818
+ */
4819
+ p ++ ;
4820
+ }
4821
+
4822
+ if (p != offset ) {
4823
+ znode const_node ;
4824
+ const_node .op_type = IS_CONST ;
4825
+ ZVAL_STRINGL (& const_node .u .constant , offset , p - offset );
4826
+ opline = zend_compile_rope_add (result , rope_elements ++ , & const_node );
4827
+ }
4828
+
4829
+ if (* q == 's' ) {
4830
+ opline = zend_compile_rope_add (result , rope_elements ++ , & elements [string_placeholder_count ]);
4831
+
4832
+ string_placeholder_count ++ ;
4833
+ }
4834
+
4835
+ p = q ;
4836
+ p ++ ;
4837
+ offset = p ;
4838
+ }
4839
+ if (end != offset ) {
4840
+ /* Add the constant part after the last placeholder. */
4841
+ znode const_node ;
4842
+ const_node .op_type = IS_CONST ;
4843
+ ZVAL_STRINGL (& const_node .u .constant , offset , end - offset );
4844
+ opline = zend_compile_rope_add (result , rope_elements ++ , & const_node );
4845
+ }
4846
+ if (rope_elements == 0 ) {
4847
+ /* Handle empty format strings. */
4848
+ znode const_node ;
4849
+ const_node .op_type = IS_CONST ;
4850
+ ZVAL_EMPTY_STRING (& const_node .u .constant );
4851
+ opline = zend_compile_rope_add (result , rope_elements ++ , & const_node );
4852
+ }
4853
+ ZEND_ASSERT (opline != NULL );
4854
+
4855
+ zend_op * init_opline = CG (active_op_array )-> opcodes + rope_init_lineno ;
4856
+ zend_compile_rope_finalize (result , rope_elements , init_opline , opline );
4857
+ efree (elements );
4858
+
4859
+ return SUCCESS ;
4860
+ }
4861
+
4715
4862
static zend_result zend_try_compile_special_func_ex (znode * result , zend_string * lcname , zend_ast_list * args , zend_function * fbc , uint32_t type ) /* {{{ */
4716
4863
{
4717
4864
if (zend_string_equals_literal (lcname , "strlen" )) {
@@ -4778,6 +4925,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
4778
4925
return zend_compile_func_array_slice (result , args );
4779
4926
} else if (zend_string_equals_literal (lcname , "array_key_exists" )) {
4780
4927
return zend_compile_func_array_key_exists (result , args );
4928
+ } else if (zend_string_equals_literal (lcname , "sprintf" )) {
4929
+ return zend_compile_func_sprintf (result , args );
4781
4930
} else {
4782
4931
return FAILURE ;
4783
4932
}
0 commit comments