fix(php): 解决动态调用和方法重写的相关问题

- 添加了动态调用时的作用域切换代码生成
- 修复了 MethodCall 中的动态调用检测逻辑,区分魔法方法调用
- 为类方法定义添加了 hasDynamicCall 属性标识
- 实现了类继承链中私有方法重写的错误检查
- 添加了多个测试用例验证字符串连接性能和动态调用行为
pull/1/head
韩天峰 5 months ago
parent 08cbfa1d71
commit 28de6c86a4
  1. 16
      src/Php/CompilerBase.php
  2. 1
      src/Php/Entity/MethodDef.php
  3. 17
      src/Php/Translator.php
  4. 32
      tests/zend/concat/bug40809.phpt
  5. 21
      tests/zend/concat/bug44069.phpt
  6. 42
      tests/zend/dynamic_call/bug46246.phpt

@ -923,6 +923,12 @@ class CompilerBase extends \PhpAot\Core\Translator
}
$this->indentLevel--;
$code .= $this->genDebugInfo();
// 函数中存在动态调用的函数,需要在运行时动态切换作用域
if ($this->methodDef and $this->methodDef->hasDynamicCall) {
$code .= $this->genScopeSwitchCode();
}
$code .= $stmts;
$code .= "}\n";
@ -3856,7 +3862,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
$dynamicCall = false;
$magicMethod = false;
$method = $this->identifierToStr($expr->name, literal: true);
// 可转为原生调用的 MethodCall
@ -3873,7 +3879,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
} catch (DynamicCall) {
$dynamicCall = true;
$magicMethod = true;
}
}
@ -3883,12 +3889,16 @@ class CompilerBase extends \PhpAot\Core\Translator
$funcName = '';
}
if ($class and $funcName and !$dynamicCall) {
if ($class and $funcName and !$magicMethod) {
$methodPtr = $this->getMethodPtr($class, $funcName);
} else {
$methodPtr = $method;
}
if ($object === 'this_' or $object === 'self' or $object === 'static') {
$this->methodDef->hasDynamicCall = true;
}
if (empty($expr->args)) {
return $object . '.call(' . $methodPtr . ')';
}

@ -13,6 +13,7 @@ class MethodDef
public int $flags;
public string $name;
public ?FunctionDef $functionDef = null;
public bool $hasDynamicCall = false;
public function __construct(int $flags, string $name)
{

@ -1179,6 +1179,23 @@ class Translator extends Preprocessor
$this->method = $name;
$flags = $this->parseModifiers($v->flags);
$classDef = $this->classDef;
while (true) {
$extends = $classDef->extends;
if (!$extends) {
break;
}
$classDef = $this->getClass($extends);
if ($classDef->hasMethod($this->method)) {
$methodDef = $classDef->getMethod($this->method);
if ($methodDef->flags & Modifiers::PRIVATE) {
$this->fatalError($v,
'Cannot override private method `' .
$classDef->getNamespacedName(false) . '::' . $this->method . '()`');
}
}
}
if (!($flags & Modifiers::ABSTRACT)) {
$this->methodDef = new MethodDef($flags, $name);
$methodCodes[$name] = $this->parseFunction($v);

@ -0,0 +1,32 @@
--TEST--
Bug #40809 (Poor performance of ".=")
--FILE--
<?php
$num_increments = 100;
$num_repeats = 1000;
$increment = 50;
/* Create some more holes to give the memory allocator something to
* work with. */
$num = 5000;
$a = Array();
for ($i=0; $i<$num; $i++) {
$a[$i] = Array(1);
}
for ($i=0; $i<$num; $i++) {
$b[$i] = $a[$i][0];
}
unset($a);
for ($i=0;$i<$num_repeats;$i++) {
$evil = "";
for ($j=0;$j<$num_increments;$j++) {
$evil .= str_repeat("a", $increment);
}
unset($evil);
}
echo "ok\n";
?>
--EXPECT--
ok

@ -0,0 +1,21 @@
--TEST--
Bug #44069 (Huge memory usage with concatenation using . instead of .=)
--FILE--
<?php
$array = array();
$newstring = "";
$string = str_repeat('This is a teststring.', 50);
for($i = 1; $i <= 2000; $i++)
{
// $newstring .= $string; //This uses an expected amount of mem.
$newstring = $newstring . $string; //This uses very much mem.
for($j = 1; $j <= 10; $j++)
{
$array[] = 'test';
}
}
echo "ok\n";
?>
--EXPECT--
ok

@ -0,0 +1,42 @@
--TEST--
Bug #46246 (difference between call_user_func(array($this, $method)) and $this->$method())
--FILE--
<?php
class A
{
protected function Test()
{
echo 'Hello from '.get_class($this)."\n";
}
public function call($method, $args = array())
{
$this->Test();
$this->$method();
call_user_func(array($this, $method));
}
}
class B extends A
{
protected function Test()
{
echo 'Overridden hello from '.get_class($this)."\n";
}
}
function main() {
$a = new A;
$b = new B;
$a->call('Test');
$b->call('Test');
}
?>
--EXPECT--
Hello from A
Hello from A
Hello from A
Overridden hello from B
Overridden hello from B
Overridden hello from B
Loading…
Cancel
Save