diff --git a/examples/error/str.php b/examples/error/str.php new file mode 100644 index 00000000..c83c38dd --- /dev/null +++ b/examples/error/str.php @@ -0,0 +1,20 @@ +a; + } +} +function test(string $a) +{ + var_dump($a); +} + +function main() +{ + $o = new TestObject(); + $o->a = "hello"; + test($o); +} + diff --git a/examples/myext/test.php b/examples/myext/test.php index b8b42c7b..7cb6f3f8 100644 --- a/examples/myext/test.php +++ b/examples/myext/test.php @@ -1,5 +1,12 @@ hasVar($var)) { $this->addLocalVar($var, $type); - } elseif (($this->getVarType($var) !== self::TYPE_VAR and $this->isTypedObject($var) and $type !== self::TYPE_OBJECT) - // 禁止字符串与数组互相转换,其他类型如对象可以使用 __toString() 协议转为字符串,整数和浮点型也可以转为字符串 - or ($this->getVarType($var) === self::TYPE_STR and $type === self::TYPE_ARRAY) - or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_STR) - // 禁止对象转为数组 - or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_OBJECT) - ) { - $this->fatalError($left, "Cannot re-assign variable `\${$var}` from " . $this->getVarType($var) . ' to ' . $type); + } else { + $this->checkVarAssignExpr($left, $this->getVarType($var), $type); } } } elseif ($this->isPropertyFetch($left) and !$left->getAttribute('nativeProperty')) { @@ -3411,6 +3405,8 @@ class CompilerBase extends \PhpAot\Core\Translator return $this->convertToRef($arg->value); } + $this->checkVarAssignExpr($arg, $argInfo->type, $type); + if ($argInfo->type === self::TYPE_OBJECT) { if ($this->isVarExpr($arg->value)) { $object = $this->parseVariable($arg->value); @@ -4730,6 +4726,37 @@ class CompilerBase extends \PhpAot\Core\Translator } } + protected function checkVarAssignExpr(NodeAbstract $left, string $toType, string $fromType): bool + { + if ($toType === self::TYPE_VAR or $fromType === self::TYPE_VAR) { + return true; + } + // 引用当前没有类型信息,按照 var 处理 + if ($toType === self::TYPE_REF or $fromType === self::TYPE_REF) { + return true; + } + if ($toType === self::TYPE_OBJECT and $fromType === self::TYPE_OBJECT) { + return true; + } + if ($toType === self::TYPE_ARRAY and $fromType === self::TYPE_ARRAY) { + return true; + } + if ($toType === self::TYPE_STR) { + if ($fromType === self::TYPE_STR) { + return true; + } + if (!$this->strictTypes) { + $this->warning($left, "Attempt to implicitly convert `$fromType` to `$toType`"); + return true; + } + } + // 原生类型可以互相转换,由 C++ 底层完成 + if ($this->isNativeType($toType) and $this->isNativeType($fromType)) { + return true; + } + $this->fatalError($left, "Cannot re-assign variable from `$fromType` to `$toType`"); + } + protected function mustNoCall(NodeAbstract $node): void { $nodeFinder = new NodeFinder(); diff --git a/src/Php/MagicMethodDetector.php b/src/Php/MagicMethodDetector.php index a038a45d..0d32ea02 100644 --- a/src/Php/MagicMethodDetector.php +++ b/src/Php/MagicMethodDetector.php @@ -32,7 +32,7 @@ trait MagicMethodDetector $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must take exactly 1 argument"); } } elseif ($nameLower == '__tostring') { - if (!$this->checkArgType($returnType, self::TYPE_STR)) { + if (!$this->checkArgType($returnType, self::TYPE_STR, false)) { $this->fatalError($v, 'Method ' . $this->class . "::{$name}() must return string"); } } elseif ($nameLower == '__serialize') { diff --git a/src/Php/Translator.php b/src/Php/Translator.php index c2dc9a2d..2721399c 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -225,6 +225,11 @@ class Translator extends Preprocessor $this->error("The `libphpx.so` is not found, please run `make` to build it"); } + $clangFormatVersion = shell_exec('clang-format --version'); + if (empty($clangFormatVersion)) { + $this->formatCode = false; + } + $files = $this->getFiles($path); // 应用 ignorePaths 过滤 if (!empty($this->ignorePaths)) {