Skip to content

Commit 98f51c8

Browse files
author
cpriest
committed
Implementation of static isset/unset code, added numerous tests.
ADDED: zend_compile_string_inline() - Compiles code straight into current op_array. ADDED: Automatic setting of function purpose for internal functions to ZEND_FNP_UNDEFINED ADDED: Handling of static self::$a accessor handling. CHANGE: Changed the way that automated implementations are coded. All automatic implementations are now embeded/built PHP strings run through zend_compile_string_inline(). Performance testing shows excellent performance and the code is 10x simpler. ADDED: Automatic implementation of isset/unset in the case of no declaration at all, when appropriate. ADDED: Implementation of static isset/unset code. CHANGE: pass_two() was modified to catch static gets where there is no getter. Static setters work by converting any class::$a into the getter, then the followup zend_do_assign() backpatches the getter into a setter call. When no zend_do_assign() is made and there is no getter, this is caught in pass_two() - pass_two() also allows for more accurate error file/line reporting now as well.
1 parent e7cc406 commit 98f51c8

30 files changed

Lines changed: 750 additions & 116 deletions

‎Zend/zend.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions TS
691691
zend_execute_internal=NULL;
692692
#endif/* HAVE_SYS_SDT_H */
693693
zend_compile_string=compile_string;
694+
zend_compile_string_inline=compile_string_inline;
694695
zend_throw_exception_hook=NULL;
695696

696697
zend_init_opcodes_handlers();

‎Zend/zend_API.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
19681968
internal_function->function_name= (char*)ptr->fname;
19691969
internal_function->scope=scope;
19701970
internal_function->prototype=NULL;
1971+
internal_function->purpose=ZEND_FNP_UNDEFINED;
19711972
if (ptr->flags) {
19721973
if (!(ptr->flags&ZEND_ACC_PPP_MASK)) {
19731974
if (ptr->flags!=ZEND_ACC_DEPRECATED||scope) {

‎Zend/zend_compile.c‎

Lines changed: 323 additions & 101 deletions
Large diffs are not rendered by default.

‎Zend/zend_compile.h‎

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,22 @@
3636

3737
#defineIS_ACCESSOR(purpose) ( purpose >= ZEND_FNP_PROP_GETTER && purpose <= ZEND_FNP_PROP_UNSETTER )
3838

39-
#defineMAKE_ZNODE(zn, str) \
39+
#defineINIT_ZNODE(zn) \
4040
{ \
4141
zn.op_type = IS_CONST; \
4242
INIT_PZVAL(&zn.u.constant); \
43-
ZVAL_STRINGL(&zn.u.constant, str, strlen(str), 1); \
4443
zn.EA = 0; \
4544
}
45+
46+
#defineMAKE_ZNODE(zn, str) \
47+
{ \
48+
INIT_ZNODE(zn); \
49+
ZVAL_STRINGL(&zn.u.constant, str, strlen(str), 1); \
50+
}
4651
#defineMAKE_ZNODEL(zn, str, l) \
4752
{ \
48-
zn.op_type = IS_CONST; \
49-
INIT_PZVAL(&zn.u.constant); \
53+
INIT_ZNODE(zn); \
5054
ZVAL_STRINGL(&zn.u.constant, str, l, 1); \
51-
zn.EA = 0; \
5255
}
5356

5457
#defineSET_UNUSED(op) op ## _type = IS_UNUSED
@@ -378,7 +381,8 @@ typedef union _zend_function {
378381
zend_internal_functioninternal_function;
379382
} zend_function;
380383

381-
#defineZEND_ACC_NAME(func) &func->common.function_name[5]
384+
#defineZEND_ACC_NAME(func) zend_get_accessor_name_from_function(func TSRMLS_CC)
385+
#defineZEND_ACC_NAME_AI(ai) zend_get_accessor_name_from_accessor_info(ai TSRMLS_CC)
382386

383387
typedefstruct_zend_accessor_info {
384388
zend_uintflags;
@@ -455,6 +459,7 @@ void zend_init_compiler_context(TSRMLS_D);
455459

456460
externZEND_APIzend_op_array*(*zend_compile_file)(zend_file_handle*file_handle, inttypeTSRMLS_DC);
457461
externZEND_APIzend_op_array*(*zend_compile_string)(zval*source_string, char*filenameTSRMLS_DC);
462+
externZEND_APIvoid (*zend_compile_string_inline)(zval*source_string, char*filenameTSRMLS_DC);
458463

459464
ZEND_APIintlex_scan(zval*zendlvalTSRMLS_DC);
460465
voidstartup_scanner(TSRMLS_D);
@@ -545,6 +550,7 @@ void zend_do_handle_exception(TSRMLS_D);
545550
voidzend_declare_accessor(znode*var_nameTSRMLS_DC);
546551
voidzend_do_begin_accessor_declaration(znode*function_token, znode*var_name, znode*modifiers, intreturn_referenceTSRMLS_DC);
547552
voidzend_do_end_accessor_declaration(znode*function_token, znode*var_name, znode*modifiers, constznode*bodyTSRMLS_DC);
553+
voidzend_finalize_accessor(znode*var_nameTSRMLS_DC);
548554

549555
voidzend_do_begin_lambda_function_declaration(znode*result, znode*function_token, intreturn_reference, intis_staticTSRMLS_DC);
550556
voidzend_do_fetch_lexical_variable(znode*varname, zend_boolis_refTSRMLS_DC);
@@ -679,6 +685,12 @@ void zend_do_goto(const znode *label TSRMLS_DC);
679685
voidzend_resolve_goto_label(zend_op_array*op_array, zend_op*opline, intpass2TSRMLS_DC);
680686
voidzend_release_labels(TSRMLS_D);
681687

688+
constchar*zend_get_accessor_name_from_function(zend_function*funcTSRMLS_DC);
689+
constchar*zend_get_accessor_name_from_accessor_info(zend_accessor_info*aiTSRMLS_DC);
690+
zend_accessor_info*zend_get_accessor_info_from_function(zend_function*funcTSRMLS_DC);
691+
zend_accessor_info*zend_get_accessor_from_init_static_method_call(zend_op_array*op_array, zend_op*opline, constchar**context_name_outTSRMLS_DC);
692+
693+
682694
ZEND_APIvoidfunction_add_ref(zend_function*function);
683695

684696
#defineINITIAL_OP_ARRAY_SIZE 64
@@ -688,6 +700,7 @@ ZEND_API void function_add_ref(zend_function *function);
688700
/* helper functions in zend_language_scanner.l */
689701
ZEND_APIzend_op_array*compile_file(zend_file_handle*file_handle, inttypeTSRMLS_DC);
690702
ZEND_APIzend_op_array*compile_string(zval*source_string, char*filenameTSRMLS_DC);
703+
ZEND_APIvoidcompile_string_inline(zval*source_string, char*filenameTSRMLS_DC);
691704
ZEND_APIzend_op_array*compile_filename(inttype, zval*filenameTSRMLS_DC);
692705
ZEND_APIintzend_execute_scripts(inttypeTSRMLS_DC, zval**retval, intfile_count, ...);
693706
ZEND_APIintopen_file_for_scanning(zend_file_handle*file_handleTSRMLS_DC);

‎Zend/zend_language_parser.y‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ class_variable_accessor_declarations:
756756
T_VARIABLE'{'
757757
{ zend_declare_accessor(&$1TSRMLS_CC); CG(accessor_node) = &$1; }
758758
accessors
759-
{ efree($1.u.constant.value.str.val); }
759+
{ zend_finalize_accessor(&$1TSRMLS_CC); efree($1.u.constant.value.str.val); }
760760
'}'
761761
|class_variable_declaration';'
762762
;

‎Zend/zend_language_scanner.l‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,49 @@ zend_op_array *compile_string(zval *source_string, char *filename TSRMLS_DC)
779779
}
780780

781781

782+
voidcompile_string_inline(zval *source_string, char *filename TSRMLS_DC)
783+
{
784+
zend_lex_state original_lex_state;
785+
zval tmp;
786+
int compiler_result;
787+
zend_bool original_in_compilation = CG(in_compilation);
788+
789+
if (Z_STRLEN_P(source_string) == 0) {
790+
return;
791+
}
792+
793+
CG(in_compilation) = 1;
794+
795+
tmp = *source_string;
796+
zval_copy_ctor(&tmp);
797+
convert_to_string(&tmp);
798+
source_string = &tmp;
799+
800+
zend_save_lexical_state(&original_lex_state TSRMLS_CC);
801+
if (zend_prepare_string_for_scanning(source_string, filename TSRMLS_CC)==SUCCESS) {
802+
CG(zend_lineno) = original_lex_state.lineno;
803+
zend_bool orig_interactive = CG(interactive);
804+
805+
CG(interactive) = 0;
806+
CG(interactive) = orig_interactive;
807+
BEGIN(ST_IN_SCRIPTING);
808+
compiler_result = zendparse(TSRMLS_C);
809+
810+
if (SCNG(script_filtered)) {
811+
efree(SCNG(script_filtered));
812+
SCNG(script_filtered) = NULL;
813+
}
814+
815+
if (compiler_result==1) {
816+
CG(unclean_shutdown)=1;
817+
}
818+
}
819+
zend_restore_lexical_state(&original_lex_state TSRMLS_CC);
820+
zval_dtor(&tmp);
821+
CG(in_compilation) = original_in_compilation;
822+
}
823+
824+
782825
BEGIN_EXTERN_C()
783826
inthighlight_file(char *filename, zend_syntax_highlighter_ini *syntax_highlighter_ini TSRMLS_DC)
784827
{

‎Zend/zend_opcode.c‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,14 @@ static void zend_check_finally_breakout(zend_op_array *op_array, zend_op *opline
530530
ZEND_APIintpass_two(zend_op_array*op_arrayTSRMLS_DC)
531531
{
532532
zend_op*opline, *end;
533+
zend_booloriginal_in_compilation=CG(in_compilation);
534+
char*original_compiled_filename=CG(compiled_filename);
535+
intoriginal_zend_lineno=CG(zend_lineno);
533536

534-
if (op_array->type!=ZEND_USER_FUNCTION&&op_array->type!=ZEND_EVAL_CODE) {
537+
if( (op_array->fn_flags&ZEND_ACC_DONE_PASS_TWO) ) {
538+
return0;
539+
}
540+
if (op_array->type!=ZEND_USER_FUNCTION&&op_array->type!=ZEND_EVAL_CODE) {
535541
return0;
536542
}
537543
if (CG(compiler_options) &ZEND_COMPILE_EXTENDED_INFO) {
@@ -554,9 +560,13 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
554560
CG(context).literals_size=op_array->last_literal;
555561
}
556562

563+
CG(in_compilation) =1;
564+
CG(compiled_filename) = (char*)op_array->filename;
565+
557566
opline=op_array->opcodes;
558567
end=opline+op_array->last;
559568
while (opline<end) {
569+
CG(zend_lineno) =opline->lineno;
560570
if (opline->op1_type==IS_CONST) {
561571
opline->op1.zv=&op_array->literals[opline->op1.constant].constant;
562572
}
@@ -613,15 +623,39 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
613623
opline->opcode=ZEND_GENERATOR_RETURN;
614624
}
615625
break;
626+
caseZEND_INIT_STATIC_METHOD_CALL: {
627+
/** Is this a call to a static getter, static setters are dependent on static getters (which get backpatched to setter calls), so during compilation
628+
* a static getter call may be in the op_array and if no zend_do_assign() has been called to backpatch it, then it's an illegal getter call, this
629+
* checks to see that by this point any getter that has not been backpatched, actually exists as a getter, or errors. */
630+
constchar*context_name; /* Does not need to be free'd */
631+
632+
zend_accessor_info*ai=zend_get_accessor_from_init_static_method_call(op_array, opline, &context_nameTSRMLS_CC);
633+
if(ai) {
634+
if(Z_STRVAL_P(opline->op2.zv)[2] =='g') {
635+
if(!ai->getter) {
636+
zend_error_noreturn(E_ERROR, "Cannot get property %s::$%s, no getter defined.", context_name, Z_STRVAL_P(opline->op2.zv)+5);
637+
}
638+
} elseif (Z_STRVAL_P(opline->op2.zv)[2] =='s') {
639+
if(!ai->setter) {
640+
zend_error_noreturn(E_ERROR, "Cannot set property %s::$%s, no setter defined.", context_name, Z_STRVAL_P(opline->op2.zv)+5);
641+
}
642+
}
643+
}
644+
}
645+
break;
616646
}
617647
ZEND_VM_SET_OPCODE_HANDLER(opline);
618648
opline++;
619649
}
620650

621651
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
652+
CG(in_compilation) =original_in_compilation;
653+
CG(compiled_filename) =original_compiled_filename;
654+
CG(zend_lineno) =original_zend_lineno;
622655
return0;
623656
}
624657

658+
625659
intprint_class(zend_class_entry*class_entryTSRMLS_DC)
626660
{
627661
printf("Class %s:\n", class_entry->name);

‎tests/classes/accessor_interface_error.phpt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ class TimePeriod implements Hours {
1616

1717
?>
1818
--EXPECTF--
19-
Fatal error: Class TimePeriod contains 2 abstract accessors and must be declared abstract or implement the remaining accessors (get Hours::$Hours, set Hours::$Hours) in %s on line %d
19+
Fatal error: Class TimePeriod contains 4 abstract accessors and must be declared abstract or implement the remaining accessors (get Hours::$Hours, set Hours::$Hours, isset Hours::$Hours, ...) in %s on line %d
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
ZE2 Tests that a read-only defined getter produces an error if unset() is attempted
3+
--FILE--
4+
<?php
5+
6+
class AccessorTest {
7+
publicread-only $b {
8+
get {
9+
echo"get AccessorTest::\$b Called.\n";
10+
return5;
11+
}
12+
}
13+
}
14+
15+
$o = newAccessorTest();
16+
unset($o->b);
17+
?>
18+
--EXPECTF--
19+
Fatal error: Cannot unset read-only property AccessorTest::$b. in %s on line %d
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
ZE2 Tests that a undefined getter produces an error upon isset()
3+
--FILE--
4+
<?php
5+
6+
class AccessorTest {
7+
public$b {
8+
set;
9+
}
10+
}
11+
12+
$o = newAccessorTest();
13+
14+
echo"isset(\$o->b) = ".(int)isset($o->b)."\n";
15+
?>
16+
--EXPECTF--
17+
Fatal error: Cannot get property AccessorTest::$b, no getter defined. in %s on line %d

0 commit comments

Comments
 (0)