fix: isolate internal methods from magic call optimization

master
韩天峰 9 hours ago
parent 3b66b488a3
commit b61b59f808
  1. 10
      phpunit/code/magic-call-codegen.php
  2. 4
      phpunit/src/MagicCallCodegenTest.php
  3. 21
      src/Parser/MethodCallTrait.php

@ -16,6 +16,10 @@ class RuntimeMagicMethod extends ExactMagicHandler
}
}
final class ExactInternalMethod extends ArrayObject
{
}
function exactMagicCall(): mixed
{
$handler = new ExactMagicHandler();
@ -27,3 +31,9 @@ function runtimeMagicCall(ExactMagicHandler $handler): mixed
return $handler->missing();
}
function exactInternalMethod(): mixed
{
$object = new ExactInternalMethod();
$object->append('value');
return $object->count();
}

@ -15,6 +15,10 @@ final class MagicCallCodegenTest extends \BaseTest
$runtimeBody = $this->functionBody($code, 'php_runtimemagiccall');
self::assertStringContainsString('.call(', $runtimeBody);
self::assertStringNotContainsString('php_exactmagichandler____call(', $runtimeBody);
$internalBody = $this->functionBody($code, 'php_exactinternalmethod');
self::assertStringContainsString('.call(', $internalBody);
self::assertStringNotContainsString('__call(', $internalBody);
}
private function compileFixture(): string

@ -318,23 +318,26 @@ trait MethodCallTrait
// otherwise getNativeMethod() would continue into the internal parent
// and incorrectly diagnose the absent magic method.
$currentClass = $exactClass;
$hasCompiledMagicMethod = false;
while ($this->hasClass($currentClass)) {
while (true) {
if (!$this->hasClass($currentClass)) {
return null;
}
$currentDef = $this->getClass($currentClass);
if ($currentDef->hasMethod('__call')) {
$hasCompiledMagicMethod = true;
break;
}
if ($currentDef->extends === '') {
break;
if ($currentDef->extends === '' || !$this->hasClass($currentDef->extends)) {
return null;
}
$currentClass = $currentDef->extends;
}
if (!$hasCompiledMagicMethod) {
return null;
}
$nativeFunc = $this->getNativeMethod($expr, $exactClass, '__call', false);
// Start from the class that actually declares the compiled method.
// This keeps getNativeMethod() out of an internal Zend parent: a real
// inherited internal method also uses DynamicCall, but an internal
// parent without __call must not be diagnosed while probing this
// optimization.
$nativeFunc = $this->getNativeMethod($expr, $currentClass, '__call', false);
if ($nativeFunc === false || !$this->hasFunction($nativeFunc)) {
return null;
}

Loading…
Cancel
Save