diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 4c65356d..4aad8283 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -169,6 +169,9 @@ class CompilerBase extends \PhpAot\Core\Translator // iterable 类型,可以是数组或者对象 'iterable' => self::TYPE_VAR, 'stream' => self::TYPE_STREAM, + 'bigint' => self::TYPE_BIGINT, + 'bigfloat' => self::TYPE_BIGFLOAT, + 'decimal' => self::TYPE_DECIMAL, ]; protected array $globalHeaders = [ 'phpx.h', @@ -1058,11 +1061,12 @@ class CompilerBase extends \PhpAot\Core\Translator return self::TYPE_VAR; } else { $typeName = $this->parseIdentifier($type); + $typeNameLower = strtolower($typeName); // 属性和类常量的类型不能声明为 void/never ,只有返回值可以 - if ($what !== self::DECL_TYPE_OF_RETURN and ($typeName === 'void' or $typeName === 'never')) { + if ($what !== self::DECL_TYPE_OF_RETURN and ($typeNameLower === 'void' or $typeNameLower === 'never')) { $this->fatalError($type, 'The type `void`/`never` is allowed only for return type'); - } elseif (isset($this->zendTypeMap[$typeName])) { - return $this->getTypeFromZendType($typeName); + } elseif (isset($this->zendTypeMap[$typeNameLower])) { + return $this->getTypeFromZendType($typeNameLower); } else { if ($typeName === 'self') { $class = $this->getFullClassName(); @@ -1636,7 +1640,7 @@ class CompilerBase extends \PhpAot\Core\Translator if ($var === 'this_') { $this->fatalError($left, 'Cannot re-assign $this'); } - $type = $this->detectTypeOfExpr($right); + $finalVarType = $type = $this->detectTypeOfExpr($right); if ($this->isVarExpr($left)) { if ($this->isStdContainer($var)) { @@ -1669,6 +1673,7 @@ class CompilerBase extends \PhpAot\Core\Translator $type = self::TYPE_VAR; if (!$this->hasVar($var)) { $this->addLocalVar($var, $type); + $finalVarType = $type; } return $var . ' = ' . $this->parseIdentifier($right->args[0]->value); } else { @@ -1709,7 +1714,8 @@ class CompilerBase extends \PhpAot\Core\Translator } else { $valueExpr = $this->parseStdCall($right); if (!$this->hasVar($var)) { - $this->addLocalVar($var, $right->getAttribute('nativeType')); + $finalVarType = $right->getAttribute('nativeType'); + $this->addLocalVar($var, $finalVarType); } return $var . ' = ' . $valueExpr; } @@ -1725,7 +1731,8 @@ class CompilerBase extends \PhpAot\Core\Translator } // 变量第一次被赋值,确定其类型,由于 PHP 的变量作用域是 function 级的,在 for/while 块中声明的变量,可以在块外使用 if (!$this->hasVar($var)) { - $this->addLocalVar($var, $this->isNativeType($type) ? $this->getNativeType($type) : $type); + $finalVarType = $this->isNativeType($type) ? $this->getNativeType($type) : $type; + $this->addLocalVar($var, $finalVarType); } else { $this->checkVarAssignExpr($left, $this->getVarType($var), $type); } @@ -1744,7 +1751,13 @@ class CompilerBase extends \PhpAot\Core\Translator } $rightExpr = $this->parseAssignRightExpr($right); - return $var . ' = ' . $this->convertExprType($rightExpr, $this->detectTypeOfExpr($left), $this->detectTypeOfExpr($right)); + $leftExprType = $this->detectTypeOfExpr($left); + $rightExprType = $this->detectTypeOfExpr($right); + if ($finalVarType === self::TYPE_VAR) { + return $var . ' = ' . $rightExpr; + } else { + return $var . ' = ' . $this->convertExprType($rightExpr, $leftExprType, $rightExprType); + } } protected function parseStdContainerCopyAssign(string $leftVar, Expr $right): ?string @@ -2455,14 +2468,6 @@ class CompilerBase extends \PhpAot\Core\Translator return self::TYPE_FLOAT; } if ($leftType === self::TYPE_INT || $rightType === self::TYPE_INT) { - // 除法存在特殊性,若未能整除,会返回浮点数,其他则一律视为整数 - if ($exprType === 'Expr_BinaryOp_Div') { - if ($leftType === self::TYPE_INT && $rightType === self::TYPE_INT) { - return self::TYPE_INT; - } else { - return self::TYPE_VAR; - } - } return self::TYPE_INT; } break; @@ -2540,12 +2545,12 @@ class CompilerBase extends \PhpAot\Core\Translator return self::TYPE_OBJECT; } $className = $this->parseIdentifier($expr->class); - if ($className === 'std') { - $method = $this->parseIdentifier($expr->name); + if (strtolower($className) === 'std') { + $method = strtolower($this->parseIdentifier($expr->name)); return match ($method) { - 'bigInt' => self::TYPE_BIGINT, + 'bigint' => self::TYPE_BIGINT, 'decimal' => self::TYPE_DECIMAL, - 'bigFloat' => self::TYPE_BIGFLOAT, + 'bigfloat' => self::TYPE_BIGFLOAT, default => self::TYPE_VAR, }; } @@ -5990,11 +5995,12 @@ class CompilerBase extends \PhpAot\Core\Translator $this->useConstants[$cn] = $fullName; } } else { - if ($id === 'native_types') { + $idLower = strtolower($id); + if ($idLower === 'native_types') { $this->nativeTypes = true; - } elseif ($id === 'decimal_types') { + } elseif ($idLower === 'decimal_types') { $this->decimalTypes = true; - } elseif ($id === 'bigint_types') { + } elseif ($idLower === 'bigint_types') { $this->bigintTypes = true; } else { $this->useNamespaces[] = $id; @@ -6184,14 +6190,14 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseStdCall(Expr\StaticCall $expr): string { - $func = $this->parseIdentifier($expr->name); + $func = strtolower($this->parseIdentifier($expr->name)); $type = match ($func) { 'int' => self::TYPE_INT, 'float' => self::TYPE_FLOAT, 'bool' => self::TYPE_BOOL, - 'bigInt' => self::TYPE_BIGINT, + 'bigint' => self::TYPE_BIGINT, 'decimal' => self::TYPE_DECIMAL, - 'bigFloat' => self::TYPE_BIGFLOAT, + 'bigfloat' => self::TYPE_BIGFLOAT, default => '', }; if ($type) { diff --git a/src/Php/Parser/StdContainerParser.php b/src/Php/Parser/StdContainerParser.php index 99f1f1d8..2f793e98 100644 --- a/src/Php/Parser/StdContainerParser.php +++ b/src/Php/Parser/StdContainerParser.php @@ -467,10 +467,10 @@ trait StdContainerParser if (!$this->isClassConstFetch($expr)) { $this->fatalError($expr, "{$owner} expects a native_types class constant"); } - if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name) || $expr->class->toString() !== 'native_types') { + if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name) || strtolower($expr->class->toString()) !== 'native_types') { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } - return match ($expr->name->name) { + return match (strtolower($expr->name->name)) { 'type_int' => self::TYPE_INT, 'type_float' => self::TYPE_FLOAT, 'type_bool' => self::TYPE_BOOL, @@ -489,12 +489,13 @@ trait StdContainerParser if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name)) { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } - if ($expr->class->toString() === 'native_types') { + $className = strtolower($expr->class->toString()); + if ($className === 'native_types') { return ['type' => $this->parseStdNativeType($expr, $owner), 'class' => null]; } - if ($expr->class->toString() === 'complex_types') { + if ($className === 'complex_types') { return [ - 'type' => match ($expr->name->name) { + 'type' => match (strtolower($expr->name->name)) { 'type_str', 'type_string' => self::TYPE_STR, 'type_array' => self::TYPE_ARRAY, 'type_object' => self::TYPE_OBJECT, @@ -634,10 +635,12 @@ trait StdContainerParser if (!$this->isClassConstFetch($expr) || !$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name)) { $this->fatalError($expr, "{$owner} expects a native_types or complex_types class constant"); } - if ($expr->class->toString() === 'native_types' && $expr->name->name === 'type_int') { + $className = strtolower($expr->class->toString()); + $constName = strtolower($expr->name->name); + if ($className === 'native_types' && $constName === 'type_int') { return self::TYPE_INT; } - if ($expr->class->toString() === 'complex_types' && in_array($expr->name->name, ['type_string', 'type_str'], true)) { + if ($className === 'complex_types' && in_array($constName, ['type_string', 'type_str'], true)) { return self::TYPE_STR; } $this->fatalError($expr, "{$owner} key only supports native_types::type_int, complex_types::type_string or complex_types::type_str"); diff --git a/src/polyfills.php b/src/polyfills.php index f69234cb..d5f6332d 100644 --- a/src/polyfills.php +++ b/src/polyfills.php @@ -11,6 +11,9 @@ class native_types public const type_int = 'int'; public const type_float = 'float'; public const type_bool = 'bool'; + public const type_bigint = 'bigint'; + public const type_bigfloat = 'bigfloat'; + public const type_decimal = 'decimal'; } class complex_types { @@ -21,6 +24,7 @@ class complex_types { public const type_string = 'string'; public const type_array = 'array'; public const type_object = 'object'; + public const type_stream = 'stream'; } class std diff --git a/tests/aot/basic/pow-int-overflow-native-type.phpt b/tests/aot/basic/pow-int-overflow-native-type.phpt new file mode 100644 index 00000000..45b7083d --- /dev/null +++ b/tests/aot/basic/pow-int-overflow-native-type.phpt @@ -0,0 +1,13 @@ +--TEST-- +pow int overflow +--FILE-- + +--EXPECT-- +0 \ No newline at end of file diff --git a/tests/aot/basic/pow-int-overflow.phpt b/tests/aot/basic/pow-int-overflow.phpt new file mode 100644 index 00000000..d74b2633 --- /dev/null +++ b/tests/aot/basic/pow-int-overflow.phpt @@ -0,0 +1,12 @@ +--TEST-- +pow int overflow +--FILE-- + +--EXPECT-- +1.2089258196146E+24 \ No newline at end of file diff --git a/tests/aot/bigint/pow-02.phpt b/tests/aot/bigint/pow-02.phpt new file mode 100644 index 00000000..9826bf88 --- /dev/null +++ b/tests/aot/bigint/pow-02.phpt @@ -0,0 +1,14 @@ +--TEST-- +BigInt: use +--FILE-- + +--EXPECT-- +1208925819614629174706176 diff --git a/tests/aot/decimal/round-02.phpt b/tests/aot/decimal/round-02.phpt index fb1610c9..e601b668 100644 --- a/tests/aot/decimal/round-02.phpt +++ b/tests/aot/decimal/round-02.phpt @@ -15,11 +15,11 @@ function main(): void { $total = $subtotal + $tax; echo "小计: " . round($subtotal, 2) . "\n"; // 89.97 - echo "税额: " . round($tax, 4) . "\n"; // 7.1976 + echo "税额: " . round($tax, 20) . "\n"; // 7.19760000000000000000 echo "总计: " . round($total, 2) . "\n"; // 97.17 } ?> --EXPECT-- 小计: 89.97 -税额: 7.1976 +税额: 7.19760000000000000000 总计: 97.17