@@ -362,21 +362,18 @@ static zend_always_inline const zend_class_entry *get_fake_or_executed_scope(voi
362362 }
363363}
364364
365- /* Resolve a property for reading (write_access == false) or for
366- * writing/unsetting (write_access == true). Write-kind resolution verifies
367- * asymmetric set visibility at population time: when the running scope lacks
368- * set access, the result is still returned (the caller's state-dependent
369- * slow path decides between an error and the __set fallback), but the
370- * runtime cache is NOT populated. A populated write-site cache slot
371- * therefore guarantees set access, which lets the VM's cached direct-assign
372- * fast path skip the per-write asymmetric visibility check. */
373- static zend_never_inline uintptr_t zend_get_property_offset_slow (zend_class_entry * ce , zend_string * member , int silent , void * * cache_slot , const zend_property_info * * info_ptr , bool write_access ) /* {{{ */
365+ static zend_always_inline uintptr_t zend_get_property_offset (zend_class_entry * ce , zend_string * member , int silent , void * * cache_slot , const zend_property_info * * info_ptr ) /* {{{ */
374366{
375367 zval * zv ;
376368 zend_property_info * property_info ;
377369 uint32_t flags ;
378370 uintptr_t offset ;
379371
372+ if (cache_slot && EXPECTED (ce == CACHED_PTR_EX (cache_slot ))) {
373+ * info_ptr = CACHED_PTR_EX (cache_slot + 2 );
374+ return (uintptr_t )CACHED_PTR_EX (cache_slot + 1 );
375+ }
376+
380377 if (UNEXPECTED (zend_hash_num_elements (& ce -> properties_info ) == 0 )
381378 || UNEXPECTED ((zv = zend_hash_find (& ce -> properties_info , member )) == NULL )) {
382379 if (UNEXPECTED (ZSTR_VAL (member )[0 ] == '\0' ) && ZSTR_LEN (member ) != 0 ) {
@@ -443,26 +440,6 @@ static zend_never_inline uintptr_t zend_get_property_offset_slow(zend_class_entr
443440 return ZEND_DYNAMIC_PROPERTY_OFFSET ;
444441 }
445442
446- if (cache_slot
447- && write_access
448- && UNEXPECTED (flags & ZEND_ACC_PPP_SET_MASK )
449- && !(flags & ZEND_ACC_PUBLIC_SET )) {
450- const zend_class_entry * scope = get_fake_or_executed_scope ();
451-
452- if (property_info -> ce != scope
453- && (!(flags & ZEND_ACC_PROTECTED_SET )
454- || !is_protected_compatible_scope (property_info -> prototype -> ce , scope ))) {
455- /* Set access denied for this scope: resolve as usual, but keep
456- * the cache slot empty so every such write stays on the slow
457- * path with its state-dependent handling. Invalidate the key
458- * too: downstream code (e.g. the hooked simple-write marking)
459- * assumes resolution populated the slot and would otherwise
460- * flag a stale polymorphic entry. */
461- CACHE_PTR_EX (cache_slot , NULL );
462- cache_slot = NULL ;
463- }
464- }
465-
466443 if (property_info -> hooks ) {
467444 * info_ptr = property_info ;
468445 if (cache_slot ) {
@@ -486,24 +463,12 @@ static zend_never_inline uintptr_t zend_get_property_offset_slow(zend_class_entr
486463}
487464/* }}} */
488465
489- /* Keep the inlined body limited to the cache-hit fast path; resolution
490- * (visibility, modules, surfaces, hooks) stays out of line so callers'
491- * hot paths remain compact. */
492- static zend_always_inline uintptr_t zend_get_property_offset (zend_class_entry * ce , zend_string * member , int silent , void * * cache_slot , const zend_property_info * * info_ptr , bool write_access )
493- {
494- if (cache_slot && EXPECTED (ce == CACHED_PTR_EX (cache_slot ))) {
495- * info_ptr = CACHED_PTR_EX (cache_slot + 2 );
496- return (uintptr_t )CACHED_PTR_EX (cache_slot + 1 );
497- }
498- return zend_get_property_offset_slow (ce , member , silent , cache_slot , info_ptr , write_access );
499- }
500-
501466static ZEND_COLD void zend_wrong_offset (zend_class_entry * ce , zend_string * member ) /* {{{ */
502467{
503468 const zend_property_info * dummy ;
504469
505470 /* Trigger the correct error */
506- zend_get_property_offset (ce , member , 0 , NULL , & dummy , false );
471+ zend_get_property_offset (ce , member , 0 , NULL , & dummy );
507472}
508473/* }}} */
509474
@@ -784,7 +749,7 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
784749#endif
785750
786751 /* make zend_get_property_info silent if we have getter - we may want to use it */
787- property_offset = zend_get_property_offset (zobj -> ce , name , (type == BP_VAR_IS ) || (zobj -> ce -> __get != NULL ), cache_slot , & prop_info , false );
752+ property_offset = zend_get_property_offset (zobj -> ce , name , (type == BP_VAR_IS ) || (zobj -> ce -> __get != NULL ), cache_slot , & prop_info );
788753
789754 if (EXPECTED (IS_VALID_PROPERTY_OFFSET (property_offset ))) {
790755try_again :
@@ -1107,7 +1072,7 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
11071072 uint32_t * guard = NULL ;
11081073 ZEND_ASSERT (!Z_ISREF_P (value ));
11091074
1110- property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __set != NULL ), cache_slot , & prop_info , true );
1075+ property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __set != NULL ), cache_slot , & prop_info );
11111076
11121077 if (EXPECTED (IS_VALID_PROPERTY_OFFSET (property_offset ))) {
11131078try_again :
@@ -1132,6 +1097,11 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
11321097 if ((prop_info -> flags & ZEND_ACC_PPP_SET_MASK ) && !zend_asymmetric_property_has_set_access (prop_info )) {
11331098 zend_asymmetric_visibility_property_modification_error (prop_info , "modify" );
11341099 variable_ptr = & EG (error_zval );
1100+ if (cache_slot ) {
1101+ /* Reset cache slot to dodge fast path in next execution. */
1102+ CACHE_POLYMORPHIC_PTR_EX (cache_slot , NULL , NULL );
1103+ CACHE_PTR_EX (cache_slot + 2 , NULL );
1104+ }
11351105 goto exit ;
11361106 }
11371107 }
@@ -1469,7 +1439,7 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
14691439 fprintf (stderr , "Ptr object #%d property: %s\n" , zobj -> handle , ZSTR_VAL (name ));
14701440#endif
14711441
1472- property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __get != NULL ), cache_slot , & prop_info , true );
1442+ property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __get != NULL ), cache_slot , & prop_info );
14731443
14741444 if (EXPECTED (IS_VALID_PROPERTY_OFFSET (property_offset ))) {
14751445try_again :
@@ -1597,7 +1567,7 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
15971567 const zend_property_info * prop_info = NULL ;
15981568 uint32_t * guard = NULL ;
15991569
1600- property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __unset != NULL ), cache_slot , & prop_info , true );
1570+ property_offset = zend_get_property_offset (zobj -> ce , name , (zobj -> ce -> __unset != NULL ), cache_slot , & prop_info );
16011571
16021572 if (EXPECTED (IS_VALID_PROPERTY_OFFSET (property_offset ))) {
16031573 zval * slot = OBJ_PROP (zobj , property_offset );
@@ -2407,7 +2377,7 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has
24072377 uintptr_t property_offset ;
24082378 const zend_property_info * prop_info = NULL ;
24092379
2410- property_offset = zend_get_property_offset (zobj -> ce , name , 1 , cache_slot , & prop_info , false );
2380+ property_offset = zend_get_property_offset (zobj -> ce , name , 1 , cache_slot , & prop_info );
24112381
24122382 if (EXPECTED (IS_VALID_PROPERTY_OFFSET (property_offset ))) {
24132383try_again :
0 commit comments