[php-src] PR #23434: [skip ci] Document the closure $this internals change in UPGRADING.INTERNALS
| From: | nicolas-grekas | Date: | Mon, 24 Aug 2026 12:10:56 +0000 |
| Subject: | [php-src] PR #23434: [skip ci] Document the closure $this internals change in UPGRADING.INTERNALS | ||
| Groups: | php.git-pulls | ||
| Request: | Send a blank email to git-pulls+get-38102@lists.php.net to get a copy of this message | ||
Pull Request: https://github.com/php/php-src/pull/23434
Author: nicolas-grekas
zend_create_closure(), zend_create_fake_closure() and
zend_create_partial_closure() take the bound $this as a
zend_object* since fbb2e1f23d6, and zend_get_closure_this_ptr() returns
one — NULL when the closure is unbound, where it used to return a zval* that is
IS_UNDEF — since 7a5e452f14c. Neither commit added a note to
UPGRADING.INTERNALS, so this adds one to the "Changed" list of section 1,
next to the other signature changes.
Worth documenting because the change is silent for extension authors: passing a zval*
where a zend_object* is now expected is only a
-Wincompatible-pointer-types warning, so an extension still builds and then reads the
wrong struct at runtime.
Concretely, ext-deepclone built clean-looking against master and segfaulted:
```
zend_hash_index_find_bucket (ht=0x7ffff2c87e61, h=757)
zim_DeepClone_HydrationContext_hydrate (deepclone.c:4007)
zend_call_known_fcc_ex (fcc->object = 0x7ffff2c87eb9)
zend_lazy_object_init (zend_lazy_objects.c:661)
```
Its lazy-object initializer was created with zend_create_fake_closure(...,
&this_zval); the engine stored that zval* as the closure's
this_ptr and handed it back as $this, so Z_OBJ_P(ZEND_THIS)
in the initializer dereferenced a zval as an object.