From 8231208eaf48240c38ab9816d9a023750524635f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 30 Jun 2026 11:27:05 +0800 Subject: [PATCH] =?UTF-8?q?fix(php):=20=E4=BF=AE=E5=A4=8D=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E8=AE=BF=E9=97=AE=E6=8E=A7=E5=88=B6=E5=92=8C=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E6=B8=85=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 canAccessProtectedProperty 替代 isInheritedFrom 处理保护属性访问 - 更新扩展模式下的映射表清理逻辑,使用 C++ 样式初始化替代 PHP 数组赋值 - 修复继承检查中私有成员重声明的处理,跳过私有父类属性/常量的兼容性检查 - 优化可见性检查逻辑,支持可见性放宽但阻止私有成员重声明 - 添加对父类可访问子类保护方法和常量的支持测试 - 新增属性和常量可见性放宽及私有成员重声明的测试用例 - 添加扩展清理映射表的运行时测试验证 --- .../compiler_api/extension_clean_maps.php | 6 ++++ .../inheritance_const_visibility_widen.php | 11 +++++++ .../inheritance_private_const_redeclare.php | 11 +++++++ .../inheritance_private_prop_redeclare.php | 11 +++++++ .../inheritance_prop_visibility_widen.php | 11 +++++++ ...heritance_protected_child_const_access.php | 14 +++++++++ ...eritance_protected_child_method_access.php | 16 ++++++++++ phpunit/src/CompilerBaseApiTest.php | 26 ++++++++++++++++ phpunit/src/InheritanceErrorTest.php | 30 +++++++++++++++++++ src/Php/CompilerBase.php | 5 +++- src/Php/Translator.php | 19 ++++++++---- 11 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 phpunit/code/compiler_api/extension_clean_maps.php create mode 100644 phpunit/code/inheritance_const_visibility_widen.php create mode 100644 phpunit/code/inheritance_private_const_redeclare.php create mode 100644 phpunit/code/inheritance_private_prop_redeclare.php create mode 100644 phpunit/code/inheritance_prop_visibility_widen.php create mode 100644 phpunit/code/inheritance_protected_child_const_access.php create mode 100644 phpunit/code/inheritance_protected_child_method_access.php diff --git a/phpunit/code/compiler_api/extension_clean_maps.php b/phpunit/code/compiler_api/extension_clean_maps.php new file mode 100644 index 00000000..74dd0adb --- /dev/null +++ b/phpunit/code/compiler_api/extension_clean_maps.php @@ -0,0 +1,6 @@ +assertArrayNotHasKey('cxxflags', $options); } + public function testExtensionCleanClearsRuntimeMapsWithValidCpp(): void + { + global $translator; + $compiler = CompilerTest::create(ROOT_PATH); + $translator = $compiler; + $ref = new \ReflectionClass($compiler); + $buildMode = $ref->getProperty('buildMode'); + $buildMode->setAccessible(true); + $buildMode->setValue($compiler, CompilerBase::BUILD_MODE_EXT); + + $testFile = ROOT_PATH . '/phpunit/code/compiler_api/extension_clean_maps.php'; + $compiler->addFiles([$testFile]); + $compiler->prepareFile($testFile); + $compiler->convertFile($testFile); + $extensionFile = $compiler->genExtension(); + $code = file_get_contents($extensionFile); + + $this->assertStringContainsString('#include ', $code); + $this->assertStringContainsString('std::memset(php_func_map, 0, sizeof(php_func_map));', $code); + $this->assertStringContainsString('std::memset(php_class_map, 0, sizeof(php_class_map));', $code); + $this->assertStringContainsString('std::memset(php_property_map, 0, sizeof(php_property_map));', $code); + $this->assertStringNotContainsString('func_map = {}', $code); + $this->assertStringNotContainsString('class_map = {}', $code); + $this->assertStringNotContainsString('property_map = {}', $code); + } + public function testObjectiveCppCompileCommandOptionsKeepCppOptions(): void { $this->setPropertyValue('cxxStd', 'c++20'); diff --git a/phpunit/src/InheritanceErrorTest.php b/phpunit/src/InheritanceErrorTest.php index ab54eac6..dcc38a33 100644 --- a/phpunit/src/InheritanceErrorTest.php +++ b/phpunit/src/InheritanceErrorTest.php @@ -78,6 +78,16 @@ class InheritanceErrorTest extends TestCase $this->assertCompiles('inheritance_error_visibility.php'); } + public function testParentScopeMayAccessChildProtectedMethod() + { + $this->assertCompiles('inheritance_protected_child_method_access.php'); + } + + public function testParentScopeMayAccessChildProtectedConstant() + { + $this->assertCompiles('inheritance_protected_child_const_access.php'); + } + public function testMethodStaticMismatch() { $this->exec('must be compatible', 'inheritance_error_static.php'); @@ -123,6 +133,16 @@ class InheritanceErrorTest extends TestCase $this->exec('must be compatible', 'inheritance_error_prop_visibility.php'); } + public function testPropertyVisibilityMayBeWidened() + { + $this->assertCompiles('inheritance_prop_visibility_widen.php'); + } + + public function testPrivateParentPropertyMayBeRedeclared() + { + $this->assertCompiles('inheritance_private_prop_redeclare.php'); + } + public function testConstantTypeMismatch() { $this->exec('must be compatible', 'inheritance_error_const_type.php'); @@ -133,6 +153,16 @@ class InheritanceErrorTest extends TestCase $this->exec('must be compatible', 'inheritance_error_const_visibility.php'); } + public function testConstantVisibilityMayBeWidened() + { + $this->assertCompiles('inheritance_const_visibility_widen.php'); + } + + public function testPrivateParentConstantMayBeRedeclared() + { + $this->assertCompiles('inheritance_private_const_redeclare.php'); + } + public function testPropertyReadonlyMismatch() { $this->exec('must be compatible', 'inheritance_error_prop_readonly.php'); diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 4c928170..73608a11 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -5483,7 +5483,10 @@ class CompilerBase extends \PhpAot\Core\Translator if (!$this->classDef) { return false; } - return $this->isInheritedFrom($this->classDef->getNamespacedName(false), $classDef->getNamespacedName(false)); + return $this->canAccessProtectedProperty( + $this->classDef->getNamespacedName(false), + $classDef->getNamespacedName(false) + ); } // 类外部调用,只允许调用 public 方法 return true; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index cfe04d6c..70ab0473 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -778,7 +778,8 @@ class Translator extends Preprocessor $this->genClassCeList(); $this->indentLevel++; - $code = $this->genIncludeHeaderFiles(); + $code = '#include ' . PHP_EOL; + $code .= $this->genIncludeHeaderFiles(); if ($this->isBuildModeBin()) { $cliHeaders = [ @@ -1018,9 +1019,9 @@ CODE; // 扩展模式,需要在 RSHUTDOWN 阶段中清理函数、类、属性表 if ($this->isBuildModeExt()) { - $code .= self::FUNC_MAP." = {}\n"; - $code .= self::CLASS_MAP." = {}\n"; - $code .= self::PROP_MAP." = {}\n"; + $code .= 'std::memset(' . self::PREFIX . self::FUNC_MAP . ', 0, sizeof(' . self::PREFIX . self::FUNC_MAP . '));' . PHP_EOL; + $code .= 'std::memset(' . self::PREFIX . self::CLASS_MAP . ', 0, sizeof(' . self::PREFIX . self::CLASS_MAP . '));' . PHP_EOL; + $code .= 'std::memset(' . self::PREFIX . self::PROP_MAP . ', 0, sizeof(' . self::PREFIX . self::PROP_MAP . '));' . PHP_EOL; } $code .= '}' . PHP_EOL . PHP_EOL; @@ -3421,12 +3422,15 @@ CODE; foreach ($this->classDef->properties as $name => $childProp) { if ($chainNode->hasProperty($name)) { $parentProp = $chainNode->getProperty($name); + if ($parentProp->flags & Modifiers::PRIVATE) { + continue; + } if ($childProp->type !== $parentProp->type || $childProp->class !== $parentProp->class) { $this->fatalError($classStmt, "Declaration of `{$className}::\${$name}` must be compatible " . "with `{$parentClass}::\${$name}`"); } - if (($childProp->flags & Modifiers::VISIBILITY_MASK) !== ($parentProp->flags & Modifiers::VISIBILITY_MASK)) { + if ($this->getVisibilityRank($childProp->flags) < $this->getVisibilityRank($parentProp->flags)) { $this->fatalError($classStmt, "Declaration of `{$className}::\${$name}` must be compatible " . "with `{$parentClass}::\${$name}`"); @@ -3455,12 +3459,15 @@ CODE; foreach ($this->classDef->constants as $name => $childConst) { if ($chainNode->hasConstant($name)) { $parentConst = $chainNode->getConstant($name); + if ($parentConst->flags & Modifiers::PRIVATE) { + continue; + } if ($childConst->type !== $parentConst->type || $childConst->class !== $parentConst->class) { $this->fatalError($classStmt, "Declaration of `{$className}::{$name}` must be compatible " . "with `{$parentClass}::{$name}`"); } - if (($childConst->flags & Modifiers::VISIBILITY_MASK) !== ($parentConst->flags & Modifiers::VISIBILITY_MASK)) { + if ($this->getVisibilityRank($childConst->flags) < $this->getVisibilityRank($parentConst->flags)) { $this->fatalError($classStmt, "Declaration of `{$className}::{$name}` must be compatible " . "with `{$parentClass}::{$name}`");