@@ -1490,14 +1490,32 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
14901490 zend_error_noreturn (E_COMPILE_ERROR , "Access level to %s::$%s must be %s (as in class %s)%s" , ZSTR_VAL (ce -> name ), ZSTR_VAL (key ), zend_visibility_string (parent_info -> flags ), ZSTR_VAL (parent_info -> ce -> name ), (parent_info -> flags & ZEND_ACC_PUBLIC ) ? "" : " or weaker" );
14911491 }
14921492 if (!(child_info -> flags & ZEND_ACC_STATIC ) && !(parent_info -> flags & ZEND_ACC_VIRTUAL )) {
1493+ /* If we added hooks to the child property, we use the child's slot for
1494+ * storage to keep the parent slot set to IS_UNDEF. This automatically
1495+ * picks the slow path in the JIT. */
1496+ bool use_child_prop = !parent_info -> hooks && child_info -> hooks ;
1497+
1498+ if (use_child_prop && child_info -> offset == ZEND_VIRTUAL_PROPERTY_OFFSET ) {
1499+ child_info -> offset = OBJ_PROP_TO_OFFSET (ce -> default_properties_count );
1500+ ce -> default_properties_count ++ ;
1501+ ce -> default_properties_table = perealloc (ce -> default_properties_table , sizeof (zval ) * ce -> default_properties_count , ce -> type == ZEND_INTERNAL_CLASS );
1502+ zval * property_default_ptr = & ce -> default_properties_table [OBJ_PROP_TO_NUM (child_info -> offset )];
1503+ ZVAL_UNDEF (property_default_ptr );
1504+ Z_PROP_FLAG_P (property_default_ptr ) = IS_PROP_UNINIT ;
1505+ }
1506+
14931507 int parent_num = OBJ_PROP_TO_NUM (parent_info -> offset );
14941508 if (child_info -> offset != ZEND_VIRTUAL_PROPERTY_OFFSET ) {
1495- int child_num = OBJ_PROP_TO_NUM (child_info -> offset );
1496-
14971509 /* Don't keep default properties in GC (they may be freed by opcache) */
14981510 zval_ptr_dtor_nogc (& (ce -> default_properties_table [parent_num ]));
1499- ce -> default_properties_table [parent_num ] = ce -> default_properties_table [child_num ];
1500- ZVAL_UNDEF (& ce -> default_properties_table [child_num ]);
1511+
1512+ if (use_child_prop ) {
1513+ ZVAL_UNDEF (& ce -> default_properties_table [parent_num ]);
1514+ } else {
1515+ int child_num = OBJ_PROP_TO_NUM (child_info -> offset );
1516+ ce -> default_properties_table [parent_num ] = ce -> default_properties_table [child_num ];
1517+ ZVAL_UNDEF (& ce -> default_properties_table [child_num ]);
1518+ }
15011519 } else {
15021520 /* Default value was removed in child, remove it from parent too. */
15031521 if (ZEND_TYPE_IS_SET (child_info -> type )) {
@@ -1507,7 +1525,9 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
15071525 }
15081526 }
15091527
1510- child_info -> offset = parent_info -> offset ;
1528+ if (!use_child_prop ) {
1529+ child_info -> offset = parent_info -> offset ;
1530+ }
15111531 child_info -> flags &= ~ZEND_ACC_VIRTUAL ;
15121532 }
15131533
@@ -1682,7 +1702,8 @@ void zend_build_properties_info_table(zend_class_entry *ce)
16821702 ZEND_HASH_MAP_FOREACH_PTR (& ce -> properties_info , prop ) {
16831703 if (prop -> ce == ce && (prop -> flags & ZEND_ACC_STATIC ) == 0
16841704 && !(prop -> flags & ZEND_ACC_VIRTUAL )) {
1685- table [OBJ_PROP_TO_NUM (prop -> offset )] = prop ;
1705+ uint32_t prop_table_offset = OBJ_PROP_TO_NUM (!(prop -> prototype -> flags & ZEND_ACC_VIRTUAL ) ? prop -> prototype -> offset : prop -> offset );
1706+ table [prop_table_offset ] = prop ;
16861707 }
16871708 } ZEND_HASH_FOREACH_END ();
16881709}
@@ -1696,8 +1717,12 @@ ZEND_API void zend_verify_hooked_property(zend_class_entry *ce, zend_property_in
16961717 /* We specified a default value (otherwise offset would be -1), but the virtual flag wasn't
16971718 * removed during inheritance. */
16981719 if ((prop_info -> flags & ZEND_ACC_VIRTUAL ) && prop_info -> offset != ZEND_VIRTUAL_PROPERTY_OFFSET ) {
1699- zend_error_noreturn (E_COMPILE_ERROR ,
1700- "Cannot specify default value for virtual hooked property %s::$%s" , ZSTR_VAL (ce -> name ), ZSTR_VAL (prop_name ));
1720+ if (Z_TYPE (ce -> default_properties_table [OBJ_PROP_TO_NUM (prop_info -> offset )]) == IS_UNDEF ) {
1721+ prop_info -> offset = ZEND_VIRTUAL_PROPERTY_OFFSET ;
1722+ } else {
1723+ zend_error_noreturn (E_COMPILE_ERROR ,
1724+ "Cannot specify default value for virtual hooked property %s::$%s" , ZSTR_VAL (ce -> name ), ZSTR_VAL (prop_name ));
1725+ }
17011726 }
17021727 /* If the property turns backed during inheritance and no type and default value are set, we want
17031728 * the default value to be null. */
0 commit comments