feat(php): 添加PHP方法重写签名验证功能

- 实现了子类方法重写时参数数量、返回类型、参数类型的兼容性检查
- 添加了按引用传递参数和可变参数的签名匹配验证
- 新增了继承错误测试用例文件
- 重构了方法重写检查逻辑,提取为独立的验证函数
- 更新了错误提示信息以显示具体的不兼容原因
pull/3/head
韩天峰 2 months ago
parent 4faff75f62
commit 23a100caf4
  1. 12
      phpunit/code/inheritance_error_byref.php
  2. 12
      phpunit/code/inheritance_error_return.php
  3. 12
      phpunit/code/inheritance_error_type.php
  4. 12
      phpunit/code/inheritance_error_variadic.php
  5. 54
      src/Php/Translator.php

@ -0,0 +1,12 @@
<?php
class A
{
function f($x) {}
}
class B extends A
{
function f(&$x) {}
}
function main() {}

@ -0,0 +1,12 @@
<?php
class A
{
function f(): int { return 1; }
}
class B extends A
{
function f(): string { return ""; }
}
function main() {}

@ -0,0 +1,12 @@
<?php
class A
{
function f(int $x) {}
}
class B extends A
{
function f(string $x) {}
}
function main() {}

@ -0,0 +1,12 @@
<?php
class A
{
function f($x) {}
}
class B extends A
{
function f(...$x) {}
}
function main() {}

@ -2856,11 +2856,12 @@ CODE;
}
/**
* 检查父类方法是否可以被重写,私有方法不能被重写
* 检查父类方法是否可以被重写,私有方法不能被重写,方法签名必须兼容
*/
protected function checkParentMethodCanBeOverridden(Node\Stmt\ClassMethod $v, string $name): void
{
$classDef = $this->classDef;
$childFuncDef = $this->methodDef->functionDef;
while (true) {
$extends = $classDef->extends;
if (!$extends) {
@ -2874,14 +2875,59 @@ CODE;
break;
}
$classDef = $this->getClass($extends);
if ($classDef->hasMethod($this->method)) {
$methodDef = $classDef->getMethod($this->method);
if ($classDef->hasMethod($name)) {
$methodDef = $classDef->getMethod($name);
if ($methodDef->flags & Modifiers::PRIVATE) {
_error:
$this->fatalError($v,
'Cannot override private method `' .
$extends . '::' . $this->method . '()`');
$extends . '::' . $name . '()`');
}
$parentFuncDef = $methodDef->functionDef;
if ($parentFuncDef) {
$this->validateMethodOverrideSignature($v, $name, $childFuncDef, $parentFuncDef, $extends);
}
break;
}
}
}
private function validateMethodOverrideSignature(
Node\Stmt\ClassMethod $v,
string $methodName,
FunctionDef $childFuncDef,
FunctionDef $parentFuncDef,
string $parentClass
): void {
$className = $this->getFullClassName();
$error = function (string $detail) use ($v, $className, $methodName, $parentClass) {
$this->fatalError($v,
"Declaration of `{$className}::{$methodName}()` must be compatible " .
"with `{$parentClass}::{$methodName}()`");
};
// Compare parameter count
if (count($childFuncDef->argInfoList) !== count($parentFuncDef->argInfoList)) {
$error('parameter count mismatch');
}
// Compare return type
if ($childFuncDef->returnType !== $parentFuncDef->returnType ||
$childFuncDef->returnClass !== $parentFuncDef->returnClass) {
$error('return type mismatch');
}
// Compare each parameter
foreach ($parentFuncDef->argInfoList as $i => $parentArg) {
$childArg = $childFuncDef->argInfoList[$i];
if ($childArg->type !== $parentArg->type || $childArg->class !== $parentArg->class) {
$error("parameter #{$i} type mismatch");
}
if ($childArg->byRef !== $parentArg->byRef) {
$error("parameter #{$i} by-reference mismatch");
}
if ($childArg->variadic !== $parentArg->variadic) {
$error("parameter #{$i} variadic mismatch");
}
}
}

Loading…
Cancel
Save