diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 35666761..b21112d4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -13,7 +13,7 @@ TypePHP is a PHP native compilation project. Its `tpc` command is TypePHP Compil ## Setup and build commands -The repo expects PHP 8.2+, GCC 9+ with C++17, CMake 3.24+, and a compiled `swoole/phpx` dependency. Install PHP dependencies with: +The repo expects PHP 8.4+, GCC 9+ with C++17, CMake 3.24+, and a compiled `swoole/phpx` dependency. Install PHP dependencies with: ```bash composer install diff --git a/README.md b/README.md index e08ec0a9..462c0f26 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # 依赖 -- 需要 PHP-8.2 以上版本 +- 编译器需要 PHP 8.4 以上版本;生成的扩展仍可面向 PHP 8.2~8.5 - 需要 GCC-9 以上版本,支持 C++17 标准 - 需要 CMake-3.24 以上版本 - 需要高精度数学库:`GMP`、`MPFR`、`libmpdec` @@ -47,4 +47,4 @@ vim /etc/ld.so.conf.d/swoole.conf ``` /home/swoole/workspace/projects/phpx/lib /opt/php-8.4/lib/ -``` \ No newline at end of file +``` diff --git a/composer.json b/composer.json index 3e3c0f83..5a0dd817 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "require": { - "php": ">=8.2 <8.6", + "php": ">=8.4 <8.6", "nikic/php-parser": "5.6.1", "league/climate": "^3.10", "marcj/topsort": "^2.0", diff --git a/composer.lock b/composer.lock index 7dd50321..eca74d3f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "41bca06c5ece84cfb4ed396385b10988", + "content-hash": "72caced27fd0c3469dae5d4f4db591d0", "packages": [ { "name": "ajaxray/ansikit", @@ -4876,7 +4876,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.2 <8.6" + "php": ">=8.4 <8.6" }, "platform-dev": {}, "plugin-api-version": "2.9.0" diff --git a/docs/COMPILE_TIME_FUNCTIONS.md b/docs/COMPILE_TIME_FUNCTIONS.md index 222df916..fe67eac0 100644 --- a/docs/COMPILE_TIME_FUNCTIONS.md +++ b/docs/COMPILE_TIME_FUNCTIONS.md @@ -74,7 +74,7 @@ ## 不计入本文清单的机制 - `$array->any()` 是 universal method,映射到 PHP `array_any()`,不是 `any()` 编译期函数。 -- `native_types::type_*`、`complex_types::type_*` 是编译期类型描述常量,不是函数。 +- `Type::*` 是编译期类型描述常量,不是函数。 - keyword extension method 是用户自定义扩展方法机制,不属于固定内置编译期函数清单。 ## 实现约束 diff --git a/docs/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md b/docs/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md index 4546c0a5..aa70d925 100644 --- a/docs/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md +++ b/docs/PATENT_01_CPP_TEMPLATE_TYPED_CONTAINER.md @@ -103,10 +103,10 @@ Swoole-Compiler 是一种 PHP AOT 编译器。它将 PHP 源文件解析为抽 本发明在动态语言中定义一组强类型容器构造语法,例如: ```php -$a = std::array(native_types::type_int, 100); -$v = std::vector(native_types::type_float); -$m = std::ordered_map(complex_types::type_str, native_types::type_int); -$h = std::map(native_types::type_int, User::class); +$a = std::array(Type::Int, 100); +$v = std::vector(Type::Float); +$m = std::ordered_map(Type::String, Type::Int); +$h = std::map(Type::Int, User::class); ``` 编译器在 AOT 阶段识别这些构造表达式,生成容器元信息,并将其转换为 C++ 模版实例: @@ -204,7 +204,7 @@ typeId:根据上述字段生成的类型标识。 例如: ```php -$b = std::array(std::array(native_types::type_int, 3), 2); +$b = std::array(std::array(Type::Int, 3), 2); ``` 对应元信息可以表示为: @@ -225,8 +225,8 @@ typeId=自动分配的整数 示例: ```php -$a = std::array(native_types::type_int, 3); -$b = std::array(std::array(native_types::type_int, 3), 2); +$a = std::array(Type::Int, 3); +$b = std::array(std::array(Type::Int, 3), 2); $a = $b[1]; ``` @@ -252,8 +252,8 @@ a = b[php::safeIndex(php::toInt(1L), 2)]; 第一类,左值为强类型容器且右值为完全相同强类型容器: ```php -$a = std::vector(native_types::type_int); -$b = std::vector(native_types::type_int); +$a = std::vector(Type::Int); +$b = std::vector(Type::Int); $a = $b; ``` @@ -344,13 +344,13 @@ for (auto it = v.begin(); it != v.end(); ++it) { ```php function update(UnsafePtr $ptr): void { - $v = std::unsafe_cast(std::vector(native_types::type_int), $ptr); + $v = std::unsafe_cast(std::vector(Type::Int), $ptr); $v[0] = 100; } function main(): void { - $v = std::vector(native_types::type_int, 1); + $v = std::vector(Type::Int, 1); update($v); } ``` diff --git a/docs/STD_CONTAINERS.md b/docs/STD_CONTAINERS.md index 1f89163f..a666317b 100644 --- a/docs/STD_CONTAINERS.md +++ b/docs/STD_CONTAINERS.md @@ -102,7 +102,7 @@ Swoole AOT 提供 `std` 容器,用来表达“这个容器的结构和元素 ```php function main(): void { - $array = std::array(native_types::type_int, 100); + $array = std::array(Type::Int, 100); $array[0] = 123; $array[99] = 456; @@ -125,7 +125,7 @@ function main(): void function main(): void { $matrix = std::array( - std::array(native_types::type_int, 4), + std::array(Type::Int, 4), 3 ); @@ -141,8 +141,8 @@ function main(): void ```php function main(): void { - $a = std::array(native_types::type_int, 3); - $b = std::array(std::array(native_types::type_int, 3), 2); + $a = std::array(Type::Int, 3); + $b = std::array(std::array(Type::Int, 3), 2); $b[1][0] = 10; $b[1][1] = 20; @@ -159,7 +159,7 @@ function main(): void ```php function main(): void { - $vector = std::vector(native_types::type_int); + $vector = std::vector(Type::Int); $vector[] = 1; $vector[] = 2; @@ -173,7 +173,7 @@ function main(): void 也可以指定初始长度: ```php -$vector = std::vector(native_types::type_float, 1024); +$vector = std::vector(Type::Float, 1024); ``` 特点: @@ -187,8 +187,8 @@ $vector = std::vector(native_types::type_float, 1024); 同类型 vector 可以 copy: ```php -$a = std::vector(native_types::type_int); -$b = std::vector(native_types::type_int); +$a = std::vector(Type::Int); +$b = std::vector(Type::Int); $b[] = 10; $b[] = 20; @@ -204,8 +204,8 @@ $a = $b; // 允许,类型完全一致,执行容器 copy function main(): void { $map = std::ordered_map( - complex_types::type_str, - native_types::type_int + Type::String, + Type::Int ); $map["a"] = 1; @@ -225,7 +225,7 @@ function main(): void 示例: ```php -$map = std::ordered_map(native_types::type_int, native_types::type_float); +$map = std::ordered_map(Type::Int, Type::Float); $map[10] = 1.25; $map[20] = 3.5; @@ -234,8 +234,8 @@ $map[20] = 3.5; 同类型 ordered_map 可以 copy: ```php -$a = std::ordered_map(native_types::type_int, native_types::type_int); -$b = std::ordered_map(native_types::type_int, native_types::type_int); +$a = std::ordered_map(Type::Int, Type::Int); +$b = std::ordered_map(Type::Int, Type::Int); $b[10] = 100; $a = $b; @@ -249,8 +249,8 @@ $a = $b; function main(): void { $map = std::map( - native_types::type_int, - native_types::type_int + Type::Int, + Type::Int ); $map[100] = 1; @@ -270,8 +270,8 @@ function main(): void 同类型 map 可以 copy: ```php -$a = std::map(native_types::type_int, native_types::type_int); -$b = std::map(native_types::type_int, native_types::type_int); +$a = std::map(Type::Int, Type::Int); +$b = std::map(Type::Int, Type::Int); $b[1] = 42; $a = $b; @@ -279,23 +279,18 @@ $a = $b; ## 支持的元素类型 -基础类型: +类型符号: ```php -native_types::type_int -native_types::type_float -native_types::type_bool -``` - -复杂类型: - -```php -complex_types::type_string -complex_types::type_str -complex_types::type_array -complex_types::type_object -complex_types::type_any -complex_types::type_var +Type::Int +Type::Float +Type::Bool +Type::String +Type::Array +Type::Object +Type::Any +Type::Stream +Type::Box ``` 也可以使用类名作为 value 类型: @@ -307,7 +302,7 @@ class User $vector = std::vector(User::class); $array = std::array(User::class, 10); -$map = std::ordered_map(complex_types::type_str, User::class); +$map = std::ordered_map(Type::String, User::class); ``` 类类型容器会在写入时检查对象类型,避免错误对象混入。 @@ -319,7 +314,7 @@ $map = std::ordered_map(complex_types::type_str, User::class); ```php function main(): void { - $vector = std::vector(native_types::type_int); + $vector = std::vector(Type::Int); $vector[] = 1; $vector[] = 2; @@ -332,8 +327,8 @@ function main(): void 如果左值本身是同类型 std 容器,则执行容器 copy,而不是转 PHP Array: ```php -$a = std::vector(native_types::type_int); -$b = std::vector(native_types::type_int); +$a = std::vector(Type::Int); +$b = std::vector(Type::Int); $a = $b; // std::vector copy ``` @@ -341,8 +336,8 @@ $a = $b; // std::vector copy 如果类型不同,则不允许 copy: ```php -$a = std::vector(native_types::type_int); -$b = std::vector(native_types::type_float); +$a = std::vector(Type::Int); +$b = std::vector(Type::Float); $a = $b; // 编译失败 ``` @@ -357,7 +352,7 @@ $a = $b; // 编译失败 function update(UnsafePtr $ptr): void { $vector = std::unsafe_cast( - std::vector(native_types::type_int), + std::vector(Type::Int), $ptr ); @@ -366,7 +361,7 @@ function update(UnsafePtr $ptr): void function main(): void { - $vector = std::vector(native_types::type_int, 1); + $vector = std::vector(Type::Int, 1); update($vector); // 编译器自动转为 UnsafePtr box } ``` @@ -404,7 +399,7 @@ std 容器则不同。编译器在解析代码时记录容器元信息: 例如: ```php -$vector = std::vector(native_types::type_int); +$vector = std::vector(Type::Int); ``` 可以生成类似: @@ -416,7 +411,7 @@ php::StdVector vector; 再例如: ```php -$array = std::array(std::array(native_types::type_int, 3), 2); +$array = std::array(std::array(Type::Int, 3), 2); ``` 可以生成类似: @@ -506,7 +501,7 @@ function main(int $argc, array $argv): void $u = (int)$argv[2]; echo "u: $u\n"; $r = rand(0, 10000); - $a = std::array(native_types::type_int, 10000); + $a = std::array(Type::Int, 10000); $begin = microtime(true); for ($i = 0; $i < 10000; $i++) { diff --git a/examples/array-loop/main.php b/examples/array-loop/main.php index 5427effc..9223a351 100644 --- a/examples/array-loop/main.php +++ b/examples/array-loop/main.php @@ -7,7 +7,7 @@ function main(int $argc, array $argv): void $u = (int)$argv[2]; echo "u: $u\n"; $r = rand(0, 10000); - $a = std::array(native_types::type_int, 10000); + $a = std::array(Type::Int, 10000); $begin = microtime(true); for ($i = 0; $i < 10000; $i++) { diff --git a/examples/array-loop/std-array.php b/examples/array-loop/std-array.php index 26492600..82b49900 100644 --- a/examples/array-loop/std-array.php +++ b/examples/array-loop/std-array.php @@ -4,7 +4,7 @@ use native_types; function main() { - $array = std::array(std::array(std::array(native_types::type_int, 13), 16), 19); + $array = std::array(std::array(std::array(Type::Int, 13), 16), 19); $index = 9; $index2 = 5; $array[$index2][$index][0] = 2026; diff --git a/examples/stdmap.php b/examples/stdmap.php index 7d645a67..25fe6b50 100644 --- a/examples/stdmap.php +++ b/examples/stdmap.php @@ -11,7 +11,7 @@ class Foo { function main() { - $map = std::map(complex_types::type_str, Foo::class); + $map = std::map(Type::String, Foo::class); $map["a"] = new Foo("a", 1); $map["b"] = new Foo("b", 2); var_dump($map); diff --git a/src/Parser/MethodCallTrait.php b/src/Parser/MethodCallTrait.php index 3d78019e..adf60e11 100644 --- a/src/Parser/MethodCallTrait.php +++ b/src/Parser/MethodCallTrait.php @@ -328,7 +328,15 @@ trait MethodCallTrait } return $this->genToConvertCall($object, $methodName, $receiverType); } - // __ keyword extensions + // A provider targeting Type::Any only applies when + // the receiver's static type is actually mixed/any. + if ($receiverType === Type::VAR) { + $anyExtension = $this->findExtensionMethod(Type::VAR, $methodName); + if ($anyExtension) { + return $this->parseUniversalMethodCall($expr, $object, $methodName, $anyExtension, $this->isVarExpr($expr->var)); + } + } + // ExtensionProvider::Keyword extensions apply to every receiver type. $kwExt = $this->findKeywordExtensionMethod($methodName); if ($kwExt) { return $this->parseUniversalMethodCall($expr, $object, $methodName, $kwExt, $this->isVarExpr($expr->var)); diff --git a/src/Parser/StdContainerTrait.php b/src/Parser/StdContainerTrait.php index a0ad918e..e5739002 100644 --- a/src/Parser/StdContainerTrait.php +++ b/src/Parser/StdContainerTrait.php @@ -469,19 +469,17 @@ trait StdContainerTrait protected function parseStdNativeType(NodeAbstract $expr, string $owner): string { - if (!$this->isClassConstFetch($expr)) { - $this->fatalError($expr, "{$owner} expects a native_types class constant"); - } - if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name) || strtolower($expr->class->toString()) !== 'native_types') { + if (!$this->isClassConstFetch($expr) || !$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name) + || strcasecmp(ltrim($expr->class->toString(), '\\'), 'Type') !== 0) { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } - return match (strtolower($expr->name->name)) { - 'type_int' => Type::INT, - 'type_float' => Type::FLOAT, - 'type_bool' => Type::BOOL, - 'type_bigint' => Type::BIGINT, - 'type_bigfloat' => Type::BIGFLOAT, - 'type_decimal' => Type::DECIMAL, + return match ($expr->name->name) { + 'Int' => Type::INT, + 'Float' => Type::FLOAT, + 'Bool' => Type::BOOL, + 'BigInt' => Type::BIGINT, + 'BigFloat' => Type::BIGFLOAT, + 'Decimal' => Type::DECIMAL, default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), }; } @@ -489,24 +487,22 @@ trait StdContainerTrait protected function parseStdValueTypeInfo(NodeAbstract $expr, string $owner): array { if (!$this->isClassConstFetch($expr)) { - $this->fatalError($expr, "{$owner} expects a native_types or complex_types class constant"); + $this->fatalError($expr, "{$owner} expects a Type constant or ClassName::class"); } if (!$this->isNameExpr($expr->class) || !$this->isIdExpr($expr->name)) { $this->fatalError($expr, "An incorrect `{$owner}` definition"); } - $className = strtolower($expr->class->toString()); - if ($className === 'native_types') { - return ['type' => $this->parseStdNativeType($expr, $owner), 'class' => null]; - } - if ($className === 'complex_types') { + $className = ltrim($expr->class->toString(), '\\'); + if (strcasecmp($className, 'Type') === 0) { return [ - 'type' => match (strtolower($expr->name->name)) { - 'type_str', 'type_string' => Type::STR, - 'type_array' => Type::ARRAY, - 'type_object' => Type::OBJECT, - 'type_any', 'type_var', 'type_variant' => Type::VAR, - 'type_stream' => Type::STREAM, - 'type_box' => Type::BOX, + 'type' => match ($expr->name->name) { + 'Int', 'Float', 'Bool', 'BigInt', 'BigFloat', 'Decimal' => $this->parseStdNativeType($expr, $owner), + 'String' => Type::STR, + 'Array' => Type::ARRAY, + 'Object' => Type::OBJECT, + 'Any' => Type::VAR, + 'Stream' => Type::STREAM, + 'Box' => Type::BOX, default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), }, 'class' => null, @@ -634,17 +630,17 @@ trait StdContainerTrait protected function parseStdMapKeyType(NodeAbstract $expr, string $owner): string { 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"); + $this->fatalError($expr, "{$owner} expects Type::Int or Type::String"); } - $className = strtolower($expr->class->toString()); - $constName = strtolower($expr->name->name); - if ($className === 'native_types' && $constName === 'type_int') { + $className = ltrim($expr->class->toString(), '\\'); + $constName = $expr->name->name; + if (strcasecmp($className, 'Type') === 0 && $constName === 'Int') { return Type::INT; } - if ($className === 'complex_types' && in_array($constName, ['type_string', 'type_str'], true)) { + if (strcasecmp($className, 'Type') === 0 && $constName === 'String') { return Type::STR; } - $this->fatalError($expr, "{$owner} key only supports native_types::type_int, complex_types::type_string or complex_types::type_str"); + $this->fatalError($expr, "{$owner} key only supports Type::Int or Type::String"); } protected function getStdMapDecl(string $containerType, string $keyType, string $valueType): string diff --git a/src/Parser/UniversalMethodCall.php b/src/Parser/UniversalMethodCall.php index 26039d35..70015cd2 100644 --- a/src/Parser/UniversalMethodCall.php +++ b/src/Parser/UniversalMethodCall.php @@ -387,6 +387,9 @@ trait UniversalMethodCall private function extensionReceiverMatchesTarget($receiver, string $target): bool { + if ($target === '*') { + return $receiver->type === Type::VAR; + } $builtinTargets = [ Type::VAR, Type::INT, Type::FLOAT, Type::BOOL, Type::STR, Type::ARRAY, Type::OBJECT, Type::STREAM, Type::BIGINT, @@ -478,7 +481,7 @@ trait UniversalMethodCall */ protected function findKeywordExtensionMethod(string $method): ?array { - return $this->findProviderExtension(Type::VAR, $method); + return $this->findProviderExtension('*', $method); } /** diff --git a/src/Preprocessor.php b/src/Preprocessor.php index c6aca98a..6dd940c3 100644 --- a/src/Preprocessor.php +++ b/src/Preprocessor.php @@ -635,37 +635,33 @@ class Preprocessor extends CompilerBase if ($value instanceof Node\Expr\ClassConstFetch && $this->isNameExpr($value->class) && $this->isIdExpr($value->name)) { - $class = strtolower(ltrim($value->class->toString(), '\\')); - $constant = strtolower($value->name->toString()); - if ($constant === 'class') { + $class = ltrim($value->class->toString(), '\\'); + $constant = $value->name->toString(); + if (strtolower($constant) === 'class') { return $this->getNamespacedClassName($value->class->toString()); } $targets = [ - 'native_types' => [ - 'type_int' => Type::INT, - 'type_float' => Type::FLOAT, - 'type_bool' => Type::BOOL, - 'type_bigint' => Type::BIGINT, - 'type_bigfloat' => Type::BIGFLOAT, - 'type_decimal' => Type::DECIMAL, - ], - 'complex_types' => [ - 'type_any' => Type::VAR, - 'type_var' => Type::VAR, - 'type_variant' => Type::VAR, - 'type_str' => Type::STR, - 'type_string' => Type::STR, - 'type_array' => Type::ARRAY, - 'type_object' => Type::OBJECT, - 'type_stream' => Type::STREAM, - 'type_box' => Type::BOX, - ], + 'Int' => Type::INT, + 'Float' => Type::FLOAT, + 'Bool' => Type::BOOL, + 'BigInt' => Type::BIGINT, + 'BigFloat' => Type::BIGFLOAT, + 'Decimal' => Type::DECIMAL, + 'String' => Type::STR, + 'Array' => Type::ARRAY, + 'Object' => Type::OBJECT, + 'Any' => Type::VAR, + 'Stream' => Type::STREAM, + 'Box' => Type::BOX, ]; - if (isset($targets[$class][$constant])) { - return $targets[$class][$constant]; + if (strcasecmp($class, 'Type') === 0 && isset($targets[$constant])) { + return $targets[$constant]; + } + if (strcasecmp($class, 'ExtensionProvider') === 0 && $constant === 'Keyword') { + return '*'; } } - $this->fatalError($errorNode, 'ExtensionProvider target must use native_types, complex_types, or ClassName::class'); + $this->fatalError($errorNode, 'ExtensionProvider target must be Type::*, ExtensionProvider::Keyword, or ClassName::class'); } protected function buildLiteralArrayInitPlan(Node\Expr\Array_ $defaultNode): ArrayInitPlan diff --git a/src/gen_stub.php b/src/gen_stub.php index 4dc9370b..ccc56d82 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -186,15 +186,15 @@ class Context { } class ArrayType extends SimpleType { - private /* readonly */ Type $keyType; - private /* readonly */ Type $valueType; + private /* readonly */ StubType $keyType; + private /* readonly */ StubType $valueType; public static function createGenericArray(): self { - return new ArrayType(Type::fromString("int|string"), Type::fromString("mixed|ref")); + return new ArrayType(StubType::fromString("int|string"), StubType::fromString("mixed|ref")); } - public function __construct(Type $keyType, Type $valueType) + public function __construct(StubType $keyType, StubType $valueType) { parent::__construct("array", true); @@ -217,8 +217,8 @@ class ArrayType extends SimpleType { return false; } - return Type::equals($this->keyType, $other->keyType) && - Type::equals($this->valueType, $other->valueType); + return StubType::equals($this->keyType, $other->keyType) && + StubType::equals($this->valueType, $other->valueType); } } @@ -299,7 +299,7 @@ class SimpleType { $matches = []; $isArray = preg_match("/(.*)\s*\[\s*\]/", $typeString, $matches); if ($isArray) { - return new ArrayType(Type::fromString("int"), Type::fromString($matches[1])); + return new ArrayType(StubType::fromString("int"), StubType::fromString($matches[1])); } $matches = []; @@ -309,7 +309,7 @@ class SimpleType { throw new Exception("array<> type hint must have both a key and a value"); } - return new ArrayType(Type::fromString($matches[1]), Type::fromString($matches[3])); + return new ArrayType(StubType::fromString($matches[1]), StubType::fromString($matches[3])); } return new SimpleType($typeString, false); @@ -521,27 +521,27 @@ class SimpleType { } } -// Instances of Type are immutable and do not need to be cloned +// Instances of StubType are immutable and do not need to be cloned // when held by an object that is cloned -class Type { +class StubType { /** @var SimpleType[] */ public /* readonly */ array $types; public /* readonly */ bool $isIntersection; - public static function fromNode(Node $node): Type { + public static function fromNode(Node $node): StubType { if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) { - $nestedTypeObjects = array_map(['Type', 'fromNode'], $node->types); + $nestedTypeObjects = array_map(['StubType', 'fromNode'], $node->types); $types = []; foreach ($nestedTypeObjects as $typeObject) { array_push($types, ...$typeObject->types); } - return new Type($types, ($node instanceof Node\IntersectionType)); + return new StubType($types, ($node instanceof Node\IntersectionType)); } if ($node instanceof Node\NullableType) { - return new Type( + return new StubType( [ - ...Type::fromNode($node->type)->types, + ...StubType::fromNode($node->type)->types, SimpleType::null(), ], false @@ -549,7 +549,7 @@ class Type { } if ($node instanceof Node\Identifier && $node->toLowerString() === "iterable") { - return new Type( + return new StubType( [ SimpleType::fromString("Traversable"), ArrayType::createGenericArray(), @@ -558,7 +558,7 @@ class Type { ); } - return new Type([SimpleType::fromNode($node)], false); + return new StubType([SimpleType::fromNode($node)], false); } public static function fromString(string $typeString): self { @@ -596,7 +596,7 @@ class Type { } } - return new Type($simpleTypes, $isIntersection); + return new StubType($simpleTypes, $isIntersection); } /** @@ -706,7 +706,7 @@ class Type { return $typeElement; } - public static function equals(?Type $a, ?Type $b): bool { + public static function equals(?StubType $a, ?StubType $b): bool { if ($a === null || $b === null) { return $a === $b; } @@ -776,8 +776,8 @@ class ArgInfo { public /* readonly */ string $name; public /* readonly */ string $sendBy; public /* readonly */ bool $isVariadic; - public ?Type $type; - private /* readonly */ ?Type $phpDocType; + public ?StubType $type; + private /* readonly */ ?StubType $phpDocType; public ?string $defaultValue; /** @var AttributeInfo[] */ public array $attributes; @@ -789,8 +789,8 @@ class ArgInfo { string $name, string $sendBy, bool $isVariadic, - ?Type $type, - ?Type $phpDocType, + ?StubType $type, + ?StubType $phpDocType, ?string $defaultValue, array $attributes ) { @@ -807,11 +807,11 @@ class ArgInfo { return $this->name === $other->name && $this->sendBy === $other->sendBy && $this->isVariadic === $other->isVariadic - && Type::equals($this->type, $other->type) + && StubType::equals($this->type, $other->type) && $this->defaultValue === $other->defaultValue; } - public function getMethodSynopsisType(): Type { + public function getMethodSynopsisType(): StubType { if ($this->type) { return $this->type; } @@ -1116,15 +1116,15 @@ class ReturnInfo { private /* readonly */ bool $byRef; // NOT readonly - gets removed when discarding info for older PHP versions - public ?Type $type; - public /* readonly */ ?Type $phpDocType; + public ?StubType $type; + public /* readonly */ ?StubType $phpDocType; public /* readonly */ bool $tentativeReturnType; public /* readonly */ string $refcount; public function __construct( bool $byRef, - ?Type $type, - ?Type $phpDocType, + ?StubType $type, + ?StubType $phpDocType, bool $tentativeReturnType, ?string $refcount ) { @@ -1137,11 +1137,11 @@ class ReturnInfo { public function equalsApartFromPhpDocAndRefcount(ReturnInfo $other): bool { return $this->byRef === $other->byRef - && Type::equals($this->type, $other->type) + && StubType::equals($this->type, $other->type) && $this->tentativeReturnType === $other->tentativeReturnType; } - public function getMethodSynopsisType(): ?Type { + public function getMethodSynopsisType(): ?StubType { return $this->type ?? $this->phpDocType; } @@ -2513,8 +2513,8 @@ class EvaluatedValue abstract class VariableLike { protected int $flags; - public ?Type $type; - public /* readonly */ ?Type $phpDocType; + public ?StubType $type; + public /* readonly */ ?StubType $phpDocType; private /* readonly */ ?string $link; protected ?int $phpVersionIdMinimumCompatibility; /** @var AttributeInfo[] */ @@ -2526,8 +2526,8 @@ abstract class VariableLike */ public function __construct( int $flags, - ?Type $type, - ?Type $phpDocType, + ?StubType $type, + ?StubType $phpDocType, ?string $link, ?int $phpVersionIdMinimumCompatibility, array $attributes, @@ -2684,8 +2684,8 @@ class ConstInfo extends VariableLike int $flags, Expr $value, ?string $valueString, - ?Type $type, - ?Type $phpDocType, + ?StubType $type, + ?StubType $phpDocType, bool $isDeprecated, ?string $cond, ?string $cValue, @@ -3212,8 +3212,8 @@ class PropertyInfo extends VariableLike PropertyName $name, int $classFlags, int $flags, - ?Type $type, - ?Type $phpDocType, + ?StubType $type, + ?StubType $phpDocType, ?Expr $defaultValue, ?string $defaultValueString, bool $isDocReadonly, @@ -5018,11 +5018,11 @@ function parseFunctionLike( throw new Exception("Only the last parameter can be variadic"); } - $type = $param->type ? Type::fromNode($param->type) : null; + $type = $param->type ? StubType::fromNode($param->type) : null; if ($type === null && !isset($docParamTypes[$varName])) { $defaultParamType = getMagicMethodDefaultParamType($name, $i); if ($defaultParamType !== null) { - $type = Type::fromString($defaultParamType); + $type = StubType::fromString($defaultParamType); } else { $docParamTypes[$varName] = 'mixed'; } @@ -5052,7 +5052,7 @@ function parseFunctionLike( $sendBy, $param->variadic, $type, - isset($docParamTypes[$varName]) ? Type::fromString($docParamTypes[$varName]) : null, + isset($docParamTypes[$varName]) ? StubType::fromString($docParamTypes[$varName]) : null, $defaultValue, AttributeInfo::createFromGroups($param->attrGroups) ); @@ -5072,8 +5072,8 @@ function parseFunctionLike( $return = new ReturnInfo( $func->returnsByRef(), - $returnType ? Type::fromNode($returnType) : null, - $docReturnType ? Type::fromString($docReturnType) : null, + $returnType ? StubType::fromNode($returnType) : null, + $docReturnType ? StubType::fromString($docReturnType) : null, $tentativeReturnType, $refcount ); @@ -5163,8 +5163,8 @@ function parseConstLike( } } - $constType = $type ? Type::fromNode($type) : null; - $constPhpDocType = $phpDocType ? Type::fromString($phpDocType) : null; + $constType = $type ? StubType::fromNode($type) : null; + $constPhpDocType = $phpDocType ? StubType::fromString($phpDocType) : null; if ($const->value instanceof Expr\ConstFetch && $const->value->name->toLowerString() === "null" && @@ -5225,9 +5225,9 @@ function parseProperty( } } - $propertyType = $type ? Type::fromNode($type) : null; + $propertyType = $type ? StubType::fromNode($type) : null; if ($propertyType === null && !$phpDocType) { - $propertyType = Type::fromString("mixed"); + $propertyType = StubType::fromString("mixed"); } if ($property->default instanceof Expr\ConstFetch && @@ -5245,7 +5245,7 @@ function parseProperty( $classFlags, $flags, $propertyType, - $phpDocType ? Type::fromString($phpDocType) : null, + $phpDocType ? StubType::fromString($phpDocType) : null, $property->default, $property->default ? $prettyPrinter->prettyPrintExpr($property->default) : null, $isDocReadonly, diff --git a/src/polyfills.php b/src/polyfills.php index 56e8adba..3bf8b208 100644 --- a/src/polyfills.php +++ b/src/polyfills.php @@ -9,32 +9,36 @@ #[Attribute(Attribute::TARGET_CLASS)] final readonly class ExtensionProvider { + public const string Keyword = '*'; + public function __construct(public string $target) { } } -class native_types +/** + * Public compile-time type symbols shared by extension providers and std containers. + * This root class is deliberately distinct from the compiler-internal TypePhp\Type. + */ +final class Type { - 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'; + public const string Int = 'int'; + public const string Float = 'float'; + public const string Bool = 'bool'; + public const string BigInt = 'bigint'; + public const string BigFloat = 'bigfloat'; + public const string Decimal = 'decimal'; + public const string String = 'string'; + public const string Array = 'array'; + public const string Object = 'object'; + public const string Any = 'any'; + public const string Stream = 'stream'; + public const string Box = 'box'; } -class complex_types +/** @deprecated Compiler directive retained independently of public type symbols. */ +class native_types { - public const type_any = 'any'; - public const type_var = 'any'; - public const type_variant = 'any'; - public const type_str = 'string'; - public const type_string = 'string'; - public const type_array = 'array'; - public const type_object = 'object'; - public const type_stream = 'stream'; - public const type_box = 'box'; } class std @@ -101,7 +105,13 @@ function any(mixed $var): mixed return $var; } +/** + * @throws Exception + */ function objval(mixed $var, string $className): mixed { + if (!$var instanceof $className) { + throw new \Exception("Invalid object type: " . get_class($var) . " expected " . $className); + } return $var; } diff --git a/tests/compiler/keyword_extension/001.phpt b/tests/compiler/keyword_extension/001.phpt index 11d75ad9..c2466687 100644 --- a/tests/compiler/keyword_extension/001.phpt +++ b/tests/compiler/keyword_extension/001.phpt @@ -5,7 +5,7 @@ Keyword ExtensionProvider method with snake_case name declare(strict_types=1); use native_types; -#[ExtensionProvider(complex_types::type_any)] +#[ExtensionProvider(ExtensionProvider::Keyword)] final class KeywordExtensions { public static function var_dump(mixed $var): void diff --git a/tests/compiler/keyword_extension/camel.phpt b/tests/compiler/keyword_extension/camel.phpt index 1cbb1d7e..243061ed 100644 --- a/tests/compiler/keyword_extension/camel.phpt +++ b/tests/compiler/keyword_extension/camel.phpt @@ -6,7 +6,7 @@ Keyword ExtensionProvider method with lowerCamelCase name declare(strict_types=1); use native_types; -#[ExtensionProvider(complex_types::type_any)] +#[ExtensionProvider(ExtensionProvider::Keyword)] final class KeywordExtensions { public static function inspectValue(mixed $value, string $prefix): void @@ -15,11 +15,24 @@ final class KeywordExtensions } } +#[ExtensionProvider(Type::Any)] +final class AnyExtensions +{ + public static function dynamicType(mixed $value): string + { + return get_debug_type($value); + } +} + function main(): void { $value = 42; $value->inspectValue('number'); + + $dynamic = $value->toAny(); + echo $dynamic->dynamicType(), "\n"; } ?> --EXPECT-- number:42 +int diff --git a/tests/compiler/std-array/001.phpt b/tests/compiler/std-array/001.phpt index 6c79ad45..b651c6fa 100644 --- a/tests/compiler/std-array/001.phpt +++ b/tests/compiler/std-array/001.phpt @@ -3,7 +3,7 @@ std array: 001 --FILE-- "array", "value" => 64]; $array = $arrays[0]; var_dump($array["name"]); var_dump($array["value"]); - $objects = std::array(complex_types::type_object, 2); + $objects = std::array(Type::Object, 2); $objects[0] = new StdArrayComplexValue(28); var_dump($objects[0] instanceof StdArrayComplexValue); $object = $objects[0]->toObject(StdArrayComplexValue::class); var_dump($object->getValue()); - $variants = std::array(complex_types::type_any, 2); + $variants = std::array(Type::Any, 2); $variants[0] = 123; $variants[1] = "variant"; var_dump($variants[0]); diff --git a/tests/compiler/std-array/007.phpt b/tests/compiler/std-array/007.phpt index bfa936f4..9d8985d0 100644 --- a/tests/compiler/std-array/007.phpt +++ b/tests/compiler/std-array/007.phpt @@ -4,13 +4,13 @@ std array: unsafe_cast toStdArray(native_types::type_int, 3); + $array = $source->toStdArray(Type::Int, 3); var_dump($array[1]); $array[2] = 9; } function main() { - $array = std::array(native_types::type_int, 3); + $array = std::array(Type::Int, 3); $array[0] = 1; $array[1] = 7; $array[2] = 3; diff --git a/tests/compiler/std-array/008.phpt b/tests/compiler/std-array/008.phpt index 3484f110..536629e7 100644 --- a/tests/compiler/std-array/008.phpt +++ b/tests/compiler/std-array/008.phpt @@ -4,11 +4,11 @@ std array: unsafe_cast type mismatch toStdArray(native_types::type_float, 3); + $array = $source->toStdArray(Type::Float, 3); } function main() { - $array = std::array(native_types::type_int, 3); + $array = std::array(Type::Int, 3); try { std_array_unsafe_ptr_type_mismatch($array); } catch (TypeError $e) { diff --git a/tests/compiler/std-array/009.phpt b/tests/compiler/std-array/009.phpt index 6e4e5a8a..776b4c70 100644 --- a/tests/compiler/std-array/009.phpt +++ b/tests/compiler/std-array/009.phpt @@ -3,7 +3,7 @@ std array: assign to PHP array --FILE-- $item) { $a[$k] = random_int(1, 1000); } diff --git a/tests/compiler/std-bigfloat/001.phpt b/tests/compiler/std-bigfloat/001.phpt index 704816a1..b1025d64 100644 --- a/tests/compiler/std-bigfloat/001.phpt +++ b/tests/compiler/std-bigfloat/001.phpt @@ -4,7 +4,7 @@ std bigfloat: vector push_back and read "unordered", "value" => 168]; $array = $arrays["item"]; var_dump($array["name"]); var_dump($array["value"]); - $objects = std::map(native_types::type_int, complex_types::type_object); + $objects = std::map(Type::Int, Type::Object); $objects[2] = new StdMapComplexValue(21); var_dump($objects[2] instanceof StdMapComplexValue); $object = $objects[2]->toObject(StdMapComplexValue::class); var_dump($object->getValue()); - $variants = std::map(native_types::type_int, complex_types::type_var); + $variants = std::map(Type::Int, Type::Any); $variants[3] = false; $variants[4] = "variant"; var_dump($variants[3]); diff --git a/tests/compiler/std-map/004.phpt b/tests/compiler/std-map/004.phpt index 1b8ed683..3c442d1a 100644 --- a/tests/compiler/std-map/004.phpt +++ b/tests/compiler/std-map/004.phpt @@ -24,7 +24,7 @@ function std_map_class_value_mixed(mixed $value): mixed } function main() { - $map = std::map(complex_types::type_str, StdMapClassValue::class); + $map = std::map(Type::String, StdMapClassValue::class); $map["a"] = new StdMapClassValue(1); var_dump($map["a"]->getValue()); diff --git a/tests/compiler/std-map/005.phpt b/tests/compiler/std-map/005.phpt index df408246..9b9b4db2 100644 --- a/tests/compiler/std-map/005.phpt +++ b/tests/compiler/std-map/005.phpt @@ -4,13 +4,13 @@ std map: unsafe_cast toStdMap(native_types::type_int, native_types::type_int); + $map = $source->toStdMap(Type::Int, Type::Int); var_dump($map[2]); $map[3] = 9; } function main() { - $map = std::map(native_types::type_int, native_types::type_int); + $map = std::map(Type::Int, Type::Int); $map[1] = 1; $map[2] = 7; $map[3] = 3; diff --git a/tests/compiler/std-map/006.phpt b/tests/compiler/std-map/006.phpt index a2f890c4..0f46e2f6 100644 --- a/tests/compiler/std-map/006.phpt +++ b/tests/compiler/std-map/006.phpt @@ -4,11 +4,11 @@ std map: unsafe_cast type mismatch toStdMap(native_types::type_int, native_types::type_float); + $map = $source->toStdMap(Type::Int, Type::Float); } function main() { - $map = std::map(native_types::type_int, native_types::type_int); + $map = std::map(Type::Int, Type::Int); try { std_map_unsafe_ptr_type_mismatch($map); } catch (TypeError $e) { diff --git a/tests/compiler/std-map/007.phpt b/tests/compiler/std-map/007.phpt index de7c047f..28dacea9 100644 --- a/tests/compiler/std-map/007.phpt +++ b/tests/compiler/std-map/007.phpt @@ -3,7 +3,7 @@ std map: assign to PHP array --FILE-- "map", "value" => 84]; $array = $arrays[2]; var_dump($array["name"]); var_dump($array["value"]); - $objects = std::ordered_map(complex_types::type_string, complex_types::type_object); + $objects = std::ordered_map(Type::String, Type::Object); $objects["item"] = new StdMapComplexValue(14); var_dump($objects["item"] instanceof StdMapComplexValue); $object = $objects["item"]->toObject(StdMapComplexValue::class); var_dump($object->getValue()); - $variants = std::ordered_map(native_types::type_int, complex_types::type_any); + $variants = std::ordered_map(Type::Int, Type::Any); $variants[3] = 12.5; $variants[4] = "any"; var_dump($variants[3]); diff --git a/tests/compiler/std-ordered-map/004.phpt b/tests/compiler/std-ordered-map/004.phpt index 8e03970a..188448a1 100644 --- a/tests/compiler/std-ordered-map/004.phpt +++ b/tests/compiler/std-ordered-map/004.phpt @@ -28,7 +28,7 @@ function std_container_class_value_mixed(mixed $value): mixed } function main() { - $map = std::ordered_map(complex_types::type_str, StdContainerClassValue::class); + $map = std::ordered_map(Type::String, StdContainerClassValue::class); $map["a"] = new StdContainerClassValue(1); $item = $map["a"]; var_dump($item->getValue()); @@ -41,7 +41,7 @@ function main() { $array[0] = new StdContainerClassValue(3); var_dump($array[0]->getValue()); - $unordered = std::map(native_types::type_int, StdContainerClassValue::class); + $unordered = std::map(Type::Int, StdContainerClassValue::class); $unordered[1] = std_container_class_value_mixed(new StdContainerClassValue(4)); var_dump($unordered[1]->getValue()); diff --git a/tests/compiler/std-ordered-map/005.phpt b/tests/compiler/std-ordered-map/005.phpt index f7d893ea..d106dafa 100644 --- a/tests/compiler/std-ordered-map/005.phpt +++ b/tests/compiler/std-ordered-map/005.phpt @@ -4,7 +4,7 @@ std ordered_map: unsafe_cast toStdOrderedMap(complex_types::type_str, native_types::type_int); + $map = $source->toStdOrderedMap(Type::String, Type::Int); var_dump($map["b"]); $map["c"] = 9; @@ -15,7 +15,7 @@ function std_map_unsafe_ptr_update($source): void } function main() { - $map = std::ordered_map(complex_types::type_str, native_types::type_int); + $map = std::ordered_map(Type::String, Type::Int); $map["a"] = 1; $map["b"] = 7; $map["c"] = 3; diff --git a/tests/compiler/std-ordered-map/006.phpt b/tests/compiler/std-ordered-map/006.phpt index cf407278..8de9ea2c 100644 --- a/tests/compiler/std-ordered-map/006.phpt +++ b/tests/compiler/std-ordered-map/006.phpt @@ -4,11 +4,11 @@ std ordered_map: unsafe_cast type mismatch toStdMap(complex_types::type_str, native_types::type_float); + $map = $source->toStdMap(Type::String, Type::Float); } function main() { - $map = std::ordered_map(complex_types::type_str, native_types::type_int); + $map = std::ordered_map(Type::String, Type::Int); try { std_map_unsafe_ptr_type_mismatch($map); } catch (TypeError $e) { diff --git a/tests/compiler/std-ordered-map/007.phpt b/tests/compiler/std-ordered-map/007.phpt index 55a42fac..a46898de 100644 --- a/tests/compiler/std-ordered-map/007.phpt +++ b/tests/compiler/std-ordered-map/007.phpt @@ -3,7 +3,7 @@ std ordered_map: assign to PHP array --FILE-- "vector", "value" => 42]; $array = $arrays[0]; var_dump($array["name"]); var_dump($array["value"]); - $objects = std::vector(complex_types::type_object); + $objects = std::vector(Type::Object); $objects[] = new StdVectorComplexValue(7); var_dump($objects[0] instanceof StdVectorComplexValue); $object = $objects[0]->toObject(StdVectorComplexValue::class); var_dump($object->getValue()); - $variants = std::vector(complex_types::type_variant); + $variants = std::vector(Type::Any); $variants[] = 99; $variants[] = "mixed"; var_dump($variants[0]); diff --git a/tests/compiler/std-vector/006.phpt b/tests/compiler/std-vector/006.phpt index 4483f6f1..740211c1 100644 --- a/tests/compiler/std-vector/006.phpt +++ b/tests/compiler/std-vector/006.phpt @@ -4,13 +4,13 @@ std vector: unsafe_cast toStdVector(native_types::type_int); + $vector = $source->toStdVector(Type::Int); var_dump($vector[1]); $vector[2] = 9; } function main() { - $vector = std::vector(native_types::type_int, 3); + $vector = std::vector(Type::Int, 3); $vector[0] = 1; $vector[1] = 7; $vector[2] = 3; diff --git a/tests/compiler/std-vector/007.phpt b/tests/compiler/std-vector/007.phpt index e6be5234..cadc4df1 100644 --- a/tests/compiler/std-vector/007.phpt +++ b/tests/compiler/std-vector/007.phpt @@ -4,11 +4,11 @@ std vector: unsafe_cast type mismatch toStdVector(native_types::type_float); + $vector = $source->toStdVector(Type::Float); } function main() { - $vector = std::vector(native_types::type_int, 3); + $vector = std::vector(Type::Int, 3); try { std_vector_unsafe_ptr_type_mismatch($vector); } catch (TypeError $e) { diff --git a/tests/compiler/std-vector/010.phpt b/tests/compiler/std-vector/010.phpt index 7b628334..0f925448 100644 --- a/tests/compiler/std-vector/010.phpt +++ b/tests/compiler/std-vector/010.phpt @@ -3,7 +3,7 @@ std vector: assign to PHP array --FILE-- getValue()); - $map = std::map(complex_types::type_str, StdContainerInterfaceValue::class); + $map = std::map(Type::String, StdContainerInterfaceValue::class); $map["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(4)); var_dump($map["item"]->getValue()); - $ordered = std::ordered_map(complex_types::type_str, StdContainerInterfaceValue::class); + $ordered = std::ordered_map(Type::String, StdContainerInterfaceValue::class); $ordered["item"] = std_container_interface_mixed(new StdContainerInterfaceImpl(5)); var_dump($ordered["item"]->getValue()); diff --git a/tests/compiler/stream_method/extension.phpt b/tests/compiler/stream_method/extension.phpt index 840329c5..34386271 100644 --- a/tests/compiler/stream_method/extension.phpt +++ b/tests/compiler/stream_method/extension.phpt @@ -3,7 +3,7 @@ stream extension method support --FILE--