diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 639de6ab..7827cb7a 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -214,7 +214,7 @@ class CompilerBase extends \PhpAot\Core\Translator $this->indentLevel = 0; $this->strictTypes = false; $this->classesDefineInFile = []; - $this->interfaceDefineInFile = []; + $this->interfacesDefineInFile = []; } protected function resetNamespace(): void diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 37820ae4..7df76082 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -166,6 +166,14 @@ class Translator extends Preprocessor return self::PREFIX . 'class_entry_' . $classDef->getNamespacedName(); } + protected function getInternalCeInfo(string $ce): array + { + return [ + 'func' => 'php::getClassEntry', + 'args' => '"' . substr($ce, strlen(self::PREFIX . 'class_entry_')) . '"', + ]; + } + protected function getParentClassCe(ClassLikeDef $classDef): string { if (!$classDef->extends) { @@ -176,7 +184,11 @@ class Translator extends Preprocessor private function getImplementCe(ClassDef $classDef): array { - return array_map(fn($v) => self::PREFIX . 'class_entry_' . $v, $classDef->implements); + $list = []; + foreach ($classDef->implements as $interface) { + $list[] = self::PREFIX . 'class_entry_' . $interface; + } + return $list; } protected function doConvert(string $phpCode): string @@ -283,12 +295,19 @@ class Translator extends Preprocessor $sorter = new StringSort(); foreach ($this->interfacesDefineInFile as $interfaceDef) { + $parent = $interfaceDef->extends; $ce = $this->getClassCe($interfaceDef); - $parentCe = $this->getParentClassCe($interfaceDef); $deps = []; - if ($parentCe !== '') { - $deps[] = $parentCe; + + if ($parent) { + // 不存在的接口,说明可能是内置接口 + $tmpCe = $this->getParentClassCe($interfaceDef); + if (!isset($this->interfaces[$parent])) { + $sorter->add($tmpCe); + } + $deps[] = $tmpCe; } + $this->classCeInfo[$ce] = [ 'deps' => $deps, 'func' => $this->getRegisterClassFunction($interfaceDef->getNamespacedName()), @@ -300,14 +319,35 @@ class Translator extends Preprocessor foreach ($this->classesDefineInFile as $classDef) { $ce = $this->getClassCe($classDef); - $depsCeList = $this->getRegisterClassFunctionCeList($classDef); + $deps = []; + $parent = $classDef->extends; + if ($parent) { + // 不存在的父类,说明可能是内置类 + $tmpCe = $this->getParentClassCe($classDef); + if (!isset($this->classes[$parent])) { + $sorter->add($tmpCe); + } + $deps[] = $tmpCe; + } + + $implements = $classDef->implements; + if ($implements) { + foreach ($implements as $interface) { + $tmpCe = self::PREFIX . 'class_entry_' . $interface; + if (!isset($this->interfaces[$interface])) { + $sorter->add($tmpCe); + } + $deps[] = $tmpCe; + } + } + $this->classCeInfo[$ce] = [ - 'deps' => $depsCeList, + 'deps' => $deps, 'func' => $this->getRegisterClassFunction($classDef->getNamespacedName()), 'args' => $this->getRegisterClassFunctionArgs($classDef), 'argDef' => $this->getRegisterClassFunctionArgDef($classDef), ]; - $sorter->add($ce, $depsCeList); + $sorter->add($ce, $deps); } $this->classCeList = $sorter->sort(); @@ -473,8 +513,9 @@ class Translator extends Preprocessor } $this->classDef->implements = $this->parseIdentifierList($class->implements); - $this->classes[$this->class] = $this->classDef; - $this->classesDefineInFile[$this->class] = $this->classDef; + $className = $this->classDef->getNamespacedName(); + $this->classes[$className] = $this->classDef; + $this->classesDefineInFile[$className] = $this->classDef; $methodCodes = []; @@ -557,6 +598,7 @@ class Translator extends Preprocessor $cppCode .= $this->getIndent() . 'return register_class_' . $name . '(' . $param . ');' . PHP_EOL; $cppCode .= '}' . PHP_EOL . PHP_EOL; + // 接口没有方法实体 if ($classDef instanceof ClassDef) { $methods = $classDef->methods; foreach ($methods as $methodDef) { @@ -762,7 +804,8 @@ class Translator extends Preprocessor $name = $this->parseIdentifier($v->name); $this->interface = $name; $this->interfaceDef = new InterfaceDef($name, $this->namespace); - $this->interfaces[$name] = $this->interfaceDef; - $this->interfacesDefineInFile[$this->interface] = $this->interfaceDef; + $interfaceName = $this->interfaceDef->getNamespacedName(); + $this->interfaces[$interfaceName] = $this->interfaceDef; + $this->interfacesDefineInFile[$interfaceName] = $this->interfaceDef; } } \ No newline at end of file diff --git a/src/template/extension.cc.php b/src/template/extension.cc.php index 0cac5b1d..a35d4e28 100644 --- a/src/template/extension.cc.php +++ b/src/template/extension.cc.php @@ -17,10 +17,14 @@ foreach ($this->globalVars as $name => $type): // class register functions classCeList as $ce): - $info = $this->classCeInfo[$ce]; ?> zend_class_entry * ; +classCeInfo[$ce])): + $info = $this->classCeInfo[$ce]; +?> extern zend_class_entry *(); + // literal strings @@ -55,7 +59,7 @@ static PHP_MINIT_FUNCTION(app) { // class/interface class entries classCeList as $ce): - $info = $this->classCeInfo[$ce]; + $info = $this->classCeInfo[$ce] ?? $this->getInternalCeInfo($ce); ?> = (); diff --git a/tests/core/classes/interfaces_001.phpt b/tests/core/classes/interfaces_001.phpt new file mode 100644 index 00000000..0bb746a9 --- /dev/null +++ b/tests/core/classes/interfaces_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +ZE2 interfaces +--FILE-- +foo; + } +} +function main() { + $foo = new Exception_foo; + echo $foo->getMessage() . "\n"; +} +?> +--EXPECT-- +foo diff --git a/tests/core/classes/interfaces_002.phpt b/tests/core/classes/interfaces_002.phpt new file mode 100644 index 00000000..01c4092e --- /dev/null +++ b/tests/core/classes/interfaces_002.phpt @@ -0,0 +1,29 @@ +--TEST-- +ZE2 interface with an unimplemented method +--SKIPIF-- + +--FILE-- +foo; + } +} + +function main() { + // this should die -- Exception class must be abstract... + $foo = new Exception_foo; + echo "Message: " . $foo->getMessage() . "\n"; +} + +?> +--EXPECTF-- +Fatal error: Class Exception_foo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (ThrowableInterface::getErrno) in %s on line %d diff --git a/tests/core/classes/interfaces_003.phpt b/tests/core/classes/interfaces_003.phpt new file mode 100644 index 00000000..5c55b81c --- /dev/null +++ b/tests/core/classes/interfaces_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +ZE2 interface and __construct +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: Too few arguments to function MyTestClass::__construct(), 0 passed in %sinterfaces_003.php on line 17 and exactly 1 expected in %sinterfaces_003.php:12 +Stack trace: +#0 %s(%d): MyTestClass->__construct() +#1 {main} + thrown in %sinterfaces_003.php on line %d