Fix GH-21362: ReflectionMethod::invoke/invokeArgs() rejects different Closure instances#21366
Fix GH-21362: ReflectionMethod::invoke/invokeArgs() rejects different Closure instances#21366iliaal wants to merge 1 commit intophp:masterfrom
Conversation
|
I did not test, but based on reading the diff I think this is not quite right, yet. Instead of comparing the objects, we should compare the inner I expect this to successfully dump 0, 1, 2. All the closure instances are "of the same class". If that already works: Great, please just add that test case. |
…ent Closure instances ReflectionMethod::invokeArgs() (and invoke()) for Closure::__invoke() incorrectly accepted any Closure object, not just the one the ReflectionMethod was created from. This happened because all Closures share a single zend_ce_closure class entry, so the instanceof_function() check always passed. Fix: store the original Closure object in intern->obj during ReflectionMethod construction, then compare object identity in reflection_method_invoke() to reject different Closure instances. Closes phpGH-21362
1581e11 to
277d301
Compare
|
Good catch -- the object identity check was too strict. Updated the comparison to use Added the loop test case you suggested. All 515 reflection tests pass. P.S. Thanks for the feedback, my php internals are a bit rusty 😆 |
| if (!variadic) { | ||
| efree(params); | ||
| } | ||
| _DO_THROW("Given object is not an instance of the class this method was declared in"); |
There was a problem hiding this comment.
but they are an instance of the class, just not the correct instance - this error message should be improved
Summary
Fixes #21362
ReflectionMethod::invokeArgs()(andinvoke()) forClosure::__invoke()incorrectly accepted any Closure object, not just the one theReflectionMethodwas created from.Root cause
All Closure objects share a single
zend_ce_closureclass entry. Theinstanceof_function()check inreflection_method_invoke()compares class entries, so it always seeszend_ce_closure == zend_ce_closureand passes -- it cannot distinguish between different Closure instances.Fix
Two changes to
ext/reflection/php_reflection.c:instantiate_reflection_method(): Store the original Closure object inintern->objviaZVAL_OBJ_COPY()when reflectingClosure::__invoke(). Previously this was a no-op (/* do nothing, mptr already set */).reflection_method_invoke(): After the existinginstanceof_functioncheck passes for Closures, add an identity check (Z_OBJ_P(object) != Z_OBJ(intern->obj)) to reject different Closure instances.Test
ext/reflection/tests/gh21362.phptcovers:invokeArgs()with the correct Closure (should work)invokeArgs()with a different Closure (should throwReflectionException)invoke()with a different Closure (should throwReflectionException)