[skip ci] Document the closure $this internals change in UPGRADING.INTERNALS - #23434
Merged
Merged
Conversation
…TERNALS zend_create_closure(), zend_create_fake_closure() and zend_create_partial_closure() take a zend_object* since fbb2e1f, and zend_get_closure_this_ptr() returns one since 7a5e452. Neither change was listed, so extensions built against the new headers get only a -Wincompatible-pointer-types warning and read garbage at runtime.
nicolas-grekas
added a commit
to symfony/php-ext-deepclone
that referenced
this pull request
Aug 24, 2026
… master php-src turned the $this a closure is bound to from a zval* into a zend_object*: zend_create_closure() and friends take one since fbb2e1f23d6 and zend_get_closure_this_ptr() returns one since 7a5e452f14c, both landed during 8.6-dev and without a ZEND_MODULE_API_NO bump to key off. Passing or reading the wrong flavor is only a -Wincompatible-pointer-types warning, so the extension still built and then read the wrong struct at runtime: the closure encoder saw every bound closure as unbound, and the lazy ghost initializer handed the engine a zval* where it now expects the object, segfaulting in zend_lazy_object_init() on the first touch of a ghost. Route both directions through shims that pick the flavor from the type the engine declares -- via _Generic where the compiler has C11, via the version otherwise. php-src is documenting the change in php/php-src#23434.
Member
|
Thanks, I didn't expect an extension to be messing around with closures. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
zend_create_closure(),zend_create_fake_closure()andzend_create_partial_closure()take the bound$thisas azend_object*since fbb2e1f, andzend_get_closure_this_ptr()returns one — NULL when the closure is unbound, where it used to return azval*that isIS_UNDEF— since 7a5e452. Neither commit added a note toUPGRADING.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 azend_object*is now expected is only a-Wincompatible-pointer-typeswarning, so an extension still builds and then reads the wrong struct at runtime.Concretely, ext-deepclone built clean-looking against master and segfaulted:
Its lazy-object initializer was created with
zend_create_fake_closure(..., &this_zval); the engine stored thatzval*as the closure'sthis_ptrand handed it back as$this, soZ_OBJ_P(ZEND_THIS)in the initializer dereferenced a zval as an object.