refactor(generator): replace self::TYPE_* constants with Type::* constants

- Replaced all occurrences of self::TYPE_* with Type::* in AnonClassGenerator.php
- Updated ArrayExpressionTrait.php to use Type::ARRAY and Type::VAR instead of self::TYPE_*
- Modified AssignOpTrait.php to reference Type constants instead of self::TYPE_*
- Changed BinaryOpTrait.php to use Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT constants
- Updated CallArgumentGenerator.php to use Type::ARRAY and Type::REF constants
- Added Type import statements to all modified files
- Maintained same functionality while improving code consistency with Type namespace
pull/17/head
韩天峰 2 months ago
parent bc435f1dab
commit a0ece071cc
  1. 2
      docs/REFACTORING_PLAN.md
  2. 33
      phpunit/src/CompilerBaseApiTest.php
  3. 7
      phpunit/src/Generator/UtilsTest.php
  4. 35
      phpunit/src/SsaAnalysisTest.php
  5. 25
      phpunit/src/TraitsTest.php
  6. 296
      src/CompilerBase.php
  7. 4
      src/Generator/AnonClassGenerator.php
  8. 32
      src/Generator/CallArgumentGenerator.php
  9. 26
      src/Generator/ClosureGenerator.php
  10. 6
      src/Generator/DefaultArgumentGenerator.php
  11. 22
      src/Generator/FiberGenerator.php
  12. 10
      src/Generator/TypeCheckGenerator.php
  13. 5
      src/Generator/Utils.php
  14. 38
      src/Optimizer/FuncCallOptimizer.php
  15. 6
      src/Optimizer/LoopVarOptimizer.php
  16. 12
      src/Optimizer/SsaPropOptimizer.php
  17. 34
      src/Optimizer/SsaTypeOptimizer.php
  18. 22
      src/Parser/ArrayExpressionTrait.php
  19. 96
      src/Parser/AssignOpTrait.php
  20. 82
      src/Parser/BinaryOpTrait.php
  21. 4
      src/Parser/ClassConstantFetchTrait.php
  22. 10
      src/Parser/ConstantExpressionTrait.php
  23. 14
      src/Parser/ExceptionControlFlowTrait.php
  24. 22
      src/Parser/ForeachTrait.php
  25. 6
      src/Parser/FunctionCallTrait.php
  26. 8
      src/Parser/LoopControlTrait.php
  27. 56
      src/Parser/MethodCallTrait.php
  28. 12
      src/Parser/NullsafeAccessTrait.php
  29. 62
      src/Parser/PropertyAccessTrait.php
  30. 20
      src/Parser/SelectionExpressionTrait.php
  31. 88
      src/Parser/StdContainerTrait.php
  32. 6
      src/Parser/SwitchTrait.php
  33. 62
      src/Parser/TypeConversionTrait.php
  34. 6
      src/Parser/TypeDetectionTrait.php
  35. 10
      src/Parser/UnaryExpressionTrait.php
  36. 636
      src/Parser/UniversalMethodCall.php
  37. 30
      src/Preprocessor.php
  38. 10
      src/Resolver/DeclarationSymbolTrait.php
  39. 86
      src/Resolver/MagicMethodDetector.php
  40. 8
      src/Resolver/NameResolutionTrait.php
  41. 47
      src/Resolver/PropertyAssignTypeInfo.php
  42. 96
      src/Translator.php
  43. 27
      src/Type.php
  44. 42
      src/TypeSystem/CompositeTypeCheckerTrait.php
  45. 16
      src/TypeSystem/NativeTypeCompatibilityTrait.php

@ -1,5 +1,7 @@
# AOT 编译器核心重构计划 # AOT 编译器核心重构计划
> 针对 `Translator`、`CompilerBase`、`Preprocessor` 的下一阶段 OOA/OOD/OOP 重构,请以 [CORE_OOA_OOD_OOP_REFACTORING_PLAN.md](CORE_OOA_OOD_OOP_REFACTORING_PLAN.md) 为实施基线。本文档保留此前模块化重构的历史计划。
## 背景 ## 背景
当前 AOT 编译器核心类承担了过多职责,尤其是 `CompilerBase`、`Translator` 等类同时包含 AST 分发、类型推导、属性访问解析、调用解析、代码生成、诊断信息、上下文状态维护等逻辑。随着功能持续增加,这种结构会带来以下问题: 当前 AOT 编译器核心类承担了过多职责,尤其是 `CompilerBase`、`Translator` 等类同时包含 AST 分发、类型推导、属性访问解析、调用解析、代码生成、诊断信息、上下文状态维护等逻辑。随着功能持续增加,这种结构会带来以下问题:

@ -5,6 +5,7 @@ namespace TypePhp\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
use TypePhp\CompilerBase; use TypePhp\CompilerBase;
use TypePhp\Type;
use TypePhp\Exception\TestError; use TypePhp\Exception\TestError;
use TypePhp\Platform\Windows; use TypePhp\Platform\Windows;
@ -159,26 +160,26 @@ class CompilerBaseApiTest extends TestCase
public function testGetTypeFromZendTypeKnown(): void public function testGetTypeFromZendTypeKnown(): void
{ {
$this->assertEquals(CompilerBase::TYPE_INT, $this->compiler->getTypeFromZendType('int')); $this->assertEquals(Type::INT, $this->compiler->getTypeFromZendType('int'));
$this->assertEquals(CompilerBase::TYPE_FLOAT, $this->compiler->getTypeFromZendType('float')); $this->assertEquals(Type::FLOAT, $this->compiler->getTypeFromZendType('float'));
$this->assertEquals(CompilerBase::TYPE_BOOL, $this->compiler->getTypeFromZendType('bool')); $this->assertEquals(Type::BOOL, $this->compiler->getTypeFromZendType('bool'));
$this->assertEquals(CompilerBase::TYPE_BOOL, $this->compiler->getTypeFromZendType('true')); $this->assertEquals(Type::BOOL, $this->compiler->getTypeFromZendType('true'));
$this->assertEquals(CompilerBase::TYPE_BOOL, $this->compiler->getTypeFromZendType('false')); $this->assertEquals(Type::BOOL, $this->compiler->getTypeFromZendType('false'));
$this->assertEquals(CompilerBase::TYPE_VOID, $this->compiler->getTypeFromZendType('void')); $this->assertEquals(Type::VOID, $this->compiler->getTypeFromZendType('void'));
$this->assertEquals(CompilerBase::TYPE_VOID, $this->compiler->getTypeFromZendType('never')); $this->assertEquals(Type::VOID, $this->compiler->getTypeFromZendType('never'));
$this->assertEquals(CompilerBase::TYPE_STR, $this->compiler->getTypeFromZendType('string')); $this->assertEquals(Type::STR, $this->compiler->getTypeFromZendType('string'));
$this->assertEquals(CompilerBase::TYPE_ARRAY, $this->compiler->getTypeFromZendType('array')); $this->assertEquals(Type::ARRAY, $this->compiler->getTypeFromZendType('array'));
$this->assertEquals(CompilerBase::TYPE_OBJECT, $this->compiler->getTypeFromZendType('object')); $this->assertEquals(Type::OBJECT, $this->compiler->getTypeFromZendType('object'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('mixed')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('mixed'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('null')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('null'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('callable')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('callable'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('iterable')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('iterable'));
} }
public function testGetTypeFromZendTypeUnknown(): void public function testGetTypeFromZendTypeUnknown(): void
{ {
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('unknown_type')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('unknown_type'));
$this->assertEquals(CompilerBase::TYPE_VAR, $this->compiler->getTypeFromZendType('SomeClass')); $this->assertEquals(Type::VAR, $this->compiler->getTypeFromZendType('SomeClass'));
} }
// ======================================================================== // ========================================================================

@ -2,9 +2,10 @@
namespace TypePhp\Tests\Generator; namespace TypePhp\Tests\Generator;
use TypePhp\Type;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
use TypePhp\CompilerBase;
class UtilsTest extends TestCase class UtilsTest extends TestCase
{ {
@ -97,14 +98,14 @@ class UtilsTest extends TestCase
public function testGenArray(): void public function testGenArray(): void
{ {
$result = $this->invokeMethod('genArray', ['1', '2', '3']); $result = $this->invokeMethod('genArray', ['1', '2', '3']);
$this->assertStringStartsWith(CompilerBase::TYPE_ARRAY . '{', $result); $this->assertStringStartsWith(Type::ARRAY . '{', $result);
$this->assertStringContainsString('1, 2, 3', $result); $this->assertStringContainsString('1, 2, 3', $result);
} }
public function testGenArrayEmpty(): void public function testGenArrayEmpty(): void
{ {
$result = $this->invokeMethod('genArray', []); $result = $this->invokeMethod('genArray', []);
$this->assertStringStartsWith(CompilerBase::TYPE_ARRAY . '{', $result); $this->assertStringStartsWith(Type::ARRAY . '{', $result);
} }
// ======================================================================== // ========================================================================

@ -2,6 +2,8 @@
namespace TypePhp\Tests; namespace TypePhp\Tests;
use TypePhp\Type;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\Analysis\SsaBuilder; use TypePhp\Analysis\SsaBuilder;
use TypePhp\Analysis\SsaFlags; use TypePhp\Analysis\SsaFlags;
@ -9,7 +11,6 @@ use TypePhp\Analysis\SsaVar;
use TypePhp\Analysis\SsaBlock; use TypePhp\Analysis\SsaBlock;
use TypePhp\Analysis\VarState; use TypePhp\Analysis\VarState;
use TypePhp\Analysis\PiConstraint; use TypePhp\Analysis\PiConstraint;
use TypePhp\CompilerBase;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
use TypePhp\Entity\ClassDef; use TypePhp\Entity\ClassDef;
use TypePhp\Entity\MethodDef; use TypePhp\Entity\MethodDef;
@ -1075,7 +1076,7 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['n'] ?? null); $this->assertSame(Type::INT, $locals['n'] ?? null);
} }
public function testLoopVarOptimizerNarrowsForCounterAndConstantBoundVar(): void public function testLoopVarOptimizerNarrowsForCounterAndConstantBoundVar(): void
@ -1087,8 +1088,8 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['i'] ?? null); $this->assertSame(Type::INT, $locals['i'] ?? null);
$this->assertSame(CompilerBase::TYPE_INT, $locals['n'] ?? null); $this->assertSame(Type::INT, $locals['n'] ?? null);
} }
public function testLoopVarOptimizerNarrowsForCounterWithStrlenBound(): void public function testLoopVarOptimizerNarrowsForCounterWithStrlenBound(): void
@ -1099,7 +1100,7 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['i'] ?? null); $this->assertSame(Type::INT, $locals['i'] ?? null);
} }
public function testLoopVarOptimizerNarrowsForCounterWithGenericIntFunctionBound(): void public function testLoopVarOptimizerNarrowsForCounterWithGenericIntFunctionBound(): void
@ -1110,7 +1111,7 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['i'] ?? null); $this->assertSame(Type::INT, $locals['i'] ?? null);
} }
public function testLoopVarOptimizerRejectsInclusiveGenericIntFunctionBound(): void public function testLoopVarOptimizerRejectsInclusiveGenericIntFunctionBound(): void
@ -1132,7 +1133,7 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['i'] ?? null); $this->assertSame(Type::INT, $locals['i'] ?? null);
} }
public function testLoopVarOptimizerNarrowsDescendingCounterFromIntMethod(): void public function testLoopVarOptimizerNarrowsDescendingCounterFromIntMethod(): void
@ -1143,7 +1144,7 @@ class SsaAnalysisTest extends TestCase
} }
'); ');
$this->assertSame(CompilerBase::TYPE_INT, $locals['i'] ?? null); $this->assertSame(Type::INT, $locals['i'] ?? null);
} }
public function testLoopVarOptimizerRejectsBodyCounterMutation(): void public function testLoopVarOptimizerRejectsBodyCounterMutation(): void
@ -1210,7 +1211,7 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assign); $ssaVar->definition = new Stmt\Expression($assign);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_INT, $result); $this->assertEquals(Type::INT, $result);
} }
public function testDetectSsaDefTypeFloatLiteral(): void public function testDetectSsaDefTypeFloatLiteral(): void
@ -1220,7 +1221,7 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assign); $ssaVar->definition = new Stmt\Expression($assign);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_FLOAT, $result); $this->assertEquals(Type::FLOAT, $result);
} }
public function testDetectSsaDefTypeStringLiteral(): void public function testDetectSsaDefTypeStringLiteral(): void
@ -1230,7 +1231,7 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assign); $ssaVar->definition = new Stmt\Expression($assign);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_STR, $result); $this->assertEquals(Type::STR, $result);
} }
public function testDetectSsaDefTypeBoolLiteral(): void public function testDetectSsaDefTypeBoolLiteral(): void
@ -1240,15 +1241,15 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assign); $ssaVar->definition = new Stmt\Expression($assign);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_BOOL, $result); $this->assertEquals(Type::BOOL, $result);
} }
public function testDetectTypeOfExplicitStdNativeCalls(): void public function testDetectTypeOfExplicitStdNativeCalls(): void
{ {
$cases = [ $cases = [
'int' => CompilerBase::TYPE_INT, 'int' => Type::INT,
'float' => CompilerBase::TYPE_FLOAT, 'float' => Type::FLOAT,
'bool' => CompilerBase::TYPE_BOOL, 'bool' => Type::BOOL,
]; ];
foreach ($cases as $method => $expectedType) { foreach ($cases as $method => $expectedType) {
@ -1298,7 +1299,7 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assignOp); $ssaVar->definition = new Stmt\Expression($assignOp);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_FLOAT, $result); $this->assertEquals(Type::FLOAT, $result);
} }
public function testDetectSsaDefTypeAssignOpPlusIsUnknownForIntRhs(): void public function testDetectSsaDefTypeAssignOpPlusIsUnknownForIntRhs(): void
@ -1330,7 +1331,7 @@ class SsaAnalysisTest extends TestCase
$ssaVar->definition = new Stmt\Expression($assign); $ssaVar->definition = new Stmt\Expression($assign);
$result = $this->invoke('detectSsaDefType', $ssaVar); $result = $this->invoke('detectSsaDefType', $ssaVar);
$this->assertEquals(CompilerBase::TYPE_INT, $result); $this->assertEquals(Type::INT, $result);
} }
// ======================================================================== // ========================================================================

@ -2,9 +2,10 @@
namespace TypePhp\Tests; namespace TypePhp\Tests;
use TypePhp\Type;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use TypePhp\CompilerTest; use TypePhp\CompilerTest;
use TypePhp\CompilerBase;
use TypePhp\Entity\ArgInfo; use TypePhp\Entity\ArgInfo;
class TraitsTest extends TestCase class TraitsTest extends TestCase
@ -123,18 +124,18 @@ class TraitsTest extends TestCase
public function testIsStdContainerTypeTrue(): void public function testIsStdContainerTypeTrue(): void
{ {
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_ARRAY)); $this->assertTrue($this->invoke('isStdContainerType', Type::STD_ARRAY));
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_VECTOR)); $this->assertTrue($this->invoke('isStdContainerType', Type::STD_VECTOR));
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_MAP)); $this->assertTrue($this->invoke('isStdContainerType', Type::STD_MAP));
$this->assertTrue($this->invoke('isStdContainerType', CompilerBase::TYPE_STD_ORDERED_MAP)); $this->assertTrue($this->invoke('isStdContainerType', Type::STD_ORDERED_MAP));
} }
public function testIsStdContainerTypeFalse(): void public function testIsStdContainerTypeFalse(): void
{ {
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_ARRAY)); $this->assertFalse($this->invoke('isStdContainerType', Type::ARRAY));
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_INT)); $this->assertFalse($this->invoke('isStdContainerType', Type::INT));
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_VAR)); $this->assertFalse($this->invoke('isStdContainerType', Type::VAR));
$this->assertFalse($this->invoke('isStdContainerType', CompilerBase::TYPE_OBJECT)); $this->assertFalse($this->invoke('isStdContainerType', Type::OBJECT));
} }
// ======================================================================== // ========================================================================
@ -304,8 +305,8 @@ class TraitsTest extends TestCase
public function testGetStdValueTypeBytes(): void public function testGetStdValueTypeBytes(): void
{ {
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_INT)); $this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', Type::INT));
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_FLOAT)); $this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', Type::FLOAT));
$this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', CompilerBase::TYPE_BOOL)); $this->assertGreaterThan(0, $this->invoke('getStdValueTypeBytes', Type::BOOL));
} }
} }

@ -143,19 +143,6 @@ class CompilerBase implements PropertyAccessContext
use LoopVarOptimizer; use LoopVarOptimizer;
use SsaPropOptimizer; use SsaPropOptimizer;
public const string TYPE_VAR = 'php::Var';
public const string TYPE_BOOL = 'php::Bool';
public const string TYPE_INT = 'php::Int';
public const string TYPE_FLOAT = 'php::Float';
public const string TYPE_OBJECT = 'php::Object';
public const string TYPE_ARRAY = 'php::Array';
public const string TYPE_RESOURCE = 'php::Resource';
public const string TYPE_STREAM = 'php::Stream';
public const string TYPE_BIGINT = 'php::BigInt';
public const string TYPE_DECIMAL = 'php::Decimal';
public const string TYPE_BIGFLOAT = 'php::BigFloat';
public const string TYPE_BOX = 'php::Box';
protected const string NATIVE_PROPERTY_VALUE_VAR = 'var'; protected const string NATIVE_PROPERTY_VALUE_VAR = 'var';
protected const string NATIVE_PROPERTY_VALUE_DYNAMIC = 'dynamic'; protected const string NATIVE_PROPERTY_VALUE_DYNAMIC = 'dynamic';
protected const int COMPOSITE_TYPE_MISMATCH = -1; protected const int COMPOSITE_TYPE_MISMATCH = -1;
@ -169,18 +156,18 @@ class CompilerBase implements PropertyAccessContext
* Use findKeywordMethod() for unified lookup including keyword extension methods. * Use findKeywordMethod() for unified lookup including keyword extension methods.
*/ */
public const array KEYWORD_METHOD_MAP = [ public const array KEYWORD_METHOD_MAP = [
'toInt' => self::TYPE_INT, 'toInt' => Type::INT,
'toFloat' => self::TYPE_FLOAT, 'toFloat' => Type::FLOAT,
'toString' => self::TYPE_STR, 'toString' => Type::STR,
'toBool' => self::TYPE_BOOL, 'toBool' => Type::BOOL,
'toArray' => self::TYPE_ARRAY, 'toArray' => Type::ARRAY,
'toStream' => self::TYPE_STREAM, 'toStream' => Type::STREAM,
'toBigInt' => self::TYPE_BIGINT, 'toBigInt' => Type::BIGINT,
'toBigFloat' => self::TYPE_BIGFLOAT, 'toBigFloat' => Type::BIGFLOAT,
'toDecimal' => self::TYPE_DECIMAL, 'toDecimal' => Type::DECIMAL,
'toObject' => self::TYPE_OBJECT, 'toObject' => Type::OBJECT,
'toAny' => self::TYPE_VAR, 'toAny' => Type::VAR,
'toRef' => self::TYPE_REF, 'toRef' => Type::REF,
]; ];
private const array STREAM_FUNCTIONS = [ private const array STREAM_FUNCTIONS = [
@ -191,15 +178,6 @@ class CompilerBase implements PropertyAccessContext
'stream_socket_accept', 'stream_socket_accept',
'popen', 'popen',
]; ];
public const string TYPE_STD_ARRAY = 'php::StdArray';
public const string TYPE_STD_VECTOR = 'php::StdVector';
public const string TYPE_STD_MAP = 'php::StdMap';
public const string TYPE_STD_ORDERED_MAP = 'php::StdOrderedMap';
public const string TYPE_ARGS = 'php::Args';
public const string TYPE_STR = 'php::Str';
public const string TYPE_REF = 'php::Ref';
public const string TYPE_VOID = 'void';
public const int DECL_TYPE_OF_RETURN = 1; public const int DECL_TYPE_OF_RETURN = 1;
public const int DECL_TYPE_OF_PROPERTY = 2; public const int DECL_TYPE_OF_PROPERTY = 2;
public const int DECL_TYPE_OF_CONST = 3; public const int DECL_TYPE_OF_CONST = 3;
@ -267,30 +245,30 @@ class CompilerBase implements PropertyAccessContext
protected int $propIndex = 0; protected int $propIndex = 0;
protected array $propMap = []; protected array $propMap = [];
protected array $zendTypeMap = [ protected array $zendTypeMap = [
'int' => self::TYPE_INT, 'int' => Type::INT,
'float' => self::TYPE_FLOAT, 'float' => Type::FLOAT,
'double' => self::TYPE_FLOAT, 'double' => Type::FLOAT,
'bool' => self::TYPE_BOOL, 'bool' => Type::BOOL,
'false' => self::TYPE_BOOL, 'false' => Type::BOOL,
'true' => self::TYPE_BOOL, 'true' => Type::BOOL,
'void' => self::TYPE_VOID, 'void' => Type::VOID,
'never' => self::TYPE_VOID, 'never' => Type::VOID,
'string' => self::TYPE_STR, 'string' => Type::STR,
'array' => self::TYPE_ARRAY, 'array' => Type::ARRAY,
'object' => self::TYPE_OBJECT, 'object' => Type::OBJECT,
'mixed' => self::TYPE_VAR, 'mixed' => Type::VAR,
'null' => self::TYPE_VAR, 'null' => Type::VAR,
'any' => self::TYPE_VAR, 'any' => Type::VAR,
// callable 类型,可以是字符串、数组、对象 // callable 类型,可以是字符串、数组、对象
// 1) 'foo' 函数名称字符串, 2) [ $obj, 'bar' ] 对象方法数组, 3) Closure 对象, 4) [ 'class', 'staticMethod'] 类名+静态方法数组 // 1) 'foo' 函数名称字符串, 2) [ $obj, 'bar' ] 对象方法数组, 3) Closure 对象, 4) [ 'class', 'staticMethod'] 类名+静态方法数组
'callable' => self::TYPE_VAR, 'callable' => Type::VAR,
// iterable 类型,可以是数组或者对象 // iterable 类型,可以是数组或者对象
'iterable' => self::TYPE_VAR, 'iterable' => Type::VAR,
'stream' => self::TYPE_STREAM, 'stream' => Type::STREAM,
'bigint' => self::TYPE_BIGINT, 'bigint' => Type::BIGINT,
'bigfloat' => self::TYPE_BIGFLOAT, 'bigfloat' => Type::BIGFLOAT,
'decimal' => self::TYPE_DECIMAL, 'decimal' => Type::DECIMAL,
'box' => self::TYPE_BOX, 'box' => Type::BOX,
]; ];
protected array $globalHeaders = [ protected array $globalHeaders = [
'phpx.h', 'phpx.h',
@ -408,15 +386,15 @@ class CompilerBase implements PropertyAccessContext
private ?DiagnosticReporter $diagnosticReporter = null; private ?DiagnosticReporter $diagnosticReporter = null;
protected FunctionContext $context; protected FunctionContext $context;
protected array $superGlobalVars = [ protected array $superGlobalVars = [
'_GET' => self::TYPE_ARRAY, '_GET' => Type::ARRAY,
'_POST' => self::TYPE_ARRAY, '_POST' => Type::ARRAY,
'_COOKIE' => self::TYPE_ARRAY, '_COOKIE' => Type::ARRAY,
'_SERVER' => self::TYPE_ARRAY, '_SERVER' => Type::ARRAY,
'_FILES' => self::TYPE_ARRAY, '_FILES' => Type::ARRAY,
'_SESSION' => self::TYPE_ARRAY, '_SESSION' => Type::ARRAY,
'_REQUEST' => self::TYPE_ARRAY, '_REQUEST' => Type::ARRAY,
'_ENV' => self::TYPE_ARRAY, '_ENV' => Type::ARRAY,
'GLOBALS' => self::TYPE_ARRAY, 'GLOBALS' => Type::ARRAY,
]; ];
protected array $globalVars = []; protected array $globalVars = [];
protected bool $nativeTypes = false; protected bool $nativeTypes = false;
@ -649,7 +627,7 @@ class CompilerBase implements PropertyAccessContext
public function getTypeFromZendType(string $type): string public function getTypeFromZendType(string $type): string
{ {
return $this->zendTypeMap[$type] ?? self::TYPE_VAR; return $this->zendTypeMap[$type] ?? Type::VAR;
} }
public function getObjectType(string $object): string public function getObjectType(string $object): string
@ -976,7 +954,7 @@ class CompilerBase implements PropertyAccessContext
return $this->context->globalVars[$name]; return $this->context->globalVars[$name];
} }
return self::TYPE_VAR; return Type::VAR;
} }
/** /**
@ -1097,7 +1075,7 @@ class CompilerBase implements PropertyAccessContext
protected function isVoidValueExpr(NodeAbstract $expr): bool protected function isVoidValueExpr(NodeAbstract $expr): bool
{ {
return $this->detectTypeOfExpr($expr) === self::TYPE_VOID; return $this->detectTypeOfExpr($expr) === Type::VOID;
} }
protected function wrapVoidExprAsNull(NodeAbstract $expr, string $exprCode): string protected function wrapVoidExprAsNull(NodeAbstract $expr, string $exprCode): string
@ -1210,7 +1188,7 @@ class CompilerBase implements PropertyAccessContext
if (isset($this->context->ceWrappers[$className])) { if (isset($this->context->ceWrappers[$className])) {
return $this->context->ceWrappers[$className]; return $this->context->ceWrappers[$className];
} }
$object = $this->addTmpVar(self::TYPE_OBJECT); $object = $this->addTmpVar(Type::OBJECT);
$this->context->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';'; $this->context->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';';
$this->context->ceWrappers[$className] = $object; $this->context->ceWrappers[$className] = $object;
return $object; return $object;
@ -1259,7 +1237,7 @@ class CompilerBase implements PropertyAccessContext
*/ */
protected function getInlineString(string $string): string protected function getInlineString(string $string): string
{ {
return self::TYPE_STR . '{ZEND_STRL(' . $this->genCharPtr($string, true) . ')}'; return Type::STR . '{ZEND_STRL(' . $this->genCharPtr($string, true) . ')}';
} }
protected function parseScalar(Node\Scalar $expr): string protected function parseScalar(Node\Scalar $expr): string
@ -1518,7 +1496,7 @@ class CompilerBase implements PropertyAccessContext
$code = ''; $code = '';
$this->appendCapturedStmtLines($code, $beforeStmts); $this->appendCapturedStmtLines($code, $beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL; $code .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL;
$this->appendCapturedStmtLines($code, $afterStmts); $this->appendCapturedStmtLines($code, $afterStmts);
$condExpr = $tmpVar; $condExpr = $tmpVar;
@ -1874,7 +1852,7 @@ class CompilerBase implements PropertyAccessContext
{ {
if ($this->functionDef->returnsByRef) { if ($this->functionDef->returnsByRef) {
if ($v->expr === null) { if ($v->expr === null) {
return 'return ' . self::TYPE_REF . '{};'; return 'return ' . Type::REF . '{};';
} }
if (!$this->isVarExpr($v->expr) if (!$this->isVarExpr($v->expr)
&& !$this->isPropertyFetch($v->expr) && !$this->isPropertyFetch($v->expr)
@ -1887,7 +1865,7 @@ class CompilerBase implements PropertyAccessContext
if (!$this->hasVar($name)) { if (!$this->hasVar($name)) {
$this->errorUndefinedVariable($v->expr); $this->errorUndefinedVariable($v->expr);
} }
if ($this->hasLocalVar($name) && $this->getVarType($name) !== self::TYPE_VAR && $this->getVarType($name) !== self::TYPE_REF) { if ($this->hasLocalVar($name) && $this->getVarType($name) !== Type::VAR && $this->getVarType($name) !== Type::REF) {
$isParameter = false; $isParameter = false;
foreach ($this->functionDef->argInfoList as $argInfo) { foreach ($this->functionDef->argInfoList as $argInfo) {
if ($argInfo->name === $name) { if ($argInfo->name === $name) {
@ -1900,7 +1878,7 @@ class CompilerBase implements PropertyAccessContext
} }
// The declaration is emitted after parsing the body, so a local can // The declaration is emitted after parsing the body, so a local can
// be promoted to Variant before C++ is generated. // be promoted to Variant before C++ is generated.
$this->context->localVars[$name] = self::TYPE_VAR; $this->context->localVars[$name] = Type::VAR;
} }
return 'return ' . $name . '.toReference();'; return 'return ' . $name . '.toReference();';
} }
@ -1928,7 +1906,7 @@ class CompilerBase implements PropertyAccessContext
'return value' 'return value'
); );
} }
if ($this->functionDef->returnType === self::TYPE_VOID and !$this->context->inClosure) { if ($this->functionDef->returnType === Type::VOID and !$this->context->inClosure) {
return 'return;'; return 'return;';
} elseif ($this->shouldCheckClosureReturnType()) { } elseif ($this->shouldCheckClosureReturnType()) {
return $this->genClosureCheckedReturn(self::VALUE_NULL); return $this->genClosureCheckedReturn(self::VALUE_NULL);
@ -1965,11 +1943,11 @@ class CompilerBase implements PropertyAccessContext
// 匿名函数的返回值一定是 var // 匿名函数的返回值一定是 var
if (!$this->context->inClosure) { if (!$this->context->inClosure) {
if ($returnType === 'void') { if ($returnType === Type::VOID) {
$this->fatalError($v, 'The return type is void, cannot return any value'); $this->fatalError($v, 'The return type is void, cannot return any value');
} }
} else { } else {
$returnType = self::TYPE_VAR; $returnType = Type::VAR;
} }
$returnObjectCheckClass = ''; $returnObjectCheckClass = '';
@ -2053,7 +2031,7 @@ class CompilerBase implements PropertyAccessContext
protected function genCheckedReturnAssignment(string $exprCode, bool $closure): array protected function genCheckedReturnAssignment(string $exprCode, bool $closure): array
{ {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
$code = $tmpVar . ' = ' . $exprCode . ';' . PHP_EOL; $code = $tmpVar . ' = ' . $exprCode . ';' . PHP_EOL;
$code .= $closure ? $this->genClosureReturnCheck($tmpVar) : $this->genUnionReturnCheck($tmpVar); $code .= $closure ? $this->genClosureReturnCheck($tmpVar) : $this->genUnionReturnCheck($tmpVar);
@ -2244,7 +2222,7 @@ class CompilerBase implements PropertyAccessContext
continue; continue;
} }
$interfaceConstDef = $interfaceDef->constants[$const]; $interfaceConstDef = $interfaceDef->constants[$const];
if ($interfaceConstDef->type === self::TYPE_ARRAY) { if ($interfaceConstDef->type === Type::ARRAY) {
return self::PREFIX . $this->getNativeName($interfaceConstDef->name, $interfaceDef->namespace, $interfaceDef->name); return self::PREFIX . $this->getNativeName($interfaceConstDef->name, $interfaceDef->namespace, $interfaceDef->name);
} }
$expr->setAttribute('nativeConst', $interfaceConstDef); $expr->setAttribute('nativeConst', $interfaceConstDef);
@ -2257,7 +2235,7 @@ class CompilerBase implements PropertyAccessContext
if ($classDef instanceof ClassDef && !$this->checkAccessible($classDef, $constDef->flags)) { if ($classDef instanceof ClassDef && !$this->checkAccessible($classDef, $constDef->flags)) {
$this->fatalError($expr, 'Constant `' . $classDef->getNamespacedName() . '::' . $const . '` is not accessible'); $this->fatalError($expr, 'Constant `' . $classDef->getNamespacedName() . '::' . $const . '` is not accessible');
} }
if ($constDef->type === self::TYPE_ARRAY) { if ($constDef->type === Type::ARRAY) {
return self::PREFIX . $this->getNativeName($constDef->name, $classDef->namespace, $classDef->name); return self::PREFIX . $this->getNativeName($constDef->name, $classDef->namespace, $classDef->name);
} else { } else {
$expr->setAttribute('nativeConst', $constDef); $expr->setAttribute('nativeConst', $constDef);
@ -2321,7 +2299,7 @@ class CompilerBase implements PropertyAccessContext
} }
$name = $this->parseIdentifier($var); $name = $this->parseIdentifier($var);
if ($this->isStdContainer($name)) { if ($this->isStdContainer($name)) {
return self::TYPE_ARRAY; return Type::ARRAY;
} }
return $this->getVarType($name); return $this->getVarType($name);
} }
@ -2334,29 +2312,29 @@ class CompilerBase implements PropertyAccessContext
return $this->detectTypeOfExpr($expr->expr); return $this->detectTypeOfExpr($expr->expr);
case 'Expr_BitwiseNot': case 'Expr_BitwiseNot':
$inner = $this->detectTypeOfExpr($expr->expr); $inner = $this->detectTypeOfExpr($expr->expr);
return $inner === self::TYPE_BIGINT ? self::TYPE_BIGINT : self::TYPE_INT; return $inner === Type::BIGINT ? Type::BIGINT : Type::INT;
case 'Expr_Print': case 'Expr_Print':
case 'Expr_Cast_Int': case 'Expr_Cast_Int':
return self::TYPE_INT; return Type::INT;
case 'Scalar_Int': case 'Scalar_Int':
return $this->bigintTypes ? self::TYPE_BIGINT : self::TYPE_INT; return $this->bigintTypes ? Type::BIGINT : Type::INT;
case 'Expr_Cast_Float': case 'Expr_Cast_Float':
case 'Expr_Cast_Double': case 'Expr_Cast_Double':
return self::TYPE_FLOAT; return Type::FLOAT;
case 'Scalar_Float': case 'Scalar_Float':
if ($this->isBigIntLiteral($expr)) { if ($this->isBigIntLiteral($expr)) {
return self::TYPE_BIGINT; return Type::BIGINT;
} }
if ($this->isDecimalLiteral($expr) || $this->decimalTypes) { if ($this->isDecimalLiteral($expr) || $this->decimalTypes) {
return self::TYPE_DECIMAL; return Type::DECIMAL;
} }
return self::TYPE_FLOAT; return Type::FLOAT;
case 'Expr_Cast_Bool': case 'Expr_Cast_Bool':
case 'Scalar_Bool': case 'Scalar_Bool':
return self::TYPE_BOOL; return Type::BOOL;
case 'Expr_Array': case 'Expr_Array':
case 'Expr_Cast_Array': case 'Expr_Cast_Array':
return self::TYPE_ARRAY; return Type::ARRAY;
case 'Expr_BinaryOp_Plus': case 'Expr_BinaryOp_Plus':
case 'Expr_BinaryOp_Minus': case 'Expr_BinaryOp_Minus':
case 'Expr_BinaryOp_Mul': case 'Expr_BinaryOp_Mul':
@ -2371,24 +2349,24 @@ class CompilerBase implements PropertyAccessContext
case 'Expr_BinaryOp_BooleanAnd': case 'Expr_BinaryOp_BooleanAnd':
$leftType = $this->detectTypeOfExpr($expr->left); $leftType = $this->detectTypeOfExpr($expr->left);
$rightType = $this->detectTypeOfExpr($expr->right); $rightType = $this->detectTypeOfExpr($expr->right);
if ($leftType === self::TYPE_BIGFLOAT || $rightType === self::TYPE_BIGFLOAT) { if ($leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) {
return self::TYPE_BIGFLOAT; return Type::BIGFLOAT;
} }
if ($leftType === self::TYPE_DECIMAL || $rightType === self::TYPE_DECIMAL) { if ($leftType === Type::DECIMAL || $rightType === Type::DECIMAL) {
return self::TYPE_DECIMAL; return Type::DECIMAL;
} }
if ($leftType === self::TYPE_BIGINT || $rightType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) {
if ($exprType === 'Expr_BinaryOp_Div') { if ($exprType === 'Expr_BinaryOp_Div') {
// BigInt division produces BigInt (integer division); BigDecimal in future // BigInt division produces BigInt (integer division); BigDecimal in future
return self::TYPE_BIGINT; return Type::BIGINT;
} }
return self::TYPE_BIGINT; return Type::BIGINT;
} }
if ($leftType === self::TYPE_FLOAT || $rightType === self::TYPE_FLOAT) { if ($leftType === Type::FLOAT || $rightType === Type::FLOAT) {
return self::TYPE_FLOAT; return Type::FLOAT;
} }
if ($leftType === self::TYPE_INT || $rightType === self::TYPE_INT) { if ($leftType === Type::INT || $rightType === Type::INT) {
return self::TYPE_INT; return Type::INT;
} }
break; break;
case 'Expr_FuncCall': case 'Expr_FuncCall':
@ -2398,29 +2376,29 @@ class CompilerBase implements PropertyAccessContext
if (in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true) && !empty($expr->args)) { if (in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true) && !empty($expr->args)) {
$argType = $this->detectTypeOfExpr($expr->args[0]->value); $argType = $this->detectTypeOfExpr($expr->args[0]->value);
if ( if (
$argType === self::TYPE_BIGINT $argType === Type::BIGINT
&& in_array($name, ['abs', 'pow', 'sqrt'], true) && in_array($name, ['abs', 'pow', 'sqrt'], true)
) { ) {
return self::TYPE_BIGINT; return Type::BIGINT;
} }
if ( if (
$argType === self::TYPE_DECIMAL $argType === Type::DECIMAL
&& in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true) && in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true)
) { ) {
return self::TYPE_DECIMAL; return Type::DECIMAL;
} }
if ( if (
$argType === self::TYPE_BIGFLOAT $argType === Type::BIGFLOAT
&& in_array($name, ['abs', 'sqrt'], true) && in_array($name, ['abs', 'sqrt'], true)
) { ) {
return self::TYPE_BIGFLOAT; return Type::BIGFLOAT;
} }
} }
if (in_array($name, self::STREAM_FUNCTIONS)) { if (in_array($name, self::STREAM_FUNCTIONS)) {
return self::TYPE_STREAM; return Type::STREAM;
} }
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) { if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT; return Type::OBJECT;
} }
if ($this->hasFunction($name)) { if ($this->hasFunction($name)) {
return $this->getFunction($name)->returnType; return $this->getFunction($name)->returnType;
@ -2440,7 +2418,7 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->resolveObjectClassDef($expr->var); $classDef = $this->resolveObjectClassDef($expr->var);
if ($classDef !== null && $classDef->hasMethod($method)) { if ($classDef !== null && $classDef->hasMethod($method)) {
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) { if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT; return Type::OBJECT;
} }
return $classDef->getMethod($method)->getReturnType(); return $classDef->getMethod($method)->getReturnType();
} }
@ -2462,7 +2440,7 @@ class CompilerBase implements PropertyAccessContext
} else { } else {
$type = $this->detectTypeOfExpr($expr->var); $type = $this->detectTypeOfExpr($expr->var);
} }
if ($type !== self::TYPE_VAR && !$this->checkArgType($type, self::TYPE_OBJECT)) { if ($type !== Type::VAR && !$this->checkArgType($type, Type::OBJECT)) {
$retType = $this->detectUniversalMethodReturnType($type, $method); $retType = $this->detectUniversalMethodReturnType($type, $method);
if ($retType !== null) { if ($retType !== null) {
return $retType; return $retType;
@ -2474,19 +2452,19 @@ class CompilerBase implements PropertyAccessContext
if ($this->isNameExpr($expr->class) && $this->isIdExpr($expr->name)) { if ($this->isNameExpr($expr->class) && $this->isIdExpr($expr->name)) {
// First-class callable syntax creates a Closure, not a method return value // First-class callable syntax creates a Closure, not a method return value
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) { if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT; return Type::OBJECT;
} }
$className = $this->parseIdentifier($expr->class); $className = $this->parseIdentifier($expr->class);
if (strtolower($className) === 'std') { if (strtolower($className) === 'std') {
$method = strtolower($this->parseIdentifier($expr->name)); $method = strtolower($this->parseIdentifier($expr->name));
return match ($method) { return match ($method) {
'int' => self::TYPE_INT, 'int' => Type::INT,
'float' => self::TYPE_FLOAT, 'float' => Type::FLOAT,
'bool' => self::TYPE_BOOL, 'bool' => Type::BOOL,
'bigint' => self::TYPE_BIGINT, 'bigint' => Type::BIGINT,
'decimal' => self::TYPE_DECIMAL, 'decimal' => Type::DECIMAL,
'bigfloat' => self::TYPE_BIGFLOAT, 'bigfloat' => Type::BIGFLOAT,
default => self::TYPE_VAR, default => Type::VAR,
}; };
} }
if ($className === 'self') { if ($className === 'self') {
@ -2552,7 +2530,7 @@ class CompilerBase implements PropertyAccessContext
if ($attr['accessLevel'] === $attr['totalLevel']) { if ($attr['accessLevel'] === $attr['totalLevel']) {
return $this->context->stdArrays[$attr['var']]['type']; return $this->context->stdArrays[$attr['var']]['type'];
} else { } else {
return self::TYPE_ARRAY; return Type::ARRAY;
} }
} }
if ($this->isStdContainerExpr($expr)) { if ($this->isStdContainerExpr($expr)) {
@ -2564,7 +2542,7 @@ class CompilerBase implements PropertyAccessContext
} }
break; break;
case 'Expr_New': case 'Expr_New':
return self::TYPE_OBJECT; return Type::OBJECT;
case 'Expr_Assign': case 'Expr_Assign':
case 'Expr_AssignOp_BitwiseAnd': case 'Expr_AssignOp_BitwiseAnd':
case 'Expr_AssignOp_BitwiseOr': case 'Expr_AssignOp_BitwiseOr':
@ -2575,12 +2553,12 @@ class CompilerBase implements PropertyAccessContext
case 'Expr_ConstFetch': case 'Expr_ConstFetch':
return $this->detectConstType($expr); return $this->detectConstType($expr);
case 'Scalar_String': case 'Scalar_String':
return self::TYPE_STR; return Type::STR;
default: default:
break; break;
} }
return self::TYPE_VAR; return Type::VAR;
} }
protected function genDynamicPropIncDec($var, string $op, bool $isPre): ?string protected function genDynamicPropIncDec($var, string $op, bool $isPre): ?string
@ -2597,14 +2575,14 @@ class CompilerBase implements PropertyAccessContext
} }
if ($getter !== null && $setter !== null) { if ($getter !== null && $setter !== null) {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
$read = $this->emitPropertyHookGetterCall($var, $getter); $read = $this->emitPropertyHookGetterCall($var, $getter);
if ($isPre) { if ($isPre) {
$set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($tmpVar)); $set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($tmpVar));
$this->context->beforeStmtLines[] = "{$tmpVar} = {$read} {$op} 1; {$set};"; $this->context->beforeStmtLines[] = "{$tmpVar} = {$read} {$op} 1; {$set};";
} else { } else {
$nextVar = $this->genTmpVarName(); $nextVar = $this->genTmpVarName();
$this->addLocalVar($nextVar, self::TYPE_VAR); $this->addLocalVar($nextVar, Type::VAR);
$set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($nextVar)); $set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($nextVar));
$this->context->beforeStmtLines[] = "{$tmpVar} = {$read};"; $this->context->beforeStmtLines[] = "{$tmpVar} = {$read};";
$this->context->afterStmtLines[] = "{$nextVar} = {$tmpVar} {$op} 1; {$set};"; $this->context->afterStmtLines[] = "{$nextVar} = {$tmpVar} {$op} 1; {$set};";
@ -2616,7 +2594,7 @@ class CompilerBase implements PropertyAccessContext
} }
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
if ($isPre) { if ($isPre) {
$this->context->beforeStmtLines[] = "{$tmpVar} = " . $this->emitDynamicPropertyFetchRead($var, $target) . " {$op} 1; " . $this->emitDynamicPropertyFetchWrite($var, $tmpVar, $target) . ';'; $this->context->beforeStmtLines[] = "{$tmpVar} = " . $this->emitDynamicPropertyFetchRead($var, $target) . " {$op} 1; " . $this->emitDynamicPropertyFetchWrite($var, $tmpVar, $target) . ';';
} else { } else {
@ -2636,7 +2614,7 @@ class CompilerBase implements PropertyAccessContext
} }
$type = $this->detectVarType($expr->var); $type = $this->detectVarType($expr->var);
if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$this->fatalError($expr, 'Cannot use ++ on ' . $type . '. Use += 1 instead (Big* types are immutable).'); $this->fatalError($expr, 'Cannot use ++ on ' . $type . '. Use += 1 instead (Big* types are immutable).');
} }
$result = '++' . $this->parseWritableIdentifier($expr->var); $result = '++' . $this->parseWritableIdentifier($expr->var);
@ -2884,7 +2862,7 @@ class CompilerBase implements PropertyAccessContext
$this->errorUndefinedVariable($expr->var); $this->errorUndefinedVariable($expr->var);
} }
$type = $this->detectVarType($expr->var); $type = $this->detectVarType($expr->var);
if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$opName = $op === '+' ? '++' : '--'; $opName = $op === '+' ? '++' : '--';
$this->fatalError($expr, "Cannot use {$opName} on {$type}. Use " . ($op === '+' ? '+= 1' : '-= 1') . ' instead (Big* types are immutable).'); $this->fatalError($expr, "Cannot use {$opName} on {$type}. Use " . ($op === '+' ? '+= 1' : '-= 1') . ' instead (Big* types are immutable).');
} }
@ -2899,7 +2877,7 @@ class CompilerBase implements PropertyAccessContext
$class = $this->identifierToStr($expr->var->class); $class = $this->identifierToStr($expr->var->class);
$prop = $this->identifierToStr($expr->var->name); $prop = $this->identifierToStr($expr->var->name);
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . Symbol::getStaticProperty() . '(' . $class . ', ' . $prop . ');'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . Symbol::getStaticProperty() . '(' . $class . ', ' . $prop . ');';
$this->context->afterStmtLines[] = Symbol::setStaticProperty() . '(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);'; $this->context->afterStmtLines[] = Symbol::setStaticProperty() . '(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);';
@ -2927,7 +2905,7 @@ class CompilerBase implements PropertyAccessContext
} }
$type = $this->detectVarType($expr->var); $type = $this->detectVarType($expr->var);
if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$this->fatalError($expr, 'Cannot use -- on ' . $type . '. Use -= 1 instead (Big* types are immutable).'); $this->fatalError($expr, 'Cannot use -- on ' . $type . '. Use -= 1 instead (Big* types are immutable).');
} }
$result = '--' . $this->parseWritableIdentifier($expr->var); $result = '--' . $this->parseWritableIdentifier($expr->var);
@ -3054,7 +3032,7 @@ class CompilerBase implements PropertyAccessContext
return 'php::instanceOf(' . $value . ', ' . $classPtr . ')'; return 'php::instanceOf(' . $value . ', ' . $classPtr . ')';
} else { } else {
[$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr->expr); [$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr->expr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$this->appendCapturedStmtLinesToContext($beforeStmts); $this->appendCapturedStmtLinesToContext($beforeStmts);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';';
$this->appendCapturedStmtLinesToContext($afterStmts); $this->appendCapturedStmtLinesToContext($afterStmts);
@ -3107,10 +3085,10 @@ class CompilerBase implements PropertyAccessContext
foreach ($expr->vars as $v) { foreach ($expr->vars as $v) {
$name = $this->parseVariable($v); $name = $this->parseVariable($v);
if (!$this->hasGlobalVar($name)) { if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, self::TYPE_VAR); $this->addGlobalVar($name, Type::VAR);
} }
if (!$this->hasScopeGlobalVar($name)) { if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, self::TYPE_VAR); $this->addScopeGlobalVar($name, Type::VAR);
} }
} }
return ''; return '';
@ -3143,13 +3121,13 @@ class CompilerBase implements PropertyAccessContext
$list = []; $list = [];
foreach ($v->vars as $var) { foreach ($v->vars as $var) {
$varName = $this->escapeVarName($var->var->name); $varName = $this->escapeVarName($var->var->name);
$type = $var->default ? $this->detectTypeOfExpr($var->default) : self::TYPE_VAR; $type = $var->default ? $this->detectTypeOfExpr($var->default) : Type::VAR;
if ($var->default) { if ($var->default) {
$this->assertExprCanBeUsedAsValue($var->default, 'static variable default value'); $this->assertExprCanBeUsedAsValue($var->default, 'static variable default value');
} }
$globalVar = $this->addStaticVar($var->var, $varName, $type); $globalVar = $this->addStaticVar($var->var, $varName, $type);
$list[] = self::TYPE_VAR . ' &' . $varName . ' = ' . $this->escapeGlobalVar($globalVar) . ';'; $list[] = Type::VAR . ' &' . $varName . ' = ' . $this->escapeGlobalVar($globalVar) . ';';
if ($var->default) { if ($var->default) {
$initState = self::STATIC_VAR . $varName . '_initialized'; $initState = self::STATIC_VAR . $varName . '_initialized';
$initCode = $this->getIndent() . 'static bool ' . $initState . ' = false;'; $initCode = $this->getIndent() . 'static bool ' . $initState . ' = false;';
@ -3324,16 +3302,16 @@ class CompilerBase implements PropertyAccessContext
$this->fatalError($expr, 'Cannot use [] for reading'); $this->fatalError($expr, 'Cannot use [] for reading');
} }
$dim = $this->parseIdentifier($expr->dim); $dim = $this->parseIdentifier($expr->dim);
$list[] = '{php::ArrayDimFetch, ' . self::TYPE_VAR . '(' . $dim . ')}'; $list[] = '{php::ArrayDimFetch, ' . Type::VAR . '(' . $dim . ')}';
} elseif ($this->isPropertyFetch($expr)) { } elseif ($this->isPropertyFetch($expr)) {
$name = $this->identifierToStr($expr->name, literal: true); $name = $this->identifierToStr($expr->name, literal: true);
$list[] = '{php::PropertyFetch, ' . self::TYPE_VAR . '(' . $name . ')}'; $list[] = '{php::PropertyFetch, ' . Type::VAR . '(' . $name . ')}';
} elseif ($this->isVarExpr($expr)) { } elseif ($this->isVarExpr($expr)) {
$var = $this->parseIdentifier($expr); $var = $this->parseIdentifier($expr);
break; break;
} else { } else {
$var = $this->genTmpVarName(); $var = $this->genTmpVarName();
$this->addLocalVar($var, self::TYPE_VAR); $this->addLocalVar($var, Type::VAR);
$this->context->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';'; $this->context->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';';
break; break;
} }
@ -3343,7 +3321,7 @@ class CompilerBase implements PropertyAccessContext
$list = array_reverse($list); $list = array_reverse($list);
if ($getValue) { if ($getValue) {
$result = $this->addTmpVar(self::TYPE_VAR); $result = $this->addTmpVar(Type::VAR);
$node->setAttribute('chainOpResult', $result); $node->setAttribute('chainOpResult', $result);
return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')'; return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} else { } else {
@ -3390,7 +3368,7 @@ class CompilerBase implements PropertyAccessContext
return $this->getTypeFromZendType($returnType); return $this->getTypeFromZendType($returnType);
} }
return self::TYPE_VAR; return Type::VAR;
} }
protected function detectMethodCallReturnType(string $class, string $method): string protected function detectMethodCallReturnType(string $class, string $method): string
@ -3399,7 +3377,7 @@ class CompilerBase implements PropertyAccessContext
if ($returnType) { if ($returnType) {
return $this->getTypeFromZendType($returnType); return $this->getTypeFromZendType($returnType);
} }
return self::TYPE_VAR; return Type::VAR;
} }
protected function genObjvalCall(Expr\FuncCall $expr): string protected function genObjvalCall(Expr\FuncCall $expr): string
@ -3612,7 +3590,7 @@ class CompilerBase implements PropertyAccessContext
return $code; return $code;
} }
protected function checkVar(NodeAbstract $node, string $name, string $defaultType = self::TYPE_VAR): void protected function checkVar(NodeAbstract $node, string $name, string $defaultType = Type::VAR): void
{ {
if (!$this->hasVar($name)) { if (!$this->hasVar($name)) {
$this->addLocalVar($name, $defaultType); $this->addLocalVar($name, $defaultType);
@ -3632,11 +3610,11 @@ class CompilerBase implements PropertyAccessContext
protected function checkVarAssignExpr(NodeAbstract $left, string $toType, string $fromType): bool protected function checkVarAssignExpr(NodeAbstract $left, string $toType, string $fromType): bool
{ {
if ($toType === self::TYPE_VAR or $fromType === self::TYPE_VAR) { if ($toType === Type::VAR or $fromType === Type::VAR) {
return true; return true;
} }
// 引用当前没有类型信息,按照 var 处理 // 引用当前没有类型信息,按照 var 处理
if ($toType === self::TYPE_REF or $fromType === self::TYPE_REF) { if ($toType === Type::REF or $fromType === Type::REF) {
return true; return true;
} }
// 类型一致,可以互相赋值 // 类型一致,可以互相赋值
@ -3648,7 +3626,7 @@ class CompilerBase implements PropertyAccessContext
return true; return true;
} }
// BigInt/BigFloat/Decimal 与原生类型之间可能发生隐式转换,允许重新赋值 // BigInt/BigFloat/Decimal 与原生类型之间可能发生隐式转换,允许重新赋值
$bigTypes = [self::TYPE_BIGINT, self::TYPE_DECIMAL, self::TYPE_BIGFLOAT]; $bigTypes = [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT];
if (in_array($toType, $bigTypes, true) or in_array($fromType, $bigTypes, true)) { if (in_array($toType, $bigTypes, true) or in_array($fromType, $bigTypes, true)) {
return true; return true;
} }
@ -3727,7 +3705,7 @@ class CompilerBase implements PropertyAccessContext
continue; continue;
} }
$code .= $this->getIndent(); $code .= $this->getIndent();
if ($type === self::TYPE_STD_ARRAY) { if ($type === Type::STD_ARRAY) {
$info = $this->context->stdArrays[$name]; $info = $this->context->stdArrays[$name];
if (isset($info['boxExpr'])) { if (isset($info['boxExpr'])) {
$code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');';
@ -3736,7 +3714,7 @@ class CompilerBase implements PropertyAccessContext
$code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL; $code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;'; $code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STD_VECTOR) { } elseif ($type === Type::STD_VECTOR) {
$info = $this->context->stdContainers[$name]; $info = $this->context->stdContainers[$name];
if (isset($info['boxExpr'])) { if (isset($info['boxExpr'])) {
$code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');';
@ -3750,7 +3728,7 @@ class CompilerBase implements PropertyAccessContext
$code .= 'php::Var ' . $name . ' = php::Var(' . $boxCtor . ');' . PHP_EOL; $code .= 'php::Var ' . $name . ' = php::Var(' . $boxCtor . ');' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;'; $code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STD_MAP || $type === self::TYPE_STD_ORDERED_MAP) { } elseif ($type === Type::STD_MAP || $type === Type::STD_ORDERED_MAP) {
$info = $this->context->stdContainers[$name]; $info = $this->context->stdContainers[$name];
if (isset($info['boxExpr'])) { if (isset($info['boxExpr'])) {
$code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');'; $code .= 'auto &' . $name . '_ref = php::toStdContainer<' . $info['decl'] . '>(' . $info['boxExpr'] . ', ' . $info['typeId'] . ');';
@ -3759,11 +3737,11 @@ class CompilerBase implements PropertyAccessContext
$code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL; $code .= 'php::Var ' . $name . ' = php::Var(new ' . $containerType . '(' . $info['typeId'] . '));' . PHP_EOL;
$code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;'; $code .= $this->getIndent() . 'auto &' . $name . '_ref = ' . $name . '.toBox<' . $containerType . '>()->container;';
} }
} elseif ($type === self::TYPE_STREAM || $type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { } elseif ($type === Type::STREAM || $type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$code .= self::TYPE_VAR . ' ' . $name . ';'; $code .= Type::VAR . ' ' . $name . ';';
} else { } else {
$code .= $type . ' ' . $name; $code .= $type . ' ' . $name;
if ($type === self::TYPE_INT or $type === self::TYPE_FLOAT or $type === self::TYPE_BOOL) { if ($type === Type::INT or $type === Type::FLOAT or $type === Type::BOOL) {
$code .= ' = 0'; $code .= ' = 0';
} }
$code .= ';'; $code .= ';';
@ -3788,20 +3766,20 @@ class CompilerBase implements PropertyAccessContext
if ($name === 'GLOBALS') { if ($name === 'GLOBALS') {
continue; continue;
} }
$code .= $this->getIndent() . self::TYPE_VAR . ' &' . $name . ' = ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' &' . $name . ' = ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL;
} }
foreach ($this->context->objectProps as $name => $info) { foreach ($this->context->objectProps as $name => $info) {
if (($info['kind'] ?? 'zval') === 'var') { if (($info['kind'] ?? 'zval') === 'var') {
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $name . ' = ' . $info['getter'] . ';' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' ' . $name . ' = ' . $info['getter'] . ';' . PHP_EOL;
} else { } else {
$zvalMacro = ($info['type'] === self::TYPE_FLOAT) ? 'Z_DVAL_P' : 'Z_LVAL_P'; $zvalMacro = ($info['type'] === Type::FLOAT) ? 'Z_DVAL_P' : 'Z_LVAL_P';
$code .= $this->getIndent() . $info['type'] . ' &' . $name . ' = ' . $zvalMacro . '(' . $info['getter'] . '.unwrap_ptr());' . PHP_EOL; $code .= $this->getIndent() . $info['type'] . ' &' . $name . ' = ' . $zvalMacro . '(' . $info['getter'] . '.unwrap_ptr());' . PHP_EOL;
} }
} }
foreach ($this->context->staticPropRefs as $name => $info) { foreach ($this->context->staticPropRefs as $name => $info) {
$getter = Symbol::getStaticProperty() . '(' . $info['classPtr'] . ', ' . $info['offsetExpr'] . ')'; $getter = Symbol::getStaticProperty() . '(' . $info['classPtr'] . ', ' . $info['offsetExpr'] . ')';
if (($info['kind'] ?? 'zval') === 'var') { if (($info['kind'] ?? 'zval') === 'var') {
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $name . ' = ' . $getter . ';' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' ' . $name . ' = ' . $getter . ';' . PHP_EOL;
} else { } else {
$code .= $this->getIndent() . 'zval *' . $name . ' = ' . $getter . '.unwrap_ptr();' . PHP_EOL; $code .= $this->getIndent() . 'zval *' . $name . ' = ' . $getter . '.unwrap_ptr();' . PHP_EOL;
} }
@ -3812,20 +3790,20 @@ class CompilerBase implements PropertyAccessContext
protected function genReturnCode(): string protected function genReturnCode(): string
{ {
if ($this->functionDef->returnsByRef) { if ($this->functionDef->returnsByRef) {
return $this->getIndent() . 'return ' . self::TYPE_REF . '{};'; return $this->getIndent() . 'return ' . Type::REF . '{};';
} }
if ($this->shouldCheckClosureReturnType()) { if ($this->shouldCheckClosureReturnType()) {
return $this->genClosureCheckedReturn(self::VALUE_NULL); return $this->genClosureCheckedReturn(self::VALUE_NULL);
} }
if ($this->functionDef->returnType === self::TYPE_VOID) { if ($this->functionDef->returnType === Type::VOID) {
return ''; return '';
} }
if ($this->functionDef->returnTypeCheck && !$this->context->inClosure) { if ($this->functionDef->returnTypeCheck && !$this->context->inClosure) {
return $this->genUnionCheckedReturn(self::VALUE_NULL); return $this->genUnionCheckedReturn(self::VALUE_NULL);
} }
if ($this->functionDef->returnType === self::TYPE_INT if ($this->functionDef->returnType === Type::INT
or $this->functionDef->returnType === self::TYPE_FLOAT or $this->functionDef->returnType === Type::FLOAT
or $this->functionDef->returnType === self::TYPE_BOOL) { or $this->functionDef->returnType === Type::BOOL) {
return $this->getIndent() . 'return 0;'; return $this->getIndent() . 'return 0;';
} else { } else {
return $this->getIndent() . 'return ' . self::VALUE_NULL . ';'; return $this->getIndent() . 'return ' . self::VALUE_NULL . ';';

@ -8,6 +8,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
use PhpParser\Node\Identifier; use PhpParser\Node\Identifier;
@ -175,7 +177,7 @@ trait AnonClassGenerator
if ($classDef->hasMethod($methodName)) { if ($classDef->hasMethod($methodName)) {
$functionDef = $classDef->getMethod($methodName)->functionDef; $functionDef = $classDef->getMethod($methodName)->functionDef;
return $functionDef !== null return $functionDef !== null
&& ($functionDef->returnTypeUndeclared || $functionDef->returnType === self::TYPE_VAR); && ($functionDef->returnTypeUndeclared || $functionDef->returnType === Type::VAR);
} }
$className = $classDef->extends; $className = $classDef->extends;
continue; continue;

@ -7,6 +7,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use PhpParser\Modifiers; use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\ArrayItem; use PhpParser\Node\ArrayItem;
@ -110,14 +112,14 @@ trait CallArgumentGenerator
$arg = $variadicArgs[0][1]; $arg = $variadicArgs[0][1];
if ($this->isVarExpr($arg->value)) { if ($this->isVarExpr($arg->value)) {
$var = $this->parseIdentifier($arg->value); $var = $this->parseIdentifier($arg->value);
if ($this->getVarType($var) === self::TYPE_ARRAY) { if ($this->getVarType($var) === Type::ARRAY) {
return $var; return $var;
} }
} }
return $this->convertArrayExpr($this->parseExpr($arg->value)); return $this->convertArrayExpr($this->parseExpr($arg->value));
} }
$tmpVar = $this->addTmpVar(self::TYPE_ARRAY); $tmpVar = $this->addTmpVar(Type::ARRAY);
foreach ($variadicArgs as [$name, $arg]) { foreach ($variadicArgs as [$name, $arg]) {
if ($arg->unpack) { if ($arg->unpack) {
$this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArrayArg($arg) . ');'; $this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArrayArg($arg) . ');';
@ -151,7 +153,7 @@ trait CallArgumentGenerator
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$array = self::TYPE_ARRAY . ' ' . $tmpVar . ';'; $array = Type::ARRAY . ' ' . $tmpVar . ';';
foreach ($namedArgs as $k => $v) { foreach ($namedArgs as $k => $v) {
$array .= $tmpVar . '.set(' . $this->getLiteralString($k) . ', ' . $v . ');' . PHP_EOL; $array .= $tmpVar . '.set(' . $this->getLiteralString($k) . ', ' . $v . ');' . PHP_EOL;
} }
@ -439,7 +441,7 @@ trait CallArgumentGenerator
$globalVar = $this->parseGlobalsArrayDimFetch($arg->value); $globalVar = $this->parseGlobalsArrayDimFetch($arg->value);
// 全局变量作为引用参数 // 全局变量作为引用参数
if ($byRef) { if ($byRef) {
$ref = $this->addTmpVar(self::TYPE_REF); $ref = $this->addTmpVar(Type::REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();'; $this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
$this->addPositionalCallArg('&' . $ref, $arrayArgsVar, $list_args); $this->addPositionalCallArg('&' . $ref, $arrayArgsVar, $list_args);
} else { } else {
@ -477,7 +479,7 @@ trait CallArgumentGenerator
$this->fatalError($arg, 'The constants cannot be used as an argument for a reference-type parameter'); $this->fatalError($arg, 'The constants cannot be used as an argument for a reference-type parameter');
} }
$tmpRef = $this->genTmpVarName(); $tmpRef = $this->genTmpVarName();
$this->addLocalVar($tmpRef, self::TYPE_REF); $this->addLocalVar($tmpRef, Type::REF);
$this->context->beforeStmtLines[] = $tmpRef . ' = ' . $this->parseChainedExpr($arg->value, self::OP_REFVAL) . ';'; $this->context->beforeStmtLines[] = $tmpRef . ' = ' . $this->parseChainedExpr($arg->value, self::OP_REFVAL) . ';';
$this->addPositionalCallArg('&' . $tmpRef, $arrayArgsVar, $list_args); $this->addPositionalCallArg('&' . $tmpRef, $arrayArgsVar, $list_args);
continue; continue;
@ -541,7 +543,7 @@ trait CallArgumentGenerator
{ {
if ($argsVar === null) { if ($argsVar === null) {
$argsVar = $this->genTmpVarName(); $argsVar = $this->genTmpVarName();
$this->context->beforeStmtLines[] = self::TYPE_ARGS . ' ' . $argsVar . '{' . Symbol::argList() . '{' . implode(', ', $listArgs) . '}};'; $this->context->beforeStmtLines[] = Type::ARGS . ' ' . $argsVar . '{' . Symbol::argList() . '{' . implode(', ', $listArgs) . '}};';
$listArgs = []; $listArgs = [];
} }
return $argsVar; return $argsVar;
@ -551,7 +553,7 @@ trait CallArgumentGenerator
{ {
if ($arrayArgsVar === null) { if ($arrayArgsVar === null) {
$arrayArgsVar = $this->genTmpVarName(); $arrayArgsVar = $this->genTmpVarName();
$this->context->beforeStmtLines[] = self::TYPE_ARRAY . ' ' . $arrayArgsVar . '{' . implode(', ', $listArgs) . '};'; $this->context->beforeStmtLines[] = Type::ARRAY . ' ' . $arrayArgsVar . '{' . implode(', ', $listArgs) . '};';
$listArgs = []; $listArgs = [];
} }
return $arrayArgsVar; return $arrayArgsVar;
@ -561,7 +563,7 @@ trait CallArgumentGenerator
{ {
if ($namedArgsVar === null) { if ($namedArgsVar === null) {
$namedArgsVar = $this->genTmpVarName(); $namedArgsVar = $this->genTmpVarName();
$this->context->beforeStmtLines[] = self::TYPE_ARRAY . ' ' . $namedArgsVar . ';'; $this->context->beforeStmtLines[] = Type::ARRAY . ' ' . $namedArgsVar . ';';
$this->context->afterStmtLines[] = $namedArgsVar . '.unset();'; $this->context->afterStmtLines[] = $namedArgsVar . '.unset();';
} }
return $namedArgsVar; return $namedArgsVar;
@ -617,7 +619,7 @@ trait CallArgumentGenerator
$array = $this->parseIdentifier($arg->value->var); $array = $this->parseIdentifier($arg->value->var);
if ($array === 'GLOBALS') { if ($array === 'GLOBALS') {
$globalVar = $this->parseGlobalsArrayDimFetch($arg->value); $globalVar = $this->parseGlobalsArrayDimFetch($arg->value);
$ref = $this->addTmpVar(self::TYPE_REF); $ref = $this->addTmpVar(Type::REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();'; $this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
return '&' . $ref; return '&' . $ref;
} }
@ -635,7 +637,7 @@ trait CallArgumentGenerator
} }
$tmpRef = $this->genTmpVarName(); $tmpRef = $this->genTmpVarName();
$this->addLocalVar($tmpRef, self::TYPE_REF); $this->addLocalVar($tmpRef, Type::REF);
$this->context->beforeStmtLines[] = $tmpRef . ' = ' . $this->parseChainedExpr($arg->value, self::OP_REFVAL) . ';'; $this->context->beforeStmtLines[] = $tmpRef . ' = ' . $this->parseChainedExpr($arg->value, self::OP_REFVAL) . ';';
return '&' . $tmpRef; return '&' . $tmpRef;
} }
@ -684,7 +686,7 @@ trait CallArgumentGenerator
$array = $this->parseIdentifier($inner->var); $array = $this->parseIdentifier($inner->var);
if ($array === 'GLOBALS') { if ($array === 'GLOBALS') {
$globalVar = $this->parseGlobalsArrayDimFetch($inner); $globalVar = $this->parseGlobalsArrayDimFetch($inner);
$ref = $this->addTmpVar(self::TYPE_REF); $ref = $this->addTmpVar(Type::REF);
$this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();'; $this->context->beforeStmtLines[] = $ref . ' = ' . $globalVar . '.toReference();';
return '&' . $ref; return '&' . $ref;
} }
@ -706,15 +708,15 @@ trait CallArgumentGenerator
{ {
if (!$this->hasVar($name)) { if (!$this->hasVar($name)) {
// 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用 // 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用
$this->addLocalVar($name, self::TYPE_REF); $this->addLocalVar($name, Type::REF);
} else { } else {
// 本地变量,且是原生类型,则转为普通变量 // 本地变量,且是原生类型,则转为普通变量
if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) { if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) {
$this->context->localVars[$name] = self::TYPE_VAR; $this->context->localVars[$name] = Type::VAR;
} }
// 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_REF); $this->addLocalVar($tmpVar, Type::REF);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();';
$name = $tmpVar; $name = $tmpVar;
} }
@ -771,7 +773,7 @@ trait CallArgumentGenerator
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
$this->errorUndefinedVariable($value); $this->errorUndefinedVariable($value);
} }
if ($this->getVarType($var) === self::TYPE_ARRAY) { if ($this->getVarType($var) === Type::ARRAY) {
return $var; return $var;
} }
} }

@ -8,6 +8,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use TypePhp\Entity\ArgInfo; use TypePhp\Entity\ArgInfo;
use TypePhp\Context\FunctionContext; use TypePhp\Context\FunctionContext;
use PhpParser\Node; use PhpParser\Node;
@ -80,9 +82,9 @@ trait ClosureGenerator
$code = $this->getIndent() . $code = $this->getIndent() .
'php::ClosureFn ' . $tmpVar . ' = [](' 'php::ClosureFn ' . $tmpVar . ' = []('
. 'INTERNAL_FUNCTION_PARAMETERS, ' . 'INTERNAL_FUNCTION_PARAMETERS, '
. self::TYPE_OBJECT . ' &this_, ' . Type::OBJECT . ' &this_, '
. self::TYPE_ARGS . ' &vars_) ' . . Type::ARGS . ' &vars_) ' .
'-> ' . self::TYPE_VAR . ' {' . PHP_EOL; '-> ' . Type::VAR . ' {' . PHP_EOL;
$oriContext = $this->context; $oriContext = $this->context;
$this->context = new FunctionContext(); $this->context = new FunctionContext();
@ -125,14 +127,14 @@ trait ClosureGenerator
$var = $this->parseIdentifier($param->var); $var = $this->parseIdentifier($param->var);
$phpName = is_string($param->var->name) ? $param->var->name : $this->unescapeVarName($var); $phpName = is_string($param->var->name) ? $param->var->name : $this->unescapeVarName($var);
if ($param->variadic) { if ($param->variadic) {
$code .= $this->getIndent() . self::TYPE_ARRAY . ' ' . $var . ';' . PHP_EOL; $code .= $this->getIndent() . Type::ARRAY . ' ' . $var . ';' . PHP_EOL;
$code .= $this->getIndent() . 'for (uint32_t i = ' . $i . '; i < php::getCallArgNum(); i++) {' . PHP_EOL; $code .= $this->getIndent() . 'for (uint32_t i = ' . $i . '; i < php::getCallArgNum(); i++) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL; $code .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL;
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->genExtraNamedVariadicArgs($var); $code .= $this->genExtraNamedVariadicArgs($var);
$this->addArgument($var, self::TYPE_ARRAY); $this->addArgument($var, Type::ARRAY);
$code .= $this->genClosureParamTypeCheck($param, $var, $phpName, $i, true); $code .= $this->genClosureParamTypeCheck($param, $var, $phpName, $i, true);
continue; continue;
} }
@ -140,18 +142,18 @@ trait ClosureGenerator
? 'php::getCallArg(' . $i . ')' ? 'php::getCallArg(' . $i . ')'
: 'php::getCallArg(' . $i . ', ' . $this->parseParamDefaultValue($param->default) . ')'; : 'php::getCallArg(' . $i . ', ' . $this->parseParamDefaultValue($param->default) . ')';
$code .= $this->getIndent() . 'auto ' . $var . ' = ' . $argExpr . ';' . PHP_EOL; $code .= $this->getIndent() . 'auto ' . $var . ' = ' . $argExpr . ';' . PHP_EOL;
$this->addArgument($var, self::TYPE_VAR); $this->addArgument($var, Type::VAR);
$code .= $this->genClosureParamTypeCheck($param, $var, $phpName, $i, false); $code .= $this->genClosureParamTypeCheck($param, $var, $phpName, $i, false);
} }
foreach ($uses as $i => $useItem) { foreach ($uses as $i => $useItem) {
$var = $this->parseIdentifier($useItem->var); $var = $this->parseIdentifier($useItem->var);
$code .= 'auto ' . $var . ' = vars_.get(' . $i . ');' . PHP_EOL; $code .= 'auto ' . $var . ' = vars_.get(' . $i . ');' . PHP_EOL;
$this->addArgument($var, self::TYPE_VAR); $this->addArgument($var, Type::VAR);
} }
if ($this->methodDef) { if ($this->methodDef) {
$this->addArgument('this_', self::TYPE_OBJECT); $this->addArgument('this_', Type::OBJECT);
} }
$body = $this->genClosureBody($expr); $body = $this->genClosureBody($expr);
@ -171,7 +173,7 @@ trait ClosureGenerator
if ($useItem->byRef) { if ($useItem->byRef) {
// 闭包的 use 语法,若为引用类型,可以就地创建变量 // 闭包的 use 语法,若为引用类型,可以就地创建变量
if (!isset($oriContext->localVars[$var])) { if (!isset($oriContext->localVars[$var])) {
$oriContext->localVars[$var] = self::TYPE_REF; $oriContext->localVars[$var] = Type::REF;
} }
$useVars[] = $this->convertToRef($useItem->var); $useVars[] = $this->convertToRef($useItem->var);
} else { } else {
@ -221,11 +223,11 @@ trait ClosureGenerator
} }
if ($this->isCallExpr($expr->expr)) { if ($this->isCallExpr($expr->expr)) {
$nativeCall = $expr->expr->getAttribute('nativeCall'); $nativeCall = $expr->expr->getAttribute('nativeCall');
if ($nativeCall and $this->getFunction($nativeCall)->returnType === self::TYPE_VOID) { if ($nativeCall and $this->getFunction($nativeCall)->returnType === Type::VOID) {
return $this->genArrowFunctionVoidReturn($beforeCode, $code); return $this->genArrowFunctionVoidReturn($beforeCode, $code);
} }
} }
if ($this->detectTypeOfExpr($expr->expr) === self::TYPE_VOID) { if ($this->detectTypeOfExpr($expr->expr) === Type::VOID) {
return $this->genArrowFunctionVoidReturn($beforeCode, $code); return $this->genArrowFunctionVoidReturn($beforeCode, $code);
} }
return $beforeCode . PHP_EOL . $this->genClosureReturnValue($code); return $beforeCode . PHP_EOL . $this->genClosureReturnValue($code);
@ -260,7 +262,7 @@ trait ClosureGenerator
$argInfo = new ArgInfo(); $argInfo = new ArgInfo();
$argInfo->name = $var; $argInfo->name = $var;
$argInfo->phpName = $phpName; $argInfo->phpName = $phpName;
$argInfo->type = self::TYPE_VAR; $argInfo->type = Type::VAR;
$argInfo->variadic = $variadic; $argInfo->variadic = $variadic;
$argInfo->typeCheck = $typeInfo['check']; $argInfo->typeCheck = $typeInfo['check'];
$argInfo->typeStr = $typeInfo['typeStr']; $argInfo->typeStr = $typeInfo['typeStr'];

@ -7,6 +7,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use TypePhp\Entity\ArgInfo; use TypePhp\Entity\ArgInfo;
use TypePhp\Entity\ArrayInitPlan; use TypePhp\Entity\ArrayInitPlan;
use TypePhp\Entity\FunctionDef; use TypePhp\Entity\FunctionDef;
@ -16,8 +18,8 @@ trait DefaultArgumentGenerator
protected function getDefaultArgumentType(ArgInfo $argInfo): string protected function getDefaultArgumentType(ArgInfo $argInfo): string
{ {
$type = $argInfo->type; $type = $argInfo->type;
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) { if ($type === Type::STREAM || $type === Type::BOX) {
return self::TYPE_VAR; return Type::VAR;
} }
return $type; return $type;
} }

@ -8,6 +8,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr\Yield_; use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Expr\YieldFrom; use PhpParser\Node\Expr\YieldFrom;
@ -71,7 +73,7 @@ trait FiberGenerator
$this->fatalError($v, 'Generator return type must accept TypePHP\\FiberGenerator; use Iterator, Traversable, iterable, object, mixed, or omit the return type'); $this->fatalError($v, 'Generator return type must accept TypePHP\\FiberGenerator; use Iterator, Traversable, iterable, object, mixed, or omit the return type');
} }
$functionDef->generator = true; $functionDef->generator = true;
$functionDef->returnType = self::TYPE_VAR; $functionDef->returnType = Type::VAR;
$functionDef->returnClass = ''; $functionDef->returnClass = '';
$functionDef->returnTypeCheck = null; $functionDef->returnTypeCheck = null;
$functionDef->returnTypeStr = ''; $functionDef->returnTypeStr = '';
@ -126,7 +128,7 @@ trait FiberGenerator
{ {
$payload = $this->genYieldPayload($expr); $payload = $this->genYieldPayload($expr);
$closed = $this->genTmpVarName(); $closed = $this->genTmpVarName();
$this->addLocalVar($closed, self::TYPE_BOOL); $this->addLocalVar($closed, Type::BOOL);
return $closed . ' = false;' . PHP_EOL return $closed . ' = false;' . PHP_EOL
. $this->getIndent() . $closed . ' = typephp_fiber_yield(' . $payload . ');' . PHP_EOL . $this->getIndent() . $closed . ' = typephp_fiber_yield(' . $payload . ');' . PHP_EOL
. $this->getIndent() . 'if (' . $closed . ') {' . PHP_EOL . $this->getIndent() . 'if (' . $closed . ') {' . PHP_EOL
@ -137,7 +139,7 @@ trait FiberGenerator
protected function parseYieldFromStmt(YieldFrom $expr): string protected function parseYieldFromStmt(YieldFrom $expr): string
{ {
$closed = $this->genTmpVarName(); $closed = $this->genTmpVarName();
$this->addLocalVar($closed, self::TYPE_BOOL); $this->addLocalVar($closed, Type::BOOL);
return $closed . ' = false;' . PHP_EOL return $closed . ' = false;' . PHP_EOL
. $this->getIndent() . 'typephp_fiber_yield_from(' . $this->parseExprAsValue($expr->expr) . ', &' . $closed . ');' . PHP_EOL . $this->getIndent() . 'typephp_fiber_yield_from(' . $this->parseExprAsValue($expr->expr) . ', &' . $closed . ');' . PHP_EOL
. $this->getIndent() . 'if (' . $closed . ') {' . PHP_EOL . $this->getIndent() . 'if (' . $closed . ') {' . PHP_EOL
@ -180,9 +182,9 @@ trait FiberGenerator
private function doGenFiberGeneratorFunction(Function_|ClassMethod $v, FunctionDef $functionDef, string $nativeName): string private function doGenFiberGeneratorFunction(Function_|ClassMethod $v, FunctionDef $functionDef, string $nativeName): string
{ {
$functionDeclCode = self::TYPE_VAR . ' ' . self::PREFIX . $nativeName . '('; $functionDeclCode = Type::VAR . ' ' . self::PREFIX . $nativeName . '(';
if ($this->class) { if ($this->class) {
$functionDeclCode .= self::TYPE_OBJECT . ' &this_'; $functionDeclCode .= Type::OBJECT . ' &this_';
if ($functionDef->params) { if ($functionDef->params) {
$functionDeclCode .= ', '; $functionDeclCode .= ', ';
} }
@ -210,8 +212,8 @@ trait FiberGenerator
$closureVar = $this->genTmpVarName(); $closureVar = $this->genTmpVarName();
$code .= $this->getIndent() . 'php::ClosureFn ' . $closureVar . ' = [](' $code .= $this->getIndent() . 'php::ClosureFn ' . $closureVar . ' = []('
. 'INTERNAL_FUNCTION_PARAMETERS, ' . 'INTERNAL_FUNCTION_PARAMETERS, '
. self::TYPE_OBJECT . ' &this_, ' . Type::OBJECT . ' &this_, '
. self::TYPE_ARGS . ' &vars_) -> ' . self::TYPE_VAR . ' {' . PHP_EOL; . Type::ARGS . ' &vars_) -> ' . Type::VAR . ' {' . PHP_EOL;
$outerContext = $this->context; $outerContext = $this->context;
$outerIndent = $this->indentLevel; $outerIndent = $this->indentLevel;
@ -222,11 +224,11 @@ trait FiberGenerator
$this->indentLevel++; $this->indentLevel++;
foreach ($functionDef->argInfoList as $i => $argInfo) { foreach ($functionDef->argInfoList as $i => $argInfo) {
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $argInfo->name . ' = vars_.get(' . $i . ');' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' ' . $argInfo->name . ' = vars_.get(' . $i . ');' . PHP_EOL;
$this->addArgument($argInfo->name, self::TYPE_VAR); $this->addArgument($argInfo->name, Type::VAR);
} }
if ($this->class) { if ($this->class) {
$this->addArgument('this_', self::TYPE_OBJECT); $this->addArgument('this_', Type::OBJECT);
} }
$body = ''; $body = '';

@ -8,6 +8,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\Type;
use TypePhp\Entity\ArgInfo; use TypePhp\Entity\ArgInfo;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\IntersectionType; use PhpParser\Node\IntersectionType;
@ -284,7 +286,7 @@ trait TypeCheckGenerator
$code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL; $code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $valueVar . ' = ' . $iterVar . '.value();' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' ' . $valueVar . ' = ' . $iterVar . '.value();' . PHP_EOL;
if ($this->compositeTypeNeedsIntToFloatCoercion($argInfo->typeCheck)) { if ($this->compositeTypeNeedsIntToFloatCoercion($argInfo->typeCheck)) {
$code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL; $code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
@ -293,7 +295,7 @@ trait TypeCheckGenerator
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
} }
$code .= $this->getIndent() . self::TYPE_INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL; $code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL;
@ -411,7 +413,7 @@ trait TypeCheckGenerator
$code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL; $code = $this->getIndent() . 'for (auto ' . $iterVar . ' = ' . $argInfo->name . '.begin(); ' . $iterVar . ' != ' . $argInfo->name . '.end(); ++' . $iterVar . ') {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $valueVar . ' = ' . $iterVar . '.value();' . PHP_EOL; $code .= $this->getIndent() . Type::VAR . ' ' . $valueVar . ' = ' . $iterVar . '.value();' . PHP_EOL;
if ($this->compositeTypeNeedsIntToFloatCoercion($argInfo->typeCheck)) { if ($this->compositeTypeNeedsIntToFloatCoercion($argInfo->typeCheck)) {
$code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL; $code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
@ -420,7 +422,7 @@ trait TypeCheckGenerator
$this->indentLevel--; $this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
} }
$code .= $this->getIndent() . self::TYPE_INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL; $code .= $this->getIndent() . Type::INT . ' ' . $argNoVar . ' = ' . ($argIndex + 1) . ' + ' . $iterVar . '.index();' . PHP_EOL;
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL; $code .= $this->getIndent() . 'if (UNEXPECTED(!(' . $orExpr . '))) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL; $code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL;

@ -8,7 +8,8 @@
namespace TypePhp\Generator; namespace TypePhp\Generator;
use TypePhp\CompilerBase; use TypePhp\Type;
use TypePhp\Metadata\Constants; use TypePhp\Metadata\Constants;
trait Utils trait Utils
@ -54,7 +55,7 @@ trait Utils
protected function genArray(array $elements): string protected function genArray(array $elements): string
{ {
return CompilerBase::TYPE_ARRAY . '{' . implode(', ', $elements) . ' }'; return Type::ARRAY . '{' . implode(', ', $elements) . ' }';
} }
protected function genRawStr(string $str): string protected function genRawStr(string $str): string

@ -8,6 +8,8 @@
namespace TypePhp\Optimizer; namespace TypePhp\Optimizer;
use TypePhp\Type;
use TypePhp\Resolver\Reflection; use TypePhp\Resolver\Reflection;
use PhpParser\Node; use PhpParser\Node;
@ -126,28 +128,28 @@ trait FuncCallOptimizer
// Big* dispatch // Big* dispatch
'abs' => ['bigDispatch' => [ 'abs' => ['bigDispatch' => [
self::TYPE_BIGINT => 'php::BigInt::abs', Type::BIGINT => 'php::BigInt::abs',
self::TYPE_BIGFLOAT => 'php::BigFloat::abs', Type::BIGFLOAT => 'php::BigFloat::abs',
self::TYPE_DECIMAL => 'php::Decimal::abs', Type::DECIMAL => 'php::Decimal::abs',
'fallback' => 'php::fn::abs', 'fallback' => 'php::fn::abs',
]], ]],
'pow' => ['bigDispatch' => [ 'pow' => ['bigDispatch' => [
self::TYPE_BIGINT => 'php::BigInt::pow', Type::BIGINT => 'php::BigInt::pow',
self::TYPE_DECIMAL => 'php::Decimal::pow', Type::DECIMAL => 'php::Decimal::pow',
'fallback' => 'php::fn::pow', 'fallback' => 'php::fn::pow',
]], ]],
'sqrt' => ['bigDispatch' => [ 'sqrt' => ['bigDispatch' => [
self::TYPE_BIGINT => 'php::BigInt::sqrt', Type::BIGINT => 'php::BigInt::sqrt',
self::TYPE_DECIMAL => 'php::Decimal::sqrt', Type::DECIMAL => 'php::Decimal::sqrt',
self::TYPE_BIGFLOAT => 'php::BigFloat::sqrt', Type::BIGFLOAT => 'php::BigFloat::sqrt',
'fallback' => 'php::fn::sqrt', 'fallback' => 'php::fn::sqrt',
]], ]],
'floor' => ['bigDispatch' => [ 'floor' => ['bigDispatch' => [
self::TYPE_DECIMAL => 'php::Decimal::floor', Type::DECIMAL => 'php::Decimal::floor',
'fallback' => 'php::fn::floor', 'fallback' => 'php::fn::floor',
]], ]],
'ceil' => ['bigDispatch' => [ 'ceil' => ['bigDispatch' => [
self::TYPE_DECIMAL => 'php::Decimal::ceil', Type::DECIMAL => 'php::Decimal::ceil',
'fallback' => 'php::fn::ceil', 'fallback' => 'php::fn::ceil',
]], ]],
@ -158,9 +160,9 @@ trait FuncCallOptimizer
'boolval' => ['conversion' => self::ARG_TYPE_BOOL], 'boolval' => ['conversion' => self::ARG_TYPE_BOOL],
// SSA compile-time type checks // SSA compile-time type checks
'is_int' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => self::TYPE_INT], 'is_int' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => Type::INT],
'is_float' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => self::TYPE_FLOAT], 'is_float' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => Type::FLOAT],
'is_bool' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => self::TYPE_BOOL], 'is_bool' => ['constFold' => self::FOLD_SSA_TYPE, 'constFoldExtra' => Type::BOOL],
// Custom handlers // Custom handlers
'is_null' => ['handler' => 'genIsNull'], 'is_null' => ['handler' => 'genIsNull'],
@ -474,9 +476,9 @@ trait FuncCallOptimizer
if ($convType === self::ARG_TYPE_STR) { if ($convType === self::ARG_TYPE_STR) {
return match ($type) { return match ($type) {
self::TYPE_BIGINT => 'php::BigInt::toString(' . $parsed . ')', Type::BIGINT => 'php::BigInt::toString(' . $parsed . ')',
self::TYPE_BIGFLOAT => 'php::BigFloat::toString(' . $parsed . ')', Type::BIGFLOAT => 'php::BigFloat::toString(' . $parsed . ')',
self::TYPE_DECIMAL => 'php::Decimal::toString(' . $parsed . ')', Type::DECIMAL => 'php::Decimal::toString(' . $parsed . ')',
default => $this->convertStringExpr($parsed), default => $this->convertStringExpr($parsed),
}; };
} }
@ -692,7 +694,7 @@ trait FuncCallOptimizer
protected function genRound(string $n, Node\Expr\FuncCall $e, array $c): string protected function genRound(string $n, Node\Expr\FuncCall $e, array $c): string
{ {
$type = $this->detectTypeOfExpr($e->args[0]->value); $type = $this->detectTypeOfExpr($e->args[0]->value);
if ($type === self::TYPE_DECIMAL) { if ($type === Type::DECIMAL) {
$a0 = $this->parseExpr($e->args[0]->value); $a0 = $this->parseExpr($e->args[0]->value);
if (count($e->args) >= 2) { if (count($e->args) >= 2) {
return 'php::Decimal::round(' . $a0 . ', ' . $this->parseExpr($e->args[1]->value) . ')'; return 'php::Decimal::round(' . $a0 . ', ' . $this->parseExpr($e->args[1]->value) . ')';
@ -743,7 +745,7 @@ trait FuncCallOptimizer
$list = []; $list = [];
foreach ($funcDef->argInfoList as $i => $argInfo) { foreach ($funcDef->argInfoList as $i => $argInfo) {
if ($argInfo->variadic) { if ($argInfo->variadic) {
$tmpVar = $this->addTmpVar(self::TYPE_ARRAY); $tmpVar = $this->addTmpVar(Type::ARRAY);
$this->context->beforeStmtLines[] = $this->genArray($list) . ';'; $this->context->beforeStmtLines[] = $this->genArray($list) . ';';
$this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $argInfo->name . ');'; $this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $argInfo->name . ');';
return $tmpVar; return $tmpVar;

@ -10,6 +10,8 @@
namespace TypePhp\Optimizer; namespace TypePhp\Optimizer;
use TypePhp\Type;
use TypePhp\Analysis\SsaBuilder; use TypePhp\Analysis\SsaBuilder;
use TypePhp\Analysis\SsaFlags; use TypePhp\Analysis\SsaFlags;
use PhpParser\Node; use PhpParser\Node;
@ -62,7 +64,7 @@ trait LoopVarOptimizer
if ($depFailed) { if ($depFailed) {
continue; continue;
} }
$this->context->localVars[$escapedName] = self::TYPE_INT; $this->context->localVars[$escapedName] = Type::INT;
} }
} }
@ -386,7 +388,7 @@ trait LoopVarOptimizer
return false; return false;
} }
return $this->detectTypeOfExpr($expr) === self::TYPE_INT; return $this->detectTypeOfExpr($expr) === Type::INT;
} }
protected function isLoopKnownNonNegativeIntCall(NodeAbstract $expr): bool protected function isLoopKnownNonNegativeIntCall(NodeAbstract $expr): bool

@ -24,6 +24,8 @@
namespace TypePhp\Optimizer; namespace TypePhp\Optimizer;
use TypePhp\Type;
use TypePhp\Analysis\SsaBuilder; use TypePhp\Analysis\SsaBuilder;
use TypePhp\Analysis\SsaFlags; use TypePhp\Analysis\SsaFlags;
use TypePhp\Resolver\Reflection; use TypePhp\Resolver\Reflection;
@ -779,18 +781,18 @@ trait SsaPropOptimizer
*/ */
protected function getHoistedObjectPropInfo(string $declaredType): array protected function getHoistedObjectPropInfo(string $declaredType): array
{ {
if ($declaredType === self::TYPE_INT || $declaredType === self::TYPE_FLOAT) { if ($declaredType === Type::INT || $declaredType === Type::FLOAT) {
return ['type' => $declaredType, 'kind' => 'zval']; return ['type' => $declaredType, 'kind' => 'zval'];
} }
return ['type' => self::TYPE_VAR, 'kind' => 'var']; return ['type' => Type::VAR, 'kind' => 'var'];
} }
protected function getZvalValueMacroForPropType(string $type): ?string protected function getZvalValueMacroForPropType(string $type): ?string
{ {
return match ($type) { return match ($type) {
self::TYPE_INT => 'Z_LVAL_P', Type::INT => 'Z_LVAL_P',
self::TYPE_FLOAT => 'Z_DVAL_P', Type::FLOAT => 'Z_DVAL_P',
default => null, default => null,
}; };
} }
@ -825,7 +827,7 @@ trait SsaPropOptimizer
if ($zvalMacro !== null) { if ($zvalMacro !== null) {
$this->context->beforeStmtLines[] = $cType . ' &' . $propVar . ' = ' . $zvalMacro . '(' . $refGetter . '.unwrap_ptr());'; $this->context->beforeStmtLines[] = $cType . ' &' . $propVar . ' = ' . $zvalMacro . '(' . $refGetter . '.unwrap_ptr());';
} else { } else {
$this->context->beforeStmtLines[] = self::TYPE_VAR . ' ' . $propVar . ' = ' . $refGetter . ';'; $this->context->beforeStmtLines[] = Type::VAR . ' ' . $propVar . ' = ' . $refGetter . ';';
} }
$this->context->hoistedProps[$objName][$propName] = true; $this->context->hoistedProps[$objName][$propName] = true;

@ -9,6 +9,8 @@
namespace TypePhp\Optimizer; namespace TypePhp\Optimizer;
use TypePhp\Type;
use TypePhp\Analysis\SsaBuilder; use TypePhp\Analysis\SsaBuilder;
use TypePhp\Analysis\SsaFlags; use TypePhp\Analysis\SsaFlags;
use TypePhp\Analysis\SsaVar; use TypePhp\Analysis\SsaVar;
@ -78,8 +80,8 @@ trait SsaTypeOptimizer
} }
$narrowableTypes = [ $narrowableTypes = [
self::TYPE_INT => true, Type::INT => true,
self::TYPE_FLOAT => true, Type::FLOAT => true,
]; ];
// Group SSA vars by original variable name // Group SSA vars by original variable name
@ -100,7 +102,7 @@ trait SsaTypeOptimizer
foreach (array_keys($groups) as $name) { foreach (array_keys($groups) as $name) {
$varName = $this->escapeVarName($name); $varName = $this->escapeVarName($name);
if (!isset($this->context->arguments[$varName]) && !$this->hasVar($varName)) { if (!isset($this->context->arguments[$varName]) && !$this->hasVar($varName)) {
$this->context->localVars[$varName] = self::TYPE_VAR; $this->context->localVars[$varName] = Type::VAR;
} }
} }
@ -141,7 +143,7 @@ trait SsaTypeOptimizer
$nonNarrowableType = $defType; $nonNarrowableType = $defType;
} elseif ($nonNarrowableType !== $defType) { } elseif ($nonNarrowableType !== $defType) {
// Mixed non-narrowable types — can't determine a single type // Mixed non-narrowable types — can't determine a single type
$nonNarrowableType = self::TYPE_VAR; $nonNarrowableType = Type::VAR;
} }
continue; continue;
} }
@ -160,7 +162,7 @@ trait SsaTypeOptimizer
// Mixed narrowable and non-narrowable types (e.g. $x = [1,2] then $x = 42) // Mixed narrowable and non-narrowable types (e.g. $x = [1,2] then $x = 42)
// — can't safely narrow. // — can't safely narrow.
if ($narrowedType !== null && $nonNarrowableType !== null && $nonNarrowableType !== self::TYPE_VAR) { if ($narrowedType !== null && $nonNarrowableType !== null && $nonNarrowableType !== Type::VAR) {
continue; continue;
} }
@ -169,7 +171,7 @@ trait SsaTypeOptimizer
// SSA variables can resolve them (these types have no extra metadata). // SSA variables can resolve them (these types have no extra metadata).
if ( if (
$nonNarrowableType !== null $nonNarrowableType !== null
&& in_array($nonNarrowableType, [self::TYPE_BIGINT, self::TYPE_DECIMAL, self::TYPE_BIGFLOAT, self::TYPE_STREAM], true) && in_array($nonNarrowableType, [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT, Type::STREAM], true)
) { ) {
$this->context->localVars[$varName] = $nonNarrowableType; $this->context->localVars[$varName] = $nonNarrowableType;
} }
@ -179,10 +181,10 @@ trait SsaTypeOptimizer
// Scan for operations that SSA definition types alone can't detect // Scan for operations that SSA definition types alone can't detect
$functionStmts = $ssa->getStmts(); $functionStmts = $ssa->getStmts();
if ($functionStmts) { if ($functionStmts) {
if ($narrowedType === self::TYPE_INT && $this->hasDangerousIntOps($varName, $functionStmts)) { if ($narrowedType === Type::INT && $this->hasDangerousIntOps($varName, $functionStmts)) {
continue; continue;
} }
if ($narrowedType === self::TYPE_FLOAT && $this->hasDangerousFloatOps($varName, $functionStmts)) { if ($narrowedType === Type::FLOAT && $this->hasDangerousFloatOps($varName, $functionStmts)) {
continue; continue;
} }
} }
@ -211,7 +213,7 @@ trait SsaTypeOptimizer
if ($def instanceof Node\Stmt\Expression && $def->expr instanceof Node\Expr\Assign) { if ($def instanceof Node\Stmt\Expression && $def->expr instanceof Node\Expr\Assign) {
$expr = $def->expr->expr; $expr = $def->expr->expr;
$type = $this->detectTypeOfExpr($expr); $type = $this->detectTypeOfExpr($expr);
if ($type === self::TYPE_INT && !$this->isSafeSsaIntExpr($expr)) { if ($type === Type::INT && !$this->isSafeSsaIntExpr($expr)) {
return null; return null;
} }
return $type; return $type;
@ -232,7 +234,7 @@ trait SsaTypeOptimizer
} }
if ($def instanceof Node\Stmt\Catch_) { if ($def instanceof Node\Stmt\Catch_) {
return self::TYPE_OBJECT; return Type::OBJECT;
} }
if ($def instanceof Node\Stmt\Static_) { if ($def instanceof Node\Stmt\Static_) {
@ -250,11 +252,11 @@ trait SsaTypeOptimizer
protected function detectAssignOpDefType(Node\Expr\AssignOp $expr): ?string protected function detectAssignOpDefType(Node\Expr\AssignOp $expr): ?string
{ {
if ($expr instanceof Node\Expr\AssignOp\Div) { if ($expr instanceof Node\Expr\AssignOp\Div) {
return self::TYPE_FLOAT; return Type::FLOAT;
} }
if ($expr instanceof Node\Expr\AssignOp\Concat) { if ($expr instanceof Node\Expr\AssignOp\Concat) {
return self::TYPE_STR; return Type::STR;
} }
if ($expr instanceof Node\Expr\AssignOp\Pow) { if ($expr instanceof Node\Expr\AssignOp\Pow) {
@ -265,10 +267,10 @@ trait SsaTypeOptimizer
|| $expr instanceof Node\Expr\AssignOp\Minus || $expr instanceof Node\Expr\AssignOp\Minus
|| $expr instanceof Node\Expr\AssignOp\Mul) { || $expr instanceof Node\Expr\AssignOp\Mul) {
$rhsType = $this->detectTypeOfExpr($expr->expr); $rhsType = $this->detectTypeOfExpr($expr->expr);
if ($rhsType === self::TYPE_FLOAT) { if ($rhsType === Type::FLOAT) {
return self::TYPE_FLOAT; return Type::FLOAT;
} }
if ($rhsType === self::TYPE_INT) { if ($rhsType === Type::INT) {
return null; return null;
} }
return $rhsType; return $rhsType;
@ -292,7 +294,7 @@ trait SsaTypeOptimizer
} }
if ($expr instanceof Node\Expr\ConstFetch) { if ($expr instanceof Node\Expr\ConstFetch) {
return $this->detectConstType($expr) === self::TYPE_INT; return $this->detectConstType($expr) === Type::INT;
} }
if ($expr instanceof Node\Expr\BitwiseNot) { if ($expr instanceof Node\Expr\BitwiseNot) {

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -18,7 +20,7 @@ trait ArrayExpressionTrait
$items = $node->items; $items = $node->items;
// 优化代码风格,空数组直接返回{},否则会产生一些空洞内容 // 优化代码风格,空数组直接返回{},否则会产生一些空洞内容
if (count($items) === 0) { if (count($items) === 0) {
return self::TYPE_ARRAY . '{}'; return Type::ARRAY . '{}';
} }
$hasKey = false; $hasKey = false;
@ -58,14 +60,14 @@ trait ArrayExpressionTrait
if ($item->key) { if ($item->key) {
$this->assertExprCanBeUsedAsValue($item->key, 'array key'); $this->assertExprCanBeUsedAsValue($item->key, 'array key');
$key = $this->parseArrayKey($item->key); $key = $this->parseArrayKey($item->key);
$list[] = $this->getIndent() . '{ ' . $key . ', ' . self::TYPE_VAR . '(' . $value . ') }'; $list[] = $this->getIndent() . '{ ' . $key . ', ' . Type::VAR . '(' . $value . ') }';
} else { } else {
$list[] = $this->getIndent() . self::TYPE_VAR . '(' . $value . ')'; $list[] = $this->getIndent() . Type::VAR . '(' . $value . ')';
} }
} }
$this->indentLevel--; $this->indentLevel--;
return self::TYPE_ARRAY . '{' . PHP_EOL . return Type::ARRAY . '{' . PHP_EOL .
implode(', ' . PHP_EOL, $list) . PHP_EOL . implode(', ' . PHP_EOL, $list) . PHP_EOL .
$this->getIndent() . $this->getIndent() .
'}'; '}';
@ -83,10 +85,10 @@ trait ArrayExpressionTrait
if ($this->isScalarString($node->dim)) { if ($this->isScalarString($node->dim)) {
$name = $node->dim->value; $name = $node->dim->value;
if (!$this->hasGlobalVar($name)) { if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, self::TYPE_VAR); $this->addGlobalVar($name, Type::VAR);
} }
if (!$this->hasScopeGlobalVar($name)) { if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, self::TYPE_VAR); $this->addScopeGlobalVar($name, Type::VAR);
} }
return $name; return $name;
} }
@ -170,17 +172,17 @@ trait ArrayExpressionTrait
} }
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
if ($write) { if ($write) {
$this->addLocalVar($var, self::TYPE_ARRAY); $this->addLocalVar($var, Type::ARRAY);
} else { } else {
$this->errorUndefinedVariable($node->var); $this->errorUndefinedVariable($node->var);
} }
} else { } else {
$type = $this->getVarType($var); $type = $this->getVarType($var);
if ($type === self::TYPE_BOOL || $type === self::TYPE_INT || $type === self::TYPE_FLOAT) { if ($type === Type::BOOL || $type === Type::INT || $type === Type::FLOAT) {
$this->fatalError($node, 'Cannot use [] for numbers'); $this->fatalError($node, 'Cannot use [] for numbers');
} }
} }
if ($this->getVarType($var) === self::TYPE_STR) { if ($this->getVarType($var) === Type::STR) {
if ($node->dim === null) { if ($node->dim === null) {
$this->fatalError($node, 'Cannot use [] for strings'); $this->fatalError($node, 'Cannot use [] for strings');
} }
@ -206,7 +208,7 @@ trait ArrayExpressionTrait
private function parseArrayMixed(Expr\Array_ $node): string private function parseArrayMixed(Expr\Array_ $node): string
{ {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_ARRAY); $this->addLocalVar($tmpVar, Type::ARRAY);
// 释放临时变量,避免修改数组产生数组复制操作 // 释放临时变量,避免修改数组产生数组复制操作
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.clean();'; $this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.clean();';

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\Resolver\PropertyWriteTarget; use TypePhp\Resolver\PropertyWriteTarget;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\ArrayItem; use PhpParser\Node\ArrayItem;
@ -26,19 +28,19 @@ trait AssignOpTrait
$target = $this->parseGlobalsArrayDimFetch($left); $target = $this->parseGlobalsArrayDimFetch($left);
$value = $this->parseExprAsValue($right); $value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
return '((' . $tmp . ' = ' . $value . ', ' . $target . ' = ' . $tmp . '), ' . $tmp . ')'; return '((' . $tmp . ' = ' . $value . ', ' . $target . ' = ' . $tmp . '), ' . $tmp . ')';
} }
$array = $this->parseWritableIdentifier($left->var); $array = $this->parseWritableIdentifier($left->var);
$code = ''; $code = '';
if (!$this->hasVar($array) and $this->isVarExpr($left->var)) { if (!$this->hasVar($array) and $this->isVarExpr($left->var)) {
$this->addLocalVar($array, self::TYPE_ARRAY); $this->addLocalVar($array, Type::ARRAY);
} }
$value = $this->parseExprAsValue($right); $value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
if ($left->dim === null) { if ($left->dim === null) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')'; return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')';
@ -62,7 +64,7 @@ trait AssignOpTrait
} }
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
// Comma expression: store RHS → execute side effect → evaluate to stored value // Comma expression: store RHS → execute side effect → evaluate to stored value
return '((' . $tmp . ' = ' . $rightExpr . ', ' . $this->emitDynamicPropertyFetchWrite($left, $tmp, $target) . '), ' . $tmp . ')'; return '((' . $tmp . ' = ' . $rightExpr . ', ' . $this->emitDynamicPropertyFetchWrite($left, $tmp, $target) . '), ' . $tmp . ')';
} }
@ -77,7 +79,7 @@ trait AssignOpTrait
$next = $next->expr; $next = $next->expr;
} }
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
// 翻转赋值链 // 翻转赋值链
$chain = array_reverse($chain); $chain = array_reverse($chain);
@ -108,7 +110,7 @@ trait AssignOpTrait
$code = '{'; $code = '{';
$this->indentLevel++; $this->indentLevel++;
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; '; $code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; ';
foreach ($items as $k => $item) { foreach ($items as $k => $item) {
if (!$item) { if (!$item) {
@ -118,13 +120,13 @@ trait AssignOpTrait
$key = $item->key ? $this->parseArrayKey($item->key) : (string) $k; $key = $item->key ? $this->parseArrayKey($item->key) : (string) $k;
if ($item->value instanceof Expr\List_) { if ($item->value instanceof Expr\List_) {
$nestedTmp = $this->genTmpVarName(); $nestedTmp = $this->genTmpVarName();
$this->addLocalVar($nestedTmp, self::TYPE_ARRAY); $this->addLocalVar($nestedTmp, Type::ARRAY);
$code .= "{$nestedTmp} = {$tmpVar}.item({$key}); "; $code .= "{$nestedTmp} = {$tmpVar}.item({$key}); ";
$code .= $this->parseAssignToList($item->value, new Variable($nestedTmp)); $code .= $this->parseAssignToList($item->value, new Variable($nestedTmp));
} else { } else {
$var = $this->parseWritableIdentifier($item->value); $var = $this->parseWritableIdentifier($item->value);
if ($this->isVarExpr($item->value) and !$this->hasVar($var)) { if ($this->isVarExpr($item->value) and !$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR); $this->addLocalVar($var, Type::VAR);
} }
$code .= "{$var} = {$tmpVar}.item({$key}); "; $code .= "{$var} = {$tmpVar}.item({$key}); ";
} }
@ -148,8 +150,8 @@ trait AssignOpTrait
$type = $this->detectTypeOfExpr($right); $type = $this->detectTypeOfExpr($right);
$finalVarType = $this->getNormalAssignType($type); $finalVarType = $this->getNormalAssignType($type);
$runtimeObjectAssignClass = ''; $runtimeObjectAssignClass = '';
if ($type === self::TYPE_VOID) { if ($type === Type::VOID) {
$type = self::TYPE_VAR; $type = Type::VAR;
} }
if ($left instanceof Expr\PropertyFetch && ($setter = $this->getPropertyHookSetter($left)) !== null) { if ($left instanceof Expr\PropertyFetch && ($setter = $this->getPropertyHookSetter($left)) !== null) {
@ -179,7 +181,7 @@ trait AssignOpTrait
// 右值是一个对象,已获得类的名称,左值必须与右值的类一致 // 右值是一个对象,已获得类的名称,左值必须与右值的类一致
if ($rightClass) { if ($rightClass) {
if (!$this->hasVar($var)) { if (!$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_OBJECT); $this->addLocalVar($var, Type::OBJECT);
$this->addObject($var, $rightClass); $this->addObject($var, $rightClass);
} elseif (($leftClass = $this->getDeclaredObjectType($var)) !== '') { } elseif (($leftClass = $this->getDeclaredObjectType($var)) !== '') {
if ($this->isObjectClassStaticallyAssignableTo($rightClass, $leftClass)) { if ($this->isObjectClassStaticallyAssignableTo($rightClass, $leftClass)) {
@ -194,7 +196,7 @@ trait AssignOpTrait
$this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`"); $this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`");
} }
} else { } else {
$this->checkVarAssignExpr($left, $this->getVarType($var), self::TYPE_OBJECT); $this->checkVarAssignExpr($left, $this->getVarType($var), Type::OBJECT);
} }
} else { } else {
if ($this->isMethodCall($right) and $this->isNamedMethod($right->name)) { if ($this->isMethodCall($right) and $this->isNamedMethod($right->name)) {
@ -210,7 +212,7 @@ trait AssignOpTrait
} }
} }
if ($this->isFuncCallExpr($right) and $this->isNameExpr($right->name)) { if ($this->isFuncCallExpr($right) and $this->isNameExpr($right->name)) {
$type = $type === self::TYPE_VOID ? self::TYPE_VAR : $type; $type = $type === Type::VOID ? Type::VAR : $type;
} elseif ($this->isStaticCall($right) and $this->isNameExpr($right->class) and $this->isIdExpr($right->name)) { } elseif ($this->isStaticCall($right) and $this->isNameExpr($right->class) and $this->isIdExpr($right->name)) {
$class = $this->parseIdentifier($right->class); $class = $this->parseIdentifier($right->class);
if ($class === 'std') { if ($class === 'std') {
@ -222,18 +224,18 @@ trait AssignOpTrait
$this->fatalError($left, "Must create std::{$right->name->toString()} in the top-level scope of the function"); $this->fatalError($left, "Must create std::{$right->name->toString()} in the top-level scope of the function");
} }
if ($right->name->toString() === 'array') { if ($right->name->toString() === 'array') {
$this->addLocalVar($var, self::TYPE_STD_ARRAY); $this->addLocalVar($var, Type::STD_ARRAY);
return $this->parseStdArray($var, $right); return $this->parseStdArray($var, $right);
} }
if ($right->name->toString() === 'vector') { if ($right->name->toString() === 'vector') {
$this->addLocalVar($var, self::TYPE_STD_VECTOR); $this->addLocalVar($var, Type::STD_VECTOR);
return $this->parseStdVector($var, $right); return $this->parseStdVector($var, $right);
} }
if ($right->name->toString() === 'map') { if ($right->name->toString() === 'map') {
$this->addLocalVar($var, self::TYPE_STD_MAP); $this->addLocalVar($var, Type::STD_MAP);
return $this->parseStdMap($var, $right); return $this->parseStdMap($var, $right);
} }
$this->addLocalVar($var, self::TYPE_STD_ORDERED_MAP); $this->addLocalVar($var, Type::STD_ORDERED_MAP);
return $this->parseStdOrderedMap($var, $right); return $this->parseStdOrderedMap($var, $right);
} else { } else {
$valueExpr = $this->parseStdCall($right); $valueExpr = $this->parseStdCall($right);
@ -246,7 +248,7 @@ trait AssignOpTrait
} }
} elseif ($this->isVarExpr($right)) { } elseif ($this->isVarExpr($right)) {
$rightVar = $this->parseIdentifier($right); $rightVar = $this->parseIdentifier($right);
$type = $this->isStdContainer($rightVar) ? self::TYPE_ARRAY : $this->getVarType($rightVar); $type = $this->isStdContainer($rightVar) ? Type::ARRAY : $this->getVarType($rightVar);
$finalVarType = $this->getNormalAssignType($type); $finalVarType = $this->getNormalAssignType($type);
$leftClass = $this->getDeclaredObjectType($var); $leftClass = $this->getDeclaredObjectType($var);
$rightClass = $this->getDeclaredObjectType($rightVar); $rightClass = $this->getDeclaredObjectType($rightVar);
@ -269,7 +271,7 @@ trait AssignOpTrait
$finalVarType = $this->getVarType($var); $finalVarType = $this->getVarType($var);
$this->checkVarAssignExpr($left, $finalVarType, $type); $this->checkVarAssignExpr($left, $finalVarType, $type);
$declaredObjectClass = $this->getDeclaredObjectType($var); $declaredObjectClass = $this->getDeclaredObjectType($var);
if ($finalVarType === self::TYPE_OBJECT && $declaredObjectClass !== '' && ($type === self::TYPE_VAR || $type === self::TYPE_OBJECT)) { if ($finalVarType === Type::OBJECT && $declaredObjectClass !== '' && ($type === Type::VAR || $type === Type::OBJECT)) {
$runtimeObjectAssignClass = $declaredObjectClass; $runtimeObjectAssignClass = $declaredObjectClass;
} }
} }
@ -278,7 +280,7 @@ trait AssignOpTrait
return $this->parseAssignPropertyFetch($left, $right, $propertyWriteTarget); return $this->parseAssignPropertyFetch($left, $right, $propertyWriteTarget);
} elseif ($this->isArrayDimFetch($left) and $this->isVarExpr($left->var)) { } elseif ($this->isArrayDimFetch($left) and $this->isVarExpr($left->var)) {
$tmp = $this->parseIdentifier($left->var); $tmp = $this->parseIdentifier($left->var);
if ($this->getVarType($tmp) === self::TYPE_STR and $left->dim === null) { if ($this->getVarType($tmp) === Type::STR and $left->dim === null) {
$this->fatalError($left, 'Cannot use [] for strings'); $this->fatalError($left, 'Cannot use [] for strings');
} }
if ($this->isStdContainerExpr($left)) { if ($this->isStdContainerExpr($left)) {
@ -304,12 +306,12 @@ trait AssignOpTrait
$leftExprType = $this->detectTypeOfExpr($left); $leftExprType = $this->detectTypeOfExpr($left);
$rightExprType = $this->detectTypeOfExpr($right); $rightExprType = $this->detectTypeOfExpr($right);
if ($propertyWriteTarget !== null && ($propertyDef = $this->getNativePropertyDef($left)) !== null) { if ($propertyWriteTarget !== null && ($propertyDef = $this->getNativePropertyDef($left)) !== null) {
$effectiveRightType = $rightExprType === self::TYPE_VAR && $this->getNativeScalarPropertyTypeCheckHelper($propertyDef) !== null $effectiveRightType = $rightExprType === Type::VAR && $this->getNativeScalarPropertyTypeCheckHelper($propertyDef) !== null
? $propertyDef->type ? $propertyDef->type
: $rightExprType; : $rightExprType;
return $var . ' = ' . $this->convertNativePropertyWriteExpr($propertyDef->type, $effectiveRightType, $rightExpr); return $var . ' = ' . $this->convertNativePropertyWriteExpr($propertyDef->type, $effectiveRightType, $rightExpr);
} }
if ($finalVarType === self::TYPE_VAR) { if ($finalVarType === Type::VAR) {
return $var . ' = ' . $rightExpr; return $var . ' = ' . $rightExpr;
} else { } else {
return $var . ' = ' . $this->convertExprType($rightExpr, $leftExprType, $rightExprType); return $var . ' = ' . $this->convertExprType($rightExpr, $leftExprType, $rightExprType);
@ -330,7 +332,7 @@ trait AssignOpTrait
$rightExpr = $this->wrapPropertyWriteTypeCheck($target, $right, $rightExpr); $rightExpr = $this->wrapPropertyWriteTypeCheck($target, $right, $rightExpr);
} }
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
$call = $this->emitPropertyHookSetterCall($left, $setter, new Expr\Variable($tmp)); $call = $this->emitPropertyHookSetterCall($left, $setter, new Expr\Variable($tmp));
return '((' . $tmp . ' = ' . $rightExpr . ', ' . $call . '), ' . $tmp . ')'; return '((' . $tmp . ' = ' . $rightExpr . ', ' . $call . '), ' . $tmp . ')';
} }
@ -346,8 +348,8 @@ trait AssignOpTrait
return false; return false;
} }
return !in_array($def->type, [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL, self::TYPE_STR, self::TYPE_ARRAY], true) return !in_array($def->type, [Type::INT, Type::FLOAT, Type::BOOL, Type::STR, Type::ARRAY], true)
&& $rightType === self::TYPE_VAR; && $rightType === Type::VAR;
} }
protected function parseStdContainerCopyAssign(string $leftVar, Expr $right): ?string protected function parseStdContainerCopyAssign(string $leftVar, Expr $right): ?string
@ -398,7 +400,7 @@ trait AssignOpTrait
$right = $this->parseExprAsValue($node->expr); $right = $this->parseExprAsValue($node->expr);
$read = $this->emitPropertyHookGetterCall($node->var, $getter); $read = $this->emitPropertyHookGetterCall($node->var, $getter);
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
$binaryOp = $this->removeAssignOp($op); $binaryOp = $this->removeAssignOp($op);
$value = match ($binaryOp) { $value = match ($binaryOp) {
'.' => 'php::concat(' . $read . ', ' . $right . ')', '.' => 'php::concat(' . $read . ', ' . $right . ')',
@ -428,7 +430,7 @@ trait AssignOpTrait
// BigInt/BigDecimal/BigFloat are immutable Box types stored inside // BigInt/BigDecimal/BigFloat are immutable Box types stored inside
// php::Var — Variant::operator+= calls ZendVM add_function which // php::Var — Variant::operator+= calls ZendVM add_function which
// cannot handle them. We must generate `$v = Type::add($v, $x)`. // cannot handle them. We must generate `$v = Type::add($v, $x)`.
if ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
return $this->parseBigAssignOp($node, $var, $type, $expr, $rightType, $op); return $this->parseBigAssignOp($node, $var, $type, $expr, $rightType, $op);
} }
@ -468,7 +470,7 @@ trait AssignOpTrait
$this->context->beforeStmtLines[] = "{$tmpVar} = php::concat(" . $this->context->beforeStmtLines[] = "{$tmpVar} = php::concat(" .
$this->convertVarType($tmpVar, $readVar) . ', ' . $this->convertVarType($tmpVar, $readVar) . ', ' .
$this->convertExprType($expr, $type, $rightType) . ');'; $this->convertExprType($expr, $type, $rightType) . ');';
} elseif ($type === self::TYPE_BIGINT || $type === self::TYPE_DECIMAL || $type === self::TYPE_BIGFLOAT) { } elseif ($type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$bigAssign = $this->parseBigAssignOpExpr($readVar, $type, $expr, $rightType, $binaryOp, $node->var, $node->expr); $bigAssign = $this->parseBigAssignOpExpr($readVar, $type, $expr, $rightType, $binaryOp, $node->var, $node->expr);
$this->context->beforeStmtLines[] = "{$tmpVar} = {$bigAssign};"; $this->context->beforeStmtLines[] = "{$tmpVar} = {$bigAssign};";
} else { } else {
@ -490,7 +492,7 @@ trait AssignOpTrait
} }
$binaryOp = $this->removeAssignOp($op); $binaryOp = $this->removeAssignOp($op);
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR); $this->addLocalVar($tmpVar, Type::VAR);
$readProperty = $this->emitDynamicPropertyFetchRead($node->var, $propertyWriteTarget); $readProperty = $this->emitDynamicPropertyFetchRead($node->var, $propertyWriteTarget);
if ($this->isAssignOpConcat($op)) { if ($this->isAssignOpConcat($op)) {
$this->context->beforeStmtLines[] = "{$tmpVar} = php::concat({$readProperty}, {$expr});"; $this->context->beforeStmtLines[] = "{$tmpVar} = php::concat({$readProperty}, {$expr});";
@ -521,7 +523,7 @@ trait AssignOpTrait
} }
$rightType = $this->detectTypeOfExpr($node->expr); $rightType = $this->detectTypeOfExpr($node->expr);
if ($this->isFixedObjectProp($def) && $rightType !== self::TYPE_VAR && !$this->canAssignStaticTypeToObjectProperty($def, $rightType)) { if ($this->isFixedObjectProp($def) && $rightType !== Type::VAR && !$this->canAssignStaticTypeToObjectProperty($def, $rightType)) {
$this->fatalError( $this->fatalError(
$node->var, $node->var,
'Cannot assign ' . $this->getPropertyAssignmentTypeName($rightType) 'Cannot assign ' . $this->getPropertyAssignmentTypeName($rightType)
@ -535,15 +537,15 @@ trait AssignOpTrait
$var = $this->parseWritableIdentifier($node->var); $var = $this->parseWritableIdentifier($node->var);
if (!$this->isNativePropertyTypedValue($node->var)) { if (!$this->isNativePropertyTypedValue($node->var)) {
$helper = $def->type === self::TYPE_FLOAT ? 'typephp_static_float_ref' : 'typephp_static_int_ref'; $helper = $def->type === Type::FLOAT ? 'typephp_static_float_ref' : 'typephp_static_int_ref';
$var = $helper . '(' . $var . '.unwrap_ptr())'; $var = $helper . '(' . $var . '.unwrap_ptr())';
} }
$rightExpr = $this->parseIdentifier($node->expr); $rightExpr = $this->parseIdentifier($node->expr);
if ($rightType === self::TYPE_VAR) { if ($rightType === Type::VAR) {
$rightExpr = $this->wrapObjectPropertyAssignTypeCheck($node->var, $node->expr, $rightExpr); $rightExpr = $this->wrapObjectPropertyAssignTypeCheck($node->var, $node->expr, $rightExpr);
} }
$effectiveRightType = $rightType === self::TYPE_VAR && $this->getNativeScalarPropertyTypeCheckHelper($def) !== null $effectiveRightType = $rightType === Type::VAR && $this->getNativeScalarPropertyTypeCheckHelper($def) !== null
? $def->type ? $def->type
: $rightType; : $rightType;
@ -561,13 +563,13 @@ trait AssignOpTrait
protected function canUseNativePropertyAssignOp(string $propertyType, string $rightType, string $op): bool protected function canUseNativePropertyAssignOp(string $propertyType, string $rightType, string $op): bool
{ {
if ($rightType !== self::TYPE_VAR && !($propertyType === $rightType || ($propertyType === self::TYPE_FLOAT && $rightType === self::TYPE_INT))) { if ($rightType !== Type::VAR && !($propertyType === $rightType || ($propertyType === Type::FLOAT && $rightType === Type::INT))) {
return false; return false;
} }
return match ($propertyType) { return match ($propertyType) {
self::TYPE_INT => in_array($op, ['+=', '-=', '*=', '%=', '<<=', '>>=', '&=', '|=', '^='], true), Type::INT => in_array($op, ['+=', '-=', '*=', '%=', '<<=', '>>=', '&=', '|=', '^='], true),
self::TYPE_FLOAT => in_array($op, ['+=', '-=', '*=', '/='], true), Type::FLOAT => in_array($op, ['+=', '-=', '*=', '/='], true),
default => false, default => false,
}; };
} }
@ -582,9 +584,9 @@ trait AssignOpTrait
protected function parseBigAssignOpExpr(string $leftExpr, string $leftType, string $rightExpr, string $rightType, string $binaryOp, NodeAbstract $errorNode, ?NodeAbstract $rightNode = null): string protected function parseBigAssignOpExpr(string $leftExpr, string $leftType, string $rightExpr, string $rightType, string $binaryOp, NodeAbstract $errorNode, ?NodeAbstract $rightNode = null): string
{ {
[$class, $opMap] = match ($leftType) { [$class, $opMap] = match ($leftType) {
self::TYPE_BIGINT => ['BigInt', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor', '<<' => 'bitShiftLeft', '>>' => 'bitShiftRight']], Type::BIGINT => ['BigInt', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor', '<<' => 'bitShiftLeft', '>>' => 'bitShiftRight']],
self::TYPE_DECIMAL => ['Decimal', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod']], Type::DECIMAL => ['Decimal', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod']],
self::TYPE_BIGFLOAT => ['BigFloat', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div']], Type::BIGFLOAT => ['BigFloat', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div']],
}; };
$method = $opMap[$binaryOp] ?? null; $method = $opMap[$binaryOp] ?? null;
@ -595,9 +597,9 @@ trait AssignOpTrait
// For bitwise shifts, the right operand is a shift amount (Int), not BigInt // For bitwise shifts, the right operand is a shift amount (Int), not BigInt
$isShift = ($binaryOp === '<<' || $binaryOp === '>>'); $isShift = ($binaryOp === '<<' || $binaryOp === '>>');
$convertedRight = match ($leftType) { $convertedRight = match ($leftType) {
self::TYPE_BIGINT => $isShift ? $rightExpr : $this->convertBigIntExpr($rightExpr, $rightType), Type::BIGINT => $isShift ? $rightExpr : $this->convertBigIntExpr($rightExpr, $rightType),
self::TYPE_DECIMAL => $this->convertDecimalExpr($rightExpr, $rightType, $rightNode), Type::DECIMAL => $this->convertDecimalExpr($rightExpr, $rightType, $rightNode),
self::TYPE_BIGFLOAT => $this->convertBigFloatExpr($rightExpr, $rightType), Type::BIGFLOAT => $this->convertBigFloatExpr($rightExpr, $rightType),
}; };
return 'php::' . $class . '::' . $method . '(' . $leftExpr . ', ' . $convertedRight . ')'; return 'php::' . $class . '::' . $method . '(' . $leftExpr . ', ' . $convertedRight . ')';
@ -681,16 +683,16 @@ trait AssignOpTrait
if ($this->isVarExpr($expr->var)) { if ($this->isVarExpr($expr->var)) {
if (!$this->hasVar($left)) { if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF); $this->addLocalVar($left, Type::REF);
} else { } else {
$type = $this->getVarType($left); $type = $this->getVarType($left);
if ($type !== self::TYPE_REF) { if ($type !== Type::REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type); $this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
} }
} }
} }
$tmpVar = $this->addTmpVar(self::TYPE_REF); $tmpVar = $this->addTmpVar(Type::REF);
$rightExpr = ''; $rightExpr = '';
if ($this->isVarExpr($expr->expr)) { if ($this->isVarExpr($expr->expr)) {
@ -778,7 +780,7 @@ trait AssignOpTrait
$value = $this->parseExprAsValue($right); $value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName(); $tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR); $this->addLocalVar($tmp, Type::VAR);
if ($left->dim === null) { if ($left->dim === null) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . $this->emitDynamicPropertyFetchAppendArray($left->var, $tmp, $propertyWriteTarget) . '), ' . $tmp . ')'; return $code . '((' . $tmp . ' = ' . $value . ', ' . $this->emitDynamicPropertyFetchAppendArray($left->var, $tmp, $propertyWriteTarget) . '), ' . $tmp . ')';
@ -815,7 +817,7 @@ trait AssignOpTrait
protected function getNormalAssignType(string $type): string protected function getNormalAssignType(string $type): string
{ {
return $type === self::TYPE_REF || $type === self::TYPE_VOID ? self::TYPE_VAR : $type; return $type === Type::REF || $type === Type::VOID ? Type::VAR : $type;
} }
} }

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\Generator\Symbol; use TypePhp\Generator\Symbol;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
@ -31,18 +33,18 @@ trait BinaryOpTrait
$leftType = $this->detectTypeOfExpr($left); $leftType = $this->detectTypeOfExpr($left);
$rightType = $this->detectTypeOfExpr($right); $rightType = $this->detectTypeOfExpr($right);
if ($leftType === self::TYPE_BIGFLOAT || $rightType === self::TYPE_BIGFLOAT) { if ($leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) {
// BigFloat cannot implicitly mix with BigInt or Decimal — risk of precision loss // BigFloat cannot implicitly mix with BigInt or Decimal — risk of precision loss
if ($leftType === self::TYPE_BIGINT || $rightType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) {
$this->fatalError($left, 'Cannot mix BigFloat and BigInt implicitly. Use std::bigFloat() to convert explicitly.'); $this->fatalError($left, 'Cannot mix BigFloat and BigInt implicitly. Use std::bigFloat() to convert explicitly.');
} }
if ($leftType === self::TYPE_DECIMAL || $rightType === self::TYPE_DECIMAL) { if ($leftType === Type::DECIMAL || $rightType === Type::DECIMAL) {
$this->fatalError($left, 'Cannot mix BigFloat and Decimal implicitly. Use std::bigFloat() to convert explicitly.'); $this->fatalError($left, 'Cannot mix BigFloat and Decimal implicitly. Use std::bigFloat() to convert explicitly.');
} }
if ($leftType !== self::TYPE_BIGFLOAT) { if ($leftType !== Type::BIGFLOAT) {
$leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType); $leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType);
} }
if ($rightType !== self::TYPE_BIGFLOAT) { if ($rightType !== Type::BIGFLOAT) {
$rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType); $rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType);
} }
$arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div']; $arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div'];
@ -56,15 +58,15 @@ trait BinaryOpTrait
} }
} }
if ($leftType === self::TYPE_DECIMAL || $rightType === self::TYPE_DECIMAL) { if ($leftType === Type::DECIMAL || $rightType === Type::DECIMAL) {
// BigInt and Decimal cannot implicitly mix — risk of precision loss // BigInt and Decimal cannot implicitly mix — risk of precision loss
if ($leftType === self::TYPE_BIGINT || $rightType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) {
$this->fatalError($left, 'Cannot mix BigInt and Decimal implicitly. Use std::decimal() or std::bigInt() to convert explicitly.'); $this->fatalError($left, 'Cannot mix BigInt and Decimal implicitly. Use std::decimal() or std::bigInt() to convert explicitly.');
} }
if ($leftType !== self::TYPE_DECIMAL) { if ($leftType !== Type::DECIMAL) {
$leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $left); $leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $left);
} }
if ($rightType !== self::TYPE_DECIMAL) { if ($rightType !== Type::DECIMAL) {
$rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $right); $rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $right);
} }
$arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod']; $arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod'];
@ -78,24 +80,24 @@ trait BinaryOpTrait
} }
} }
if ($leftType === self::TYPE_BIGINT || $rightType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) {
// Bitwise shifts: right operand is shift amount, must stay as Int // Bitwise shifts: right operand is shift amount, must stay as Int
if ($op === '<<' || $op === '>>') { if ($op === '<<' || $op === '>>') {
if ($leftType !== self::TYPE_BIGINT) { if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType); $leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
} }
if ($rightType === self::TYPE_BIGINT) { if ($rightType === Type::BIGINT) {
$rightExpr = 'php::BigInt::toInt(' . $rightExpr . ')'; $rightExpr = 'php::BigInt::toInt(' . $rightExpr . ')';
} elseif ($rightType !== self::TYPE_INT) { } elseif ($rightType !== Type::INT) {
$rightExpr = $this->convertExprType($rightExpr, $rightType, self::TYPE_INT); $rightExpr = $this->convertExprType($rightExpr, $rightType, Type::INT);
} }
$method = ($op === '<<') ? 'bitShiftLeft' : 'bitShiftRight'; $method = ($op === '<<') ? 'bitShiftLeft' : 'bitShiftRight';
return 'php::BigInt::' . $method . '(' . $leftExpr . ', ' . $rightExpr . ')'; return 'php::BigInt::' . $method . '(' . $leftExpr . ', ' . $rightExpr . ')';
} }
if ($leftType !== self::TYPE_BIGINT) { if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType); $leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
} }
if ($rightType !== self::TYPE_BIGINT) { if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType); $rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
} }
$arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor']; $arithOpMap = ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor'];
@ -110,7 +112,7 @@ trait BinaryOpTrait
} }
// Any Big*-typed operand reaching here means no Big* block handled the operator // Any Big*-typed operand reaching here means no Big* block handled the operator
$bigTypes = [self::TYPE_BIGFLOAT, self::TYPE_DECIMAL, self::TYPE_BIGINT]; $bigTypes = [Type::BIGFLOAT, Type::DECIMAL, Type::BIGINT];
if (in_array($leftType, $bigTypes, true) || in_array($rightType, $bigTypes, true)) { if (in_array($leftType, $bigTypes, true) || in_array($rightType, $bigTypes, true)) {
$this->fatalError($left, "Operator '{$op}' is not supported for Big* numeric types"); $this->fatalError($left, "Operator '{$op}' is not supported for Big* numeric types");
} }
@ -118,15 +120,15 @@ trait BinaryOpTrait
// Only promote between native types (Int ↔ Float). When one side is // Only promote between native types (Int ↔ Float). When one side is
// php::Var, let the Variant operator handle type coercion so that // php::Var, let the Variant operator handle type coercion so that
// run-time PHP type-juggling rules are followed correctly. // run-time PHP type-juggling rules are followed correctly.
if ($leftType === self::TYPE_FLOAT && $rightType === self::TYPE_INT) { if ($leftType === Type::FLOAT && $rightType === Type::INT) {
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_FLOAT, $rightType); $rightExpr = $this->convertExprType($rightExpr, Type::FLOAT, $rightType);
} elseif ($rightType === self::TYPE_FLOAT && $leftType === self::TYPE_INT) { } elseif ($rightType === Type::FLOAT && $leftType === Type::INT) {
$leftExpr = $this->convertExprType($leftExpr, $leftType, self::TYPE_FLOAT); $leftExpr = $this->convertExprType($leftExpr, $leftType, Type::FLOAT);
} }
$this->guardLiteralDivisionByZero($right, $op); $this->guardLiteralDivisionByZero($right, $op);
if ($op === '%' and !($leftType === self::TYPE_INT and $rightType === self::TYPE_INT)) { if ($op === '%' and !($leftType === Type::INT and $rightType === Type::INT)) {
return 'php::fn::mod(' . $leftExpr . ', ' . $rightExpr . ')'; return 'php::fn::mod(' . $leftExpr . ', ' . $rightExpr . ')';
} }
@ -191,7 +193,7 @@ trait BinaryOpTrait
{ {
if ($expr instanceof Expr\BinaryOp) { if ($expr instanceof Expr\BinaryOp) {
$type = $this->detectTypeOfExpr($expr); $type = $this->detectTypeOfExpr($expr);
return in_array($type, [self::TYPE_BIGINT, self::TYPE_DECIMAL, self::TYPE_BIGFLOAT], true) ? $type : self::TYPE_VAR; return in_array($type, [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT], true) ? $type : Type::VAR;
} }
if ( if (
@ -200,7 +202,7 @@ trait BinaryOpTrait
|| $expr instanceof Expr\StaticCall || $expr instanceof Expr\StaticCall
) { ) {
$type = $this->detectTypeOfExpr($expr); $type = $this->detectTypeOfExpr($expr);
return in_array($type, [self::TYPE_BIGINT, self::TYPE_DECIMAL, self::TYPE_BIGFLOAT], true) ? $type : self::TYPE_VAR; return in_array($type, [Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT], true) ? $type : Type::VAR;
} }
if ($expr instanceof Expr\PropertyFetch) { if ($expr instanceof Expr\PropertyFetch) {
@ -215,7 +217,7 @@ trait BinaryOpTrait
return $def->type; return $def->type;
} }
} }
return self::TYPE_VAR; return Type::VAR;
} }
if ($expr instanceof Expr\StaticPropertyFetch) { if ($expr instanceof Expr\StaticPropertyFetch) {
@ -223,11 +225,11 @@ trait BinaryOpTrait
if ($def && $this->isNativePropertyTypedValue($expr)) { if ($def && $this->isNativePropertyTypedValue($expr)) {
return $def->type; return $def->type;
} }
return self::TYPE_VAR; return Type::VAR;
} }
if ($expr instanceof Expr\ArrayDimFetch) { if ($expr instanceof Expr\ArrayDimFetch) {
return self::TYPE_VAR; return Type::VAR;
} }
$type = $this->detectTypeOfExpr($expr); $type = $this->detectTypeOfExpr($expr);
@ -305,11 +307,11 @@ trait BinaryOpTrait
$this->assertExprCanBeUsedAsValue($expr->left, 'binary operand'); $this->assertExprCanBeUsedAsValue($expr->left, 'binary operand');
$this->assertExprCanBeUsedAsValue($expr->right, 'binary operand'); $this->assertExprCanBeUsedAsValue($expr->right, 'binary operand');
$leftType = $this->detectTypeOfExpr($expr->left); $leftType = $this->detectTypeOfExpr($expr->left);
if ($leftType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT) {
$leftExpr = $this->parseOrderedOperand($expr->left, false); $leftExpr = $this->parseOrderedOperand($expr->left, false);
$rightExpr = $this->parseOrderedOperand($expr->right, false); $rightExpr = $this->parseOrderedOperand($expr->right, false);
$rightType = $this->detectTypeOfExpr($expr->right); $rightType = $this->detectTypeOfExpr($expr->right);
if ($rightType !== self::TYPE_BIGINT) { if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType); $rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
} }
return 'php::BigInt::pow(' . $leftExpr . ', ' . $rightExpr . ')'; return 'php::BigInt::pow(' . $leftExpr . ', ' . $rightExpr . ')';
@ -376,7 +378,7 @@ trait BinaryOpTrait
*/ */
private function optimizeIdenticalOp(NodeAbstract $astLeft, NodeAbstract $astRight, string $cppLeft, string $cppRight): ?string private function optimizeIdenticalOp(NodeAbstract $astLeft, NodeAbstract $astRight, string $cppLeft, string $cppRight): ?string
{ {
$primitiveTypes = [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL]; $primitiveTypes = [Type::INT, Type::FLOAT, Type::BOOL];
$leftType = $this->detectTypeOfExpr($astLeft); $leftType = $this->detectTypeOfExpr($astLeft);
$rightType = $this->detectTypeOfExpr($astRight); $rightType = $this->detectTypeOfExpr($astRight);
@ -431,7 +433,7 @@ trait BinaryOpTrait
$code .= $this->getIndent() . 'if (' . $rightCondition . ') {'; $code .= $this->getIndent() . 'if (' . $rightCondition . ') {';
$code .= $this->formatCapturedStmtLines($rightBeforeStmts); $code .= $this->formatCapturedStmtLines($rightBeforeStmts);
if ($rightAfterStmts) { if ($rightAfterStmts) {
$rightTmpVar = $this->addTmpVar(self::TYPE_VAR); $rightTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';'; $code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';';
$code .= $this->formatCapturedStmtLines($rightAfterStmts); $code .= $this->formatCapturedStmtLines($rightAfterStmts);
$rightExpr = $rightTmpVar; $rightExpr = $rightTmpVar;
@ -470,35 +472,35 @@ trait BinaryOpTrait
$leftType = $this->detectTypeOfExpr($expr->left); $leftType = $this->detectTypeOfExpr($expr->left);
$rightType = $this->detectTypeOfExpr($expr->right); $rightType = $this->detectTypeOfExpr($expr->right);
if ($leftType === self::TYPE_BIGFLOAT || $rightType === self::TYPE_BIGFLOAT) { if ($leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) {
$leftExpr = $this->parseOrderedOperand($expr->left, false); $leftExpr = $this->parseOrderedOperand($expr->left, false);
$rightExpr = $this->parseOrderedOperand($expr->right, false); $rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_BIGFLOAT) { if ($leftType !== Type::BIGFLOAT) {
$leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType); $leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType);
} }
if ($rightType !== self::TYPE_BIGFLOAT) { if ($rightType !== Type::BIGFLOAT) {
$rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType); $rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType);
} }
return 'php::BigFloat::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix; return 'php::BigFloat::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix;
} }
if ($leftType === self::TYPE_BIGINT || $rightType === self::TYPE_BIGINT) { if ($leftType === Type::BIGINT || $rightType === Type::BIGINT) {
$leftExpr = $this->parseOrderedOperand($expr->left, false); $leftExpr = $this->parseOrderedOperand($expr->left, false);
$rightExpr = $this->parseOrderedOperand($expr->right, false); $rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_BIGINT) { if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType); $leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
} }
if ($rightType !== self::TYPE_BIGINT) { if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType); $rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
} }
return 'php::BigInt::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix; return 'php::BigInt::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix;
} }
if ($leftType === self::TYPE_DECIMAL || $rightType === self::TYPE_DECIMAL) { if ($leftType === Type::DECIMAL || $rightType === Type::DECIMAL) {
$leftExpr = $this->parseOrderedOperand($expr->left, false); $leftExpr = $this->parseOrderedOperand($expr->left, false);
$rightExpr = $this->parseOrderedOperand($expr->right, false); $rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_DECIMAL) { if ($leftType !== Type::DECIMAL) {
$leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $expr->left); $leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $expr->left);
} }
if ($rightType !== self::TYPE_DECIMAL) { if ($rightType !== Type::DECIMAL) {
$rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $expr->right); $rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $expr->right);
} }
return 'php::Decimal::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix; return 'php::Decimal::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix;

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
use TypePhp\Generator\Symbol; use TypePhp\Generator\Symbol;
@ -88,7 +90,7 @@ trait ClassConstantFetchTrait
{ {
$this->assertExprCanBeUsedAsValue($expr, 'class constant target'); $this->assertExprCanBeUsedAsValue($expr, 'class constant target');
[$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr); [$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$this->appendCapturedStmtLinesToContext($beforeStmts); $this->appendCapturedStmtLinesToContext($beforeStmts);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';';
$this->appendCapturedStmtLinesToContext($afterStmts); $this->appendCapturedStmtLinesToContext($afterStmts);

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\MagicConst; use PhpParser\Node\Scalar\MagicConst;
@ -123,15 +125,15 @@ trait ConstantExpressionTrait
return $this->getTypeFromZendType(gettype($this->internalConstants[$name])); return $this->getTypeFromZendType(gettype($this->internalConstants[$name]));
} }
if (strcasecmp($name, 'true') === 0) { if (strcasecmp($name, 'true') === 0) {
return self::TYPE_BOOL; return Type::BOOL;
} }
if (strcasecmp($name, 'false') === 0) { if (strcasecmp($name, 'false') === 0) {
return self::TYPE_BOOL; return Type::BOOL;
} }
if ($name === 'NAN' or $name === 'INF') { if ($name === 'NAN' or $name === 'INF') {
return self::TYPE_FLOAT; return Type::FLOAT;
} }
return self::TYPE_VAR; return Type::VAR;
} }
protected function isInternalScalarConstant(string $name): bool protected function isInternalScalarConstant(string $name): bool

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable; use PhpParser\Node\Expr\Variable;
@ -26,13 +28,13 @@ trait ExceptionControlFlowTrait
return 'php::throwException(' . $ex . ')'; return 'php::throwException(' . $ex . ')';
} elseif ($this->isVarExpr($expr->expr)) { } elseif ($this->isVarExpr($expr->expr)) {
$ex = $this->parseIdentifier($expr->expr); $ex = $this->parseIdentifier($expr->expr);
if ($type == self::TYPE_OBJECT) { if ($type == Type::OBJECT) {
return 'php::throwException(' . $ex . ')'; return 'php::throwException(' . $ex . ')';
} }
} else { } else {
$ex = $this->parseExpr($expr->expr); $ex = $this->parseExpr($expr->expr);
} }
if ($type != self::TYPE_VAR) { if ($type != Type::VAR) {
$this->fatalError($expr, 'Can only throw objects'); $this->fatalError($expr, 'Can only throw objects');
} }
return 'php::throwValue(' . $ex . ')'; return 'php::throwValue(' . $ex . ')';
@ -55,7 +57,7 @@ trait ExceptionControlFlowTrait
if ($catch->var) { if ($catch->var) {
$varName = $this->parseIdentifier($catch->var); $varName = $this->parseIdentifier($catch->var);
if (!$this->hasVar($varName) && $this->stmtListUsesVariable($finally->stmts, $varName)) { if (!$this->hasVar($varName) && $this->stmtListUsesVariable($finally->stmts, $varName)) {
$this->addLocalVar($varName, self::TYPE_OBJECT); $this->addLocalVar($varName, Type::OBJECT);
} }
} }
} }
@ -66,7 +68,7 @@ trait ExceptionControlFlowTrait
$code .= $this->getIndent() . '}' . PHP_EOL; $code .= $this->getIndent() . '}' . PHP_EOL;
$exVar = $this->genTmpVarName(); $exVar = $this->genTmpVarName();
$this->addLocalVar($exVar, self::TYPE_VAR); $this->addLocalVar($exVar, Type::VAR);
$code .= 'catch(zend_object *_ex) {' . PHP_EOL; $code .= 'catch(zend_object *_ex) {' . PHP_EOL;
$code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL; $code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL;
@ -99,7 +101,7 @@ trait ExceptionControlFlowTrait
foreach ($stmts as $stmt) { foreach ($stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Return_) { if ($stmt instanceof Node\Stmt\Return_) {
if ($stmt->expr) { if ($stmt->expr) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$result[] = new Node\Stmt\Expression(new Expr\Assign(new Variable($tmpVar), $stmt->expr)); $result[] = new Node\Stmt\Expression(new Expr\Assign(new Variable($tmpVar), $stmt->expr));
array_push($result, ...$this->cloneStmtList($finallyStmts)); array_push($result, ...$this->cloneStmtList($finallyStmts));
$result[] = new Node\Stmt\Return_(new Variable($tmpVar)); $result[] = new Node\Stmt\Return_(new Variable($tmpVar));
@ -191,7 +193,7 @@ trait ExceptionControlFlowTrait
$types = $catch->types; $types = $catch->types;
$var = $catch->var ? $this->parseIdentifier($catch->var) : ''; $var = $catch->var ? $this->parseIdentifier($catch->var) : '';
if ($var !== '' && !$this->hasVar($var)) { if ($var !== '' && !$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_OBJECT); $this->addLocalVar($var, Type::OBJECT);
} }
$code = $this->parseBeforeStmtLines() . PHP_EOL; $code = $this->parseBeforeStmtLines() . PHP_EOL;

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node\ArrayItem; use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\Foreach_;
@ -24,14 +26,14 @@ trait ForeachTrait
$key = $item->key ? $this->parseArrayKey($item->key) : (string) $k; $key = $item->key ? $this->parseArrayKey($item->key) : (string) $k;
if ($item->value instanceof Expr\List_) { if ($item->value instanceof Expr\List_) {
$nestedTmpVar = $this->genTmpVarName(); $nestedTmpVar = $this->genTmpVarName();
$this->addLocalVar($nestedTmpVar, self::TYPE_VAR); $this->addLocalVar($nestedTmpVar, Type::VAR);
$code .= $this->getIndent() . ' ' . $nestedTmpVar . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL; $code .= $this->getIndent() . ' ' . $nestedTmpVar . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
$code .= $this->parseForeachItemAsList($nestedTmpVar, $item->value->items); $code .= $this->parseForeachItemAsList($nestedTmpVar, $item->value->items);
continue; continue;
} }
$var = $this->parseWritableIdentifier($item->value); $var = $this->parseWritableIdentifier($item->value);
if ($this->isVarExpr($item->value) and !$this->hasVar($var)) { if ($this->isVarExpr($item->value) and !$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR); $this->addLocalVar($var, Type::VAR);
} }
$code .= $this->getIndent() . ' ' . $var . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL; $code .= $this->getIndent() . ' ' . $var . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
} else { } else {
@ -46,7 +48,7 @@ trait ForeachTrait
return $this->parseStmts($node->stmts) . $this->genLoopEndFlagCheck(); return $this->parseStmts($node->stmts) . $this->genLoopEndFlagCheck();
} }
protected function parseForeachKeyAssignment(Foreach_ $node, string $keyExpr, string $defaultType = self::TYPE_VAR): string protected function parseForeachKeyAssignment(Foreach_ $node, string $keyExpr, string $defaultType = Type::VAR): string
{ {
if (!$node->keyVar) { if (!$node->keyVar) {
return ''; return '';
@ -72,7 +74,7 @@ trait ForeachTrait
$this->fatalError($node, 'Foreach by reference cannot use list destructuring'); $this->fatalError($node, 'Foreach by reference cannot use list destructuring');
} }
$listTmpVar = $this->genTmpVarName(); $listTmpVar = $this->genTmpVarName();
$this->addLocalVar($listTmpVar, self::TYPE_VAR); $this->addLocalVar($listTmpVar, Type::VAR);
return $this->getIndent() . ' ' . $listTmpVar . ' = ' . $valueExpr . ';' . PHP_EOL return $this->getIndent() . ' ' . $listTmpVar . ' = ' . $valueExpr . ';' . PHP_EOL
. $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items); . $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items);
} }
@ -92,8 +94,8 @@ trait ForeachTrait
$valueVar = $this->parseIdentifier($node->valueVar); $valueVar = $this->parseIdentifier($node->valueVar);
if ($node->byRef) { if ($node->byRef) {
if (!$this->hasVar($valueVar)) { if (!$this->hasVar($valueVar)) {
$this->addLocalVar($valueVar, self::TYPE_REF); $this->addLocalVar($valueVar, Type::REF);
} elseif ($this->getVarType($valueVar) !== self::TYPE_REF) { } elseif ($this->getVarType($valueVar) !== Type::REF) {
$this->fatalError($node, 'Cannot assign value to reference of type'); $this->fatalError($node, 'Cannot assign value to reference of type');
} }
return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueRefExpr . ';' . PHP_EOL; return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueRefExpr . ';' . PHP_EOL;
@ -130,7 +132,7 @@ trait ForeachTrait
$name = $this->parseIdentifier($node->expr); $name = $this->parseIdentifier($node->expr);
if ($this->hasVar($name)) { if ($this->hasVar($name)) {
$type = $this->getVarType($name); $type = $this->getVarType($name);
if ($type === self::TYPE_OBJECT) { if ($type === Type::OBJECT) {
if ($node->byRef) { if ($node->byRef) {
$this->fatalError($node, 'Cannot use & with foreach'); $this->fatalError($node, 'Cannot use & with foreach');
} }
@ -148,9 +150,9 @@ trait ForeachTrait
$iterableVar = $this->genTmpVarName(); $iterableVar = $this->genTmpVarName();
$arrayVar = $this->genTmpVarName(); $arrayVar = $this->genTmpVarName();
$objectVar = $this->genTmpVarName(); $objectVar = $this->genTmpVarName();
$this->addLocalVar($iterableVar, self::TYPE_VAR); $this->addLocalVar($iterableVar, Type::VAR);
$this->addLocalVar($arrayVar, self::TYPE_ARRAY); $this->addLocalVar($arrayVar, Type::ARRAY);
$this->addLocalVar($objectVar, self::TYPE_OBJECT); $this->addLocalVar($objectVar, Type::OBJECT);
$code .= $iterableVar . ' = ' . $expr . ';' . PHP_EOL; $code .= $iterableVar . ' = ' . $expr . ';' . PHP_EOL;
$code .= 'if (' . $iterableVar . '.isArray()) {' . PHP_EOL; $code .= 'if (' . $iterableVar . '.isArray()) {' . PHP_EOL;

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\CallLike;
@ -24,7 +26,7 @@ trait FunctionCallTrait
[$leftExpr, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr->left); [$leftExpr, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr->left);
$this->appendCapturedStmtLinesToContext($beforeStmts); $this->appendCapturedStmtLinesToContext($beforeStmts);
$value = $this->addTmpVar(self::TYPE_VAR); $value = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $value . ' = ' . $leftExpr . ';'; $this->context->beforeStmtLines[] = $value . ' = ' . $leftExpr . ';';
$this->appendCapturedStmtLinesToContext($afterStmts); $this->appendCapturedStmtLinesToContext($afterStmts);
@ -116,7 +118,7 @@ trait FunctionCallTrait
$fn = $this->getFuncPtr($name); $fn = $this->getFuncPtr($name);
$this->context->beforeStmtLines[] = $this->formatCppLineComment('Func Call: ', $name . '()'); $this->context->beforeStmtLines[] = $this->formatCppLineComment('Func Call: ', $name . '()');
} else { } else {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->name) . ';'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->name) . ';';
$placeHolder = $fn = $tmpVar; $placeHolder = $fn = $tmpVar;
$name = ''; $name = '';

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
trait LoopControlTrait trait LoopControlTrait
@ -56,7 +58,7 @@ trait LoopControlTrait
foreach ($list_cond as [$condExpr, $beforeStmts, $afterStmts]) { foreach ($list_cond as [$condExpr, $beforeStmts, $afterStmts]) {
$this->appendCapturedStmtLines($condCode, $beforeStmts); $this->appendCapturedStmtLines($condCode, $beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$condCode .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL; $condCode .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL;
$this->appendCapturedStmtLines($condCode, $afterStmts); $this->appendCapturedStmtLines($condCode, $afterStmts);
$condExpr = $tmpVar; $condExpr = $tmpVar;
@ -115,7 +117,7 @@ trait LoopControlTrait
$code .= 'while (true) {' . PHP_EOL; $code .= 'while (true) {' . PHP_EOL;
$this->appendCapturedStmtLines($code, $beforeStmts); $this->appendCapturedStmtLines($code, $beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $cond . ';' . PHP_EOL; $code .= $this->getIndent() . $tmpVar . ' = ' . $cond . ';' . PHP_EOL;
$this->appendCapturedStmtLines($code, $afterStmts); $this->appendCapturedStmtLines($code, $afterStmts);
$cond = $tmpVar; $cond = $tmpVar;
@ -141,7 +143,7 @@ trait LoopControlTrait
$condCode = '[&]() -> bool {'; $condCode = '[&]() -> bool {';
$this->appendCapturedStmtLines($condCode, $beforeStmts); $this->appendCapturedStmtLines($condCode, $beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$condCode .= $this->getIndent() . $tmpVar . ' = ' . $cond . ';' . PHP_EOL; $condCode .= $this->getIndent() . $tmpVar . ' = ' . $cond . ';' . PHP_EOL;
$this->appendCapturedStmtLines($condCode, $afterStmts); $this->appendCapturedStmtLines($condCode, $afterStmts);
$cond = $tmpVar; $cond = $tmpVar;

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Modifiers; use PhpParser\Modifiers;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
@ -166,9 +168,9 @@ trait MethodCallTrait
protected function parseNativeMethodCall(string $object, string $nativeFunc, array $args): string protected function parseNativeMethodCall(string $object, string $nativeFunc, array $args): string
{ {
if ($this->getVarType($object) != self::TYPE_OBJECT) { if ($this->getVarType($object) != Type::OBJECT) {
$tmpVar = $this->genTmpVarName(); $tmpVar = $this->genTmpVarName();
$this->context->beforeStmtLines[] = self::TYPE_OBJECT . ' ' . $tmpVar . ' = ' . $object . ';'; $this->context->beforeStmtLines[] = Type::OBJECT . ' ' . $tmpVar . ' = ' . $object . ';';
$object = $tmpVar; $object = $tmpVar;
} }
if (count($args) === 0) { if (count($args) === 0) {
@ -181,35 +183,35 @@ trait MethodCallTrait
{ {
$func = strtolower($this->parseIdentifier($expr->name)); $func = strtolower($this->parseIdentifier($expr->name));
$type = match ($func) { $type = match ($func) {
'int' => self::TYPE_INT, 'int' => Type::INT,
'float' => self::TYPE_FLOAT, 'float' => Type::FLOAT,
'bool' => self::TYPE_BOOL, 'bool' => Type::BOOL,
'bigint' => self::TYPE_BIGINT, 'bigint' => Type::BIGINT,
'decimal' => self::TYPE_DECIMAL, 'decimal' => Type::DECIMAL,
'bigfloat' => self::TYPE_BIGFLOAT, 'bigfloat' => Type::BIGFLOAT,
default => '', default => '',
}; };
if ($type) { if ($type) {
$expr->setAttribute('nativeType', $type); $expr->setAttribute('nativeType', $type);
$valueExpr = $this->parseExpr($expr->args[0]->value); $valueExpr = $this->parseExpr($expr->args[0]->value);
if (in_array($type, [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL])) { if (in_array($type, [Type::INT, Type::FLOAT, Type::BOOL])) {
return $this->convertExprFromType($type, $valueExpr); return $this->convertExprFromType($type, $valueExpr);
} }
$argType = $this->detectTypeOfExpr($expr->args[0]->value); $argType = $this->detectTypeOfExpr($expr->args[0]->value);
if ($argType === $type) { if ($argType === $type) {
return $valueExpr; return $valueExpr;
} }
if ($type === self::TYPE_BIGINT) { if ($type === Type::BIGINT) {
if ($argType === self::TYPE_FLOAT) { if ($argType === Type::FLOAT) {
$this->fatalError($expr, 'Cannot construct BigInt from float, use string or int instead'); $this->fatalError($expr, 'Cannot construct BigInt from float, use string or int instead');
} }
if ($argType === self::TYPE_INT) { if ($argType === Type::INT) {
return 'php::toBigInt(' . $valueExpr . ')'; return 'php::toBigInt(' . $valueExpr . ')';
} }
return 'php::BigInt::newInstance(' . $valueExpr . ')'; return 'php::BigInt::newInstance(' . $valueExpr . ')';
} }
if ($type === self::TYPE_DECIMAL) { if ($type === Type::DECIMAL) {
if ($argType === self::TYPE_FLOAT) { if ($argType === Type::FLOAT) {
$argNode = $expr->args[0]->value; $argNode = $expr->args[0]->value;
if ($argNode instanceof Node\Scalar\Float_) { if ($argNode instanceof Node\Scalar\Float_) {
$rawValue = $argNode->getAttribute('rawValue'); $rawValue = $argNode->getAttribute('rawValue');
@ -218,16 +220,16 @@ trait MethodCallTrait
} }
$this->fatalError($expr, 'Cannot construct Decimal from float variable, use string or int instead'); $this->fatalError($expr, 'Cannot construct Decimal from float variable, use string or int instead');
} }
if ($argType === self::TYPE_INT) { if ($argType === Type::INT) {
return 'php::toDecimal(' . $valueExpr . ')'; return 'php::toDecimal(' . $valueExpr . ')';
} }
return 'php::Decimal::newInstance(' . $valueExpr . ')'; return 'php::Decimal::newInstance(' . $valueExpr . ')';
} }
if ($type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGFLOAT) {
if ($argType === self::TYPE_INT) { if ($argType === Type::INT) {
return 'php::toBigFloat(' . $valueExpr . ')'; return 'php::toBigFloat(' . $valueExpr . ')';
} }
if ($argType === self::TYPE_FLOAT) { if ($argType === Type::FLOAT) {
return 'php::toBigFloat(' . $valueExpr . ')'; return 'php::toBigFloat(' . $valueExpr . ')';
} }
return 'php::BigFloat::newInstance(' . $valueExpr . ')'; return 'php::BigFloat::newInstance(' . $valueExpr . ')';
@ -310,8 +312,8 @@ trait MethodCallTrait
if ($this->isNamedMethod($expr->name)) { if ($this->isNamedMethod($expr->name)) {
$methodName = $expr->name->toString(); $methodName = $expr->name->toString();
$receiverType = $this->isVarExpr($expr->var) ? $this->getVarType($object) : $this->detectTypeOfExpr($expr->var); $receiverType = $this->isVarExpr($expr->var) ? $this->getVarType($object) : $this->detectTypeOfExpr($expr->var);
if ($receiverType === self::TYPE_VOID) { if ($receiverType === Type::VOID) {
$receiverType = self::TYPE_VAR; $receiverType = Type::VAR;
} }
// to* builtins // to* builtins
if (isset(self::KEYWORD_METHOD_MAP[$methodName])) { if (isset(self::KEYWORD_METHOD_MAP[$methodName])) {
@ -337,12 +339,12 @@ trait MethodCallTrait
if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) { if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->getVarType($object); $type = $this->getVarType($object);
// 引用参数允许方法调用:有class信息走原生调用,无class信息走动态调用 // 引用参数允许方法调用:有class信息走原生调用,无class信息走动态调用
if (!$this->checkArgType($type, self::TYPE_OBJECT) and $type !== self::TYPE_REF) { if (!$this->checkArgType($type, Type::OBJECT) and $type !== Type::REF) {
$methodName = $expr->name->toString(); $methodName = $expr->name->toString();
// 非对象类型可使用内置方法 // 非对象类型可使用内置方法
$fn = $this->findUniversalMethodAnyType($type, $methodName); $fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) { if ($fn) {
if ($type === self::TYPE_STREAM) { if ($type === Type::STREAM) {
return $this->genStreamNullGuard($expr, $object, $methodName, $fn); return $this->genStreamNullGuard($expr, $object, $methodName, $fn);
} }
return $this->parseUniversalMethodCall($expr, $object, $methodName, $fn); return $this->parseUniversalMethodCall($expr, $object, $methodName, $fn);
@ -384,10 +386,10 @@ trait MethodCallTrait
// 表达式返回值也可使用内置方法:fn()->method(), $obj->fn()->method(), Foo::fn()->method(), $obj->prop->method() // 表达式返回值也可使用内置方法:fn()->method(), $obj->fn()->method(), Foo::fn()->method(), $obj->prop->method()
if (!$this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) { if (!$this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->detectTypeOfExpr($expr->var); $type = $this->detectTypeOfExpr($expr->var);
if ($type === self::TYPE_VOID) { if ($type === Type::VOID) {
$type = self::TYPE_VAR; $type = Type::VAR;
} }
if ($type !== self::TYPE_VAR && !$this->checkArgType($type, self::TYPE_OBJECT)) { if ($type !== Type::VAR && !$this->checkArgType($type, Type::OBJECT)) {
$methodName = $expr->name->toString(); $methodName = $expr->name->toString();
$fn = $this->findUniversalMethodAnyType($type, $methodName); $fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) { if ($fn) {
@ -397,7 +399,7 @@ trait MethodCallTrait
if ($fn['handler'] === 'direct_method') { if ($fn['handler'] === 'direct_method') {
$receiver = $this->wrapUniversalReceiver($type, $object); $receiver = $this->wrapUniversalReceiver($type, $object);
} }
if ($type === self::TYPE_STREAM) { if ($type === Type::STREAM) {
return $this->genStreamNullGuard($expr, $receiver, $methodName, $fn); return $this->genStreamNullGuard($expr, $receiver, $methodName, $fn);
} }
return $this->parseUniversalMethodCall($expr, $receiver, $methodName, $fn, false); return $this->parseUniversalMethodCall($expr, $receiver, $methodName, $fn, false);
@ -458,7 +460,7 @@ trait MethodCallTrait
$class = $this->getObjectType($var); $class = $this->getObjectType($var);
goto _do_call; goto _do_call;
} }
if ($this->getVarType($var) == self::TYPE_OBJECT) { if ($this->getVarType($var) == Type::OBJECT) {
$fn = 'php::concat({' . $var . '.getClassName(), "::", ' . $this->identifierToStr($expr->name) . '})'; $fn = 'php::concat({' . $var . '.getClassName(), "::", ' . $this->identifierToStr($expr->name) . '})';
} else { } else {
$fn = 'php::concat({' . $this->identifierToStr($expr->class) . ', "::", ' . $this->identifierToStr($expr->name) . '})'; $fn = 'php::concat({' . $this->identifierToStr($expr->class) . ', "::", ' . $this->identifierToStr($expr->name) . '})';

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -59,11 +61,11 @@ trait NullsafeAccessTrait
$this->errorUndefinedVariable($expr); $this->errorUndefinedVariable($expr);
} }
$type = $this->getVarType($object); $type = $this->getVarType($object);
if ($type === self::TYPE_OBJECT) { if ($type === Type::OBJECT) {
break; break;
} }
} }
$object = $this->addTmpVar(self::TYPE_OBJECT); $object = $this->addTmpVar(Type::OBJECT);
$this->context->beforeStmtLines[] = $this->getIndent() . $object . ' = ' . $this->parseIdentifier($expr) . ';'; $this->context->beforeStmtLines[] = $this->getIndent() . $object . ' = ' . $this->parseIdentifier($expr) . ';';
break; break;
} }
@ -74,10 +76,10 @@ trait NullsafeAccessTrait
$last = array_key_last($list); $last = array_key_last($list);
$tmpFn = $this->genTmpVarName(); $tmpFn = $this->genTmpVarName();
$code = $comment . PHP_EOL . 'auto ' . $tmpFn . ' = [&]() -> ' . self::TYPE_VAR . '{' . PHP_EOL; $code = $comment . PHP_EOL . 'auto ' . $tmpFn . ' = [&]() -> ' . Type::VAR . '{' . PHP_EOL;
foreach ($list as $key => $item) { foreach ($list as $key => $item) {
$tmpVar = $this->addTmpVar($key !== $last ? self::TYPE_OBJECT : self::TYPE_VAR); $tmpVar = $this->addTmpVar($key !== $last ? Type::OBJECT : Type::VAR);
if ($item[3]) { if ($item[3]) {
$code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }'; $code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }';
} }
@ -151,7 +153,7 @@ trait NullsafeAccessTrait
$this->detectClassOfExpr($baseExpr), $this->detectClassOfExpr($baseExpr),
$properties, $properties,
$scope, $scope,
self::TYPE_OBJECT, Type::OBJECT,
); );
foreach ($results as $index => $result) { foreach ($results as $index => $result) {
$this->applyNativePropertyAccessResult($properties[$index]['node'], $result); $this->applyNativePropertyAccessResult($properties[$index]['node'], $result);

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -286,7 +288,7 @@ trait PropertyAccessTrait
$this->registerStaticPropertyRef($refVar, $class, $nativeProp, $info); $this->registerStaticPropertyRef($refVar, $class, $nativeProp, $info);
if ($info['kind'] === 'zval') { if ($info['kind'] === 'zval') {
$helper = $def->type === self::TYPE_FLOAT ? 'typephp_static_float_ref' : 'typephp_static_int_ref'; $helper = $def->type === Type::FLOAT ? 'typephp_static_float_ref' : 'typephp_static_int_ref';
return $helper . '(' . $refVar . ')'; return $helper . '(' . $refVar . ')';
} }
@ -329,8 +331,8 @@ trait PropertyAccessTrait
$classValue = $this->getDynamicStaticClassValue($expr->class); $classValue = $this->getDynamicStaticClassValue($expr->class);
$propertyValue = $this->identifierToStr($expr->name, literal: true); $propertyValue = $this->identifierToStr($expr->name, literal: true);
$classVar = $this->addTmpVar(self::TYPE_VAR); $classVar = $this->addTmpVar(Type::VAR);
$propertyVar = $this->addTmpVar(self::TYPE_VAR); $propertyVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $classVar . ' = ' . $classValue . ';'; $this->context->beforeStmtLines[] = $classVar . ' = ' . $classValue . ';';
$this->context->beforeStmtLines[] = $propertyVar . ' = ' . $propertyValue . ';'; $this->context->beforeStmtLines[] = $propertyVar . ' = ' . $propertyValue . ';';
@ -456,7 +458,7 @@ trait PropertyAccessTrait
// Untyped properties retain normal PHP mixed semantics: assigning // Untyped properties retain normal PHP mixed semantics: assigning
// null is valid. Only an explicitly typed non-nullable property // null is valid. Only an explicitly typed non-nullable property
// can be rejected at compile time. // can be rejected at compile time.
if ($def->type !== self::TYPE_VAR && !$def->nullable) { if ($def->type !== Type::VAR && !$def->nullable) {
$typeStr = $this->getObjectPropertyTypeCheckTypeString($def); $typeStr = $this->getObjectPropertyTypeCheckTypeString($def);
$this->fatalError( $this->fatalError(
$left, $left,
@ -467,7 +469,7 @@ trait PropertyAccessTrait
} }
$rightType = $this->detectTypeOfExpr($right); $rightType = $this->detectTypeOfExpr($right);
if ($this->isFixedObjectProp($def) && $rightType !== self::TYPE_VAR) { if ($this->isFixedObjectProp($def) && $rightType !== Type::VAR) {
if (!$this->canAssignStaticTypeToObjectProperty($def, $rightType)) { if (!$this->canAssignStaticTypeToObjectProperty($def, $rightType)) {
$this->fatalError( $this->fatalError(
$left, $left,
@ -479,11 +481,11 @@ trait PropertyAccessTrait
return; return;
} }
if ($def->type !== self::TYPE_OBJECT) { if ($def->type !== Type::OBJECT) {
return; return;
} }
if ($rightType !== self::TYPE_VAR && $rightType !== self::TYPE_OBJECT) { if ($rightType !== Type::VAR && $rightType !== Type::OBJECT) {
$this->fatalError( $this->fatalError(
$left, $left,
"Cannot assign value of type `{$rightType}` to {$label} `{$propName}` of type `{$def->type}`" "Cannot assign value of type `{$rightType}` to {$label} `{$propName}` of type `{$def->type}`"
@ -532,16 +534,16 @@ trait PropertyAccessTrait
'property assignment' 'property assignment'
); );
} }
if ($compositeRelation === self::COMPOSITE_TYPE_MATCH && $rightType !== self::TYPE_VAR) { if ($compositeRelation === self::COMPOSITE_TYPE_MATCH && $rightType !== Type::VAR) {
// A statically known member of the composite type needs no // A statically known member of the composite type needs no
// Variant runtime guard on this property write. // Variant runtime guard on this property write.
return $rightExpr; return $rightExpr;
} }
if ($rightType !== self::TYPE_VAR && $this->canAssignStaticTypeToObjectProperty($def, $rightType)) { if ($rightType !== Type::VAR && $this->canAssignStaticTypeToObjectProperty($def, $rightType)) {
return $rightExpr; return $rightExpr;
} }
if ($rightType === self::TYPE_VAR && ($helper = $this->getNativeScalarPropertyTypeCheckHelper($def)) !== null) { if ($rightType === Type::VAR && ($helper = $this->getNativeScalarPropertyTypeCheckHelper($def)) !== null) {
return $helper . '(' . $rightExpr . ', ' . $this->genCharPtr($this->getObjectPropertyTypeCheckDisplayName($left)) . ')'; return $helper . '(' . $rightExpr . ', ' . $this->genCharPtr($this->getObjectPropertyTypeCheckDisplayName($left)) . ')';
} }
@ -550,7 +552,7 @@ trait PropertyAccessTrait
return $rightExpr; return $rightExpr;
} }
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$conditions = []; $conditions = [];
foreach ($typeCheck as $entry) { foreach ($typeCheck as $entry) {
$cond = $this->genSingleTypeCondition($tmpVar, $entry); $cond = $this->genSingleTypeCondition($tmpVar, $entry);
@ -577,7 +579,7 @@ trait PropertyAccessTrait
? 'if (' . $tmpVar . '.isInt()) { ' . $tmpVar . ' = php::toFloat(' . $tmpVar . '); } ' ? 'if (' . $tmpVar . '.isInt()) { ' . $tmpVar . ' = php::toFloat(' . $tmpVar . '); } '
: ''; : '';
return '([&]() -> ' . self::TYPE_VAR . ' { ' return '([&]() -> ' . Type::VAR . ' { '
. $tmpVar . ' = ' . $rightExpr . '; ' . $tmpVar . ' = ' . $rightExpr . '; '
. $coercion . $coercion
. 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) { ' . 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) { '
@ -616,11 +618,11 @@ trait PropertyAccessTrait
private function usesPhpStylePropertyAssignTypeError(PropertyDef $def): bool private function usesPhpStylePropertyAssignTypeError(PropertyDef $def): bool
{ {
return empty($def->typeCheck) && $def->class === '' && in_array($def->type, [ return empty($def->typeCheck) && $def->class === '' && in_array($def->type, [
self::TYPE_INT, Type::INT,
self::TYPE_FLOAT, Type::FLOAT,
self::TYPE_BOOL, Type::BOOL,
self::TYPE_STR, Type::STR,
self::TYPE_ARRAY, Type::ARRAY,
], true); ], true);
} }
@ -631,9 +633,9 @@ trait PropertyAccessTrait
} }
return match ($def->type) { return match ($def->type) {
self::TYPE_INT => 'php::toIntExact', Type::INT => 'php::toIntExact',
self::TYPE_FLOAT => 'php::toFloatExact', Type::FLOAT => 'php::toFloatExact',
self::TYPE_BOOL => 'php::toBoolExact', Type::BOOL => 'php::toBoolExact',
default => null, default => null,
}; };
} }
@ -641,7 +643,7 @@ trait PropertyAccessTrait
protected function canAssignStaticTypeToObjectProperty(PropertyDef $def, string $rightType): bool protected function canAssignStaticTypeToObjectProperty(PropertyDef $def, string $rightType): bool
{ {
return match ($def->type) { return match ($def->type) {
self::TYPE_FLOAT => $rightType === self::TYPE_FLOAT || $rightType === self::TYPE_INT, Type::FLOAT => $rightType === Type::FLOAT || $rightType === Type::INT,
default => $rightType === $def->type, default => $rightType === $def->type,
}; };
} }
@ -649,12 +651,12 @@ trait PropertyAccessTrait
protected function getPropertyAssignmentTypeName(string $type): string protected function getPropertyAssignmentTypeName(string $type): string
{ {
return match ($type) { return match ($type) {
self::TYPE_INT => 'int', Type::INT => 'int',
self::TYPE_FLOAT => 'float', Type::FLOAT => 'float',
self::TYPE_BOOL => 'bool', Type::BOOL => 'bool',
self::TYPE_STR => 'string', Type::STR => 'string',
self::TYPE_ARRAY => 'array', Type::ARRAY => 'array',
self::TYPE_OBJECT => 'object', Type::OBJECT => 'object',
default => 'value', default => 'value',
}; };
} }
@ -692,7 +694,7 @@ trait PropertyAccessTrait
// properties, so PHP can represent their uninitialized // properties, so PHP can represent their uninitialized
// state after unset(). Keep that behavior instead of // state after unset(). Keep that behavior instead of
// restoring a fixed default value. // restoring a fixed default value.
if ($this->isFixedObjectProp($def) && $def->type !== self::TYPE_OBJECT) { if ($this->isFixedObjectProp($def) && $def->type !== Type::OBJECT) {
$restoreDefault = $this->getFixedObjectPropDefaultValue($def); $restoreDefault = $this->getFixedObjectPropDefaultValue($def);
if ($restoreDefault === null) { if ($restoreDefault === null) {
$this->fatalError($var, "Cannot unset object property `{$this->parseIdentifier($var->name)}` of fixed type `{$def->type}` without default value"); $this->fatalError($var, "Cannot unset object property `{$this->parseIdentifier($var->name)}` of fixed type `{$def->type}` without default value");
@ -885,11 +887,11 @@ trait PropertyAccessTrait
PropertyDef $def, PropertyDef $def,
string $getter, string $getter,
): ?string { ): ?string {
if ($this->isPropertyFetchUpdate($expr) && !in_array($def->type, [self::TYPE_INT, self::TYPE_FLOAT], true)) { if ($this->isPropertyFetchUpdate($expr) && !in_array($def->type, [Type::INT, Type::FLOAT], true)) {
return null; return null;
} }
if ($def->type === self::TYPE_BOOL) { if ($def->type === Type::BOOL) {
$this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC); $this->setNativePropertyValueSource($expr, self::NATIVE_PROPERTY_VALUE_DYNAMIC);
return $this->convertBoolExpr($getter); return $this->convertBoolExpr($getter);
} }

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -44,10 +46,10 @@ trait SelectionExpressionTrait
$else = 'php::Var(' . $else . ')'; $else = 'php::Var(' . $else . ')';
} }
if ($hasBranchStmts) { if ($hasBranchStmts) {
$code = '[&]() -> ' . self::TYPE_VAR . '{'; $code = '[&]() -> ' . Type::VAR . '{';
$code .= $this->formatCapturedStmtLines($condBeforeStmts); $code .= $this->formatCapturedStmtLines($condBeforeStmts);
if ($condAfterStmts) { if ($condAfterStmts) {
$condTmpVar = $this->addTmpVar(self::TYPE_VAR); $condTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . "{$condTmpVar} = {$cond};"; $code .= $this->getIndent() . "{$condTmpVar} = {$cond};";
$code .= $this->formatCapturedStmtLines($condAfterStmts); $code .= $this->formatCapturedStmtLines($condAfterStmts);
$cond = $condTmpVar; $cond = $condTmpVar;
@ -67,7 +69,7 @@ trait SelectionExpressionTrait
{ {
$code = $this->formatCapturedStmtLines($beforeStmts); $code = $this->formatCapturedStmtLines($beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . "{$tmpVar} = {$value};"; $code .= $this->getIndent() . "{$tmpVar} = {$value};";
$code .= $this->formatCapturedStmtLines($afterStmts); $code .= $this->formatCapturedStmtLines($afterStmts);
$code .= $this->getIndent() . 'return ' . $tmpVar . ';'; $code .= $this->getIndent() . 'return ' . $tmpVar . ';';
@ -86,12 +88,12 @@ trait SelectionExpressionTrait
$this->errorUndefinedVariable($expr->cond); $this->errorUndefinedVariable($expr->cond);
} }
} else { } else {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $var . ';'; $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $var . ';';
$var = $tmpVar; $var = $tmpVar;
} }
$code = '[&]() -> ' . self::TYPE_VAR . '{'; $code = '[&]() -> ' . Type::VAR . '{';
$default = null; $default = null;
foreach ($expr->arms as $arm) { foreach ($expr->arms as $arm) {
if ($arm->conds === null) { if ($arm->conds === null) {
@ -109,7 +111,7 @@ trait SelectionExpressionTrait
$code .= $this->getIndent() . 'if (!' . $matched . ') {'; $code .= $this->getIndent() . 'if (!' . $matched . ') {';
$code .= $this->formatCapturedStmtLines($beforeStmts); $code .= $this->formatCapturedStmtLines($beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$condTmpVar = $this->addTmpVar(self::TYPE_VAR); $condTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . "{$condTmpVar} = {$condValue};"; $code .= $this->getIndent() . "{$condTmpVar} = {$condValue};";
$code .= $this->formatCapturedStmtLines($afterStmts); $code .= $this->formatCapturedStmtLines($afterStmts);
$condValue = $condTmpVar; $condValue = $condTmpVar;
@ -140,7 +142,7 @@ trait SelectionExpressionTrait
[$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($body); [$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($body);
$code = $this->formatCapturedStmtLines($beforeStmts); $code = $this->formatCapturedStmtLines($beforeStmts);
if ($afterStmts) { if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . "{$tmpVar} = {$value};"; $code .= $this->getIndent() . "{$tmpVar} = {$value};";
$code .= $this->formatCapturedStmtLines($afterStmts); $code .= $this->formatCapturedStmtLines($afterStmts);
$code .= $this->getIndent() . 'return ' . $tmpVar . ';'; $code .= $this->getIndent() . 'return ' . $tmpVar . ';';
@ -175,7 +177,7 @@ trait SelectionExpressionTrait
$this->context->afterStmtLines = array_slice($this->context->afterStmtLines, 0, $rightAfterStmtCount); $this->context->afterStmtLines = array_slice($this->context->afterStmtLines, 0, $rightAfterStmtCount);
$this->checkVarMustExist($right, $rightExpr); $this->checkVarMustExist($right, $rightExpr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
if ($rightBeforeStmts || $rightAfterStmts) { if ($rightBeforeStmts || $rightAfterStmts) {
$code = $this->formatCppLineComment('Expr: ', $this->printer->prettyPrintExpr($expr)) . PHP_EOL . $code = $this->formatCppLineComment('Expr: ', $this->printer->prettyPrintExpr($expr)) . PHP_EOL .
'if (' . $condExpr . ') {' . PHP_EOL . 'if (' . $condExpr . ') {' . PHP_EOL .
@ -185,7 +187,7 @@ trait SelectionExpressionTrait
$code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $rightBeforeStmts) . PHP_EOL; $code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $rightBeforeStmts) . PHP_EOL;
} }
if ($rightAfterStmts) { if ($rightAfterStmts) {
$rightTmpVar = $this->addTmpVar(self::TYPE_VAR); $rightTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';' . PHP_EOL; $code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';' . PHP_EOL;
$code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $rightAfterStmts) . PHP_EOL; $code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $rightAfterStmts) . PHP_EOL;
$code .= $this->getIndent() . $tmpVar . ' = ' . $rightTmpVar . ';' . PHP_EOL; $code .= $this->getIndent() . $tmpVar . ' = ' . $rightTmpVar . ';' . PHP_EOL;

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\Generator\Symbol; use TypePhp\Generator\Symbol;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticCall;
@ -26,31 +28,31 @@ trait StdContainerTrait
protected function isStdContainerType(string $type): bool protected function isStdContainerType(string $type): bool
{ {
return in_array($type, [ return in_array($type, [
self::TYPE_STD_ARRAY, Type::STD_ARRAY,
self::TYPE_STD_VECTOR, Type::STD_VECTOR,
self::TYPE_STD_MAP, Type::STD_MAP,
self::TYPE_STD_ORDERED_MAP, Type::STD_ORDERED_MAP,
], true); ], true);
} }
protected function isStdArray(string $var): bool protected function isStdArray(string $var): bool
{ {
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_ARRAY; return $this->hasLocalVar($var) and $this->getVarType($var) === Type::STD_ARRAY;
} }
protected function isStdVector(string $var): bool protected function isStdVector(string $var): bool
{ {
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_VECTOR; return $this->hasLocalVar($var) and $this->getVarType($var) === Type::STD_VECTOR;
} }
protected function isStdMap(string $var): bool protected function isStdMap(string $var): bool
{ {
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_MAP; return $this->hasLocalVar($var) and $this->getVarType($var) === Type::STD_MAP;
} }
protected function isStdOrderedMap(string $var): bool protected function isStdOrderedMap(string $var): bool
{ {
return $this->hasLocalVar($var) and $this->getVarType($var) === self::TYPE_STD_ORDERED_MAP; return $this->hasLocalVar($var) and $this->getVarType($var) === Type::STD_ORDERED_MAP;
} }
protected function getStdTypeKey(array $info): string protected function getStdTypeKey(array $info): string
@ -84,7 +86,7 @@ trait StdContainerTrait
protected function getStdContainerKeyType(string $var): string protected function getStdContainerKeyType(string $var): string
{ {
if ($this->isStdVector($var) or $this->isStdArray($var)) { if ($this->isStdVector($var) or $this->isStdArray($var)) {
return self::TYPE_INT; return Type::INT;
} }
return $this->getStdContainerVarInfo($var)['keyType']; return $this->getStdContainerVarInfo($var)['keyType'];
} }
@ -93,9 +95,9 @@ trait StdContainerTrait
{ {
$info = $this->getStdContainerVarInfo($var); $info = $this->getStdContainerVarInfo($var);
if ($this->isStdArray($var)) { if ($this->isStdArray($var)) {
return count($info['sizes']) > 1 ? self::TYPE_ARRAY : $info['type']; return count($info['sizes']) > 1 ? Type::ARRAY : $info['type'];
} }
if ($info['type'] === self::TYPE_OBJECT and $info['class']) { if ($info['type'] === Type::OBJECT and $info['class']) {
$this->addObject($valueVar, $info['class']); $this->addObject($valueVar, $info['class']);
} else { } else {
unset($this->context->objects[$valueVar]); unset($this->context->objects[$valueVar]);
@ -105,7 +107,7 @@ trait StdContainerTrait
protected function getStdArrayDecl(string $type, array $sizes): string protected function getStdArrayDecl(string $type, array $sizes): string
{ {
$decl = str_repeat(self::TYPE_STD_ARRAY . '<', count($sizes)); $decl = str_repeat(Type::STD_ARRAY . '<', count($sizes));
$decl .= $this->getStdContainerElementType($type); $decl .= $this->getStdContainerElementType($type);
for ($i = count($sizes) - 1; $i >= 0; $i--) { for ($i = count($sizes) - 1; $i >= 0; $i--) {
$decl .= ', ' . $sizes[$i] . '>'; $decl .= ', ' . $sizes[$i] . '>';
@ -116,8 +118,8 @@ trait StdContainerTrait
protected function getStdValueTypeBytes(string $type): int protected function getStdValueTypeBytes(string $type): int
{ {
return match ($type) { return match ($type) {
self::TYPE_BOOL => 1, Type::BOOL => 1,
self::TYPE_INT, self::TYPE_FLOAT => 8, Type::INT, Type::FLOAT => 8,
default => 16, default => 16,
}; };
} }
@ -451,7 +453,7 @@ trait StdContainerTrait
protected function convertStdContainerKey(array $info, string $index): string protected function convertStdContainerKey(array $info, string $index): string
{ {
if ($info['keyType'] === self::TYPE_STR) { if ($info['keyType'] === Type::STR) {
return $this->convertStringExpr($index); return $this->convertStringExpr($index);
} }
return $this->convertIntExpr($index); return $this->convertIntExpr($index);
@ -460,7 +462,7 @@ trait StdContainerTrait
protected function getStdContainerElementType(string $type): string protected function getStdContainerElementType(string $type): string
{ {
return match ($type) { return match ($type) {
self::TYPE_BIGINT, self::TYPE_BIGFLOAT, self::TYPE_DECIMAL, self::TYPE_STREAM, self::TYPE_BOX => self::TYPE_VAR, Type::BIGINT, Type::BIGFLOAT, Type::DECIMAL, Type::STREAM, Type::BOX => Type::VAR,
default => $type, default => $type,
}; };
} }
@ -474,12 +476,12 @@ trait StdContainerTrait
$this->fatalError($expr, "An incorrect `{$owner}` definition"); $this->fatalError($expr, "An incorrect `{$owner}` definition");
} }
return match (strtolower($expr->name->name)) { return match (strtolower($expr->name->name)) {
'type_int' => self::TYPE_INT, 'type_int' => Type::INT,
'type_float' => self::TYPE_FLOAT, 'type_float' => Type::FLOAT,
'type_bool' => self::TYPE_BOOL, 'type_bool' => Type::BOOL,
'type_bigint' => self::TYPE_BIGINT, 'type_bigint' => Type::BIGINT,
'type_bigfloat' => self::TYPE_BIGFLOAT, 'type_bigfloat' => Type::BIGFLOAT,
'type_decimal' => self::TYPE_DECIMAL, 'type_decimal' => Type::DECIMAL,
default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), default => $this->fatalError($expr, "An incorrect `{$owner}` definition"),
}; };
} }
@ -499,12 +501,12 @@ trait StdContainerTrait
if ($className === 'complex_types') { if ($className === 'complex_types') {
return [ return [
'type' => match (strtolower($expr->name->name)) { 'type' => match (strtolower($expr->name->name)) {
'type_str', 'type_string' => self::TYPE_STR, 'type_str', 'type_string' => Type::STR,
'type_array' => self::TYPE_ARRAY, 'type_array' => Type::ARRAY,
'type_object' => self::TYPE_OBJECT, 'type_object' => Type::OBJECT,
'type_any', 'type_var', 'type_variant' => self::TYPE_VAR, 'type_any', 'type_var', 'type_variant' => Type::VAR,
'type_stream' => self::TYPE_STREAM, 'type_stream' => Type::STREAM,
'type_box' => self::TYPE_BOX, 'type_box' => Type::BOX,
default => $this->fatalError($expr, "An incorrect `{$owner}` definition"), default => $this->fatalError($expr, "An incorrect `{$owner}` definition"),
}, },
'class' => null, 'class' => null,
@ -514,7 +516,7 @@ trait StdContainerTrait
$this->fatalError($expr, "{$owner} class value only supports ClassName::class"); $this->fatalError($expr, "{$owner} class value only supports ClassName::class");
} }
$class = $this->parseStdClassValueType($expr, $owner); $class = $this->parseStdClassValueType($expr, $owner);
return ['type' => self::TYPE_OBJECT, 'class' => $class]; return ['type' => Type::OBJECT, 'class' => $class];
} }
protected function parseStdValueType(NodeAbstract $expr, string $owner): string protected function parseStdValueType(NodeAbstract $expr, string $owner): string
@ -550,7 +552,7 @@ trait StdContainerTrait
$class = $info['class'] ?? null; $class = $info['class'] ?? null;
if ($class === null) { if ($class === null) {
$targetType = $info['type']; $targetType = $info['type'];
if ($targetType === self::TYPE_BIGINT || $targetType === self::TYPE_BIGFLOAT || $targetType === self::TYPE_DECIMAL || $targetType === self::TYPE_STREAM || $targetType === self::TYPE_BOX) { if ($targetType === Type::BIGINT || $targetType === Type::BIGFLOAT || $targetType === Type::DECIMAL || $targetType === Type::STREAM || $targetType === Type::BOX) {
return $this->convertStdVarBackedExpr($targetType, $valueExpr, $expr); return $this->convertStdVarBackedExpr($targetType, $valueExpr, $expr);
} }
return $this->convertExprFromType($targetType, $valueExpr); return $this->convertExprFromType($targetType, $valueExpr);
@ -571,16 +573,16 @@ trait StdContainerTrait
if ($sourceType === $targetType) { if ($sourceType === $targetType) {
return $valueExpr; return $valueExpr;
} }
if ($targetType === self::TYPE_STREAM || $targetType === self::TYPE_BOX) { if ($targetType === Type::STREAM || $targetType === Type::BOX) {
return $valueExpr; return $valueExpr;
} }
if ($targetType === self::TYPE_BIGINT) { if ($targetType === Type::BIGINT) {
return $this->convertBigIntExpr($valueExpr, $sourceType); return $this->convertBigIntExpr($valueExpr, $sourceType);
} }
if ($targetType === self::TYPE_BIGFLOAT) { if ($targetType === Type::BIGFLOAT) {
return $this->convertBigFloatExpr($valueExpr, $sourceType); return $this->convertBigFloatExpr($valueExpr, $sourceType);
} }
if ($targetType === self::TYPE_DECIMAL) { if ($targetType === Type::DECIMAL) {
return $this->convertDecimalExpr($valueExpr, $sourceType, $expr); return $this->convertDecimalExpr($valueExpr, $sourceType, $expr);
} }
return $valueExpr; return $valueExpr;
@ -609,20 +611,20 @@ trait StdContainerTrait
$fakeCall = new StaticCall($name, $method, $expr->args); $fakeCall = new StaticCall($name, $method, $expr->args);
if ($containerType === 'array') { if ($containerType === 'array') {
$this->addLocalVar($var, self::TYPE_STD_ARRAY); $this->addLocalVar($var, Type::STD_ARRAY);
$this->parseStdArray($var, $fakeCall); $this->parseStdArray($var, $fakeCall);
$this->context->stdArrays[$var]['boxExpr'] = $sourceVar; $this->context->stdArrays[$var]['boxExpr'] = $sourceVar;
return '// StdContainer<' . $this->context->stdArrays[$var]['decl'] . '>(' . $sourceVar . ')'; return '// StdContainer<' . $this->context->stdArrays[$var]['decl'] . '>(' . $sourceVar . ')';
} }
if ($containerType === 'vector') { if ($containerType === 'vector') {
$this->addLocalVar($var, self::TYPE_STD_VECTOR); $this->addLocalVar($var, Type::STD_VECTOR);
$this->parseStdVector($var, $fakeCall); $this->parseStdVector($var, $fakeCall);
} elseif ($containerType === 'map') { } elseif ($containerType === 'map') {
$this->addLocalVar($var, self::TYPE_STD_MAP); $this->addLocalVar($var, Type::STD_MAP);
$this->parseStdMap($var, $fakeCall); $this->parseStdMap($var, $fakeCall);
} else { } else {
$this->addLocalVar($var, self::TYPE_STD_ORDERED_MAP); $this->addLocalVar($var, Type::STD_ORDERED_MAP);
$this->parseStdOrderedMap($var, $fakeCall); $this->parseStdOrderedMap($var, $fakeCall);
} }
$this->context->stdContainers[$var]['boxExpr'] = $sourceVar; $this->context->stdContainers[$var]['boxExpr'] = $sourceVar;
@ -637,10 +639,10 @@ trait StdContainerTrait
$className = strtolower($expr->class->toString()); $className = strtolower($expr->class->toString());
$constName = strtolower($expr->name->name); $constName = strtolower($expr->name->name);
if ($className === 'native_types' && $constName === 'type_int') { if ($className === 'native_types' && $constName === 'type_int') {
return self::TYPE_INT; return Type::INT;
} }
if ($className === 'complex_types' && in_array($constName, ['type_string', 'type_str'], true)) { if ($className === 'complex_types' && in_array($constName, ['type_string', 'type_str'], true)) {
return self::TYPE_STR; 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 native_types::type_int, complex_types::type_string or complex_types::type_str");
} }
@ -710,7 +712,7 @@ trait StdContainerTrait
} }
$size = $expr->args[1]->value->value; $size = $expr->args[1]->value->value;
} }
$decl = self::TYPE_STD_VECTOR . '<' . $this->getStdContainerElementType($type) . '>'; $decl = Type::STD_VECTOR . '<' . $this->getStdContainerElementType($type) . '>';
$this->context->stdContainers[$var] = $this->addStdTypeId([ $this->context->stdContainers[$var] = $this->addStdTypeId([
'kind' => 'vector', 'kind' => 'vector',
'decl' => $decl, 'decl' => $decl,
@ -723,12 +725,12 @@ trait StdContainerTrait
protected function parseStdMap(string $var, Expr\StaticCall $expr): string protected function parseStdMap(string $var, Expr\StaticCall $expr): string
{ {
return $this->parseStdMapBase($var, $expr, 'std::map', self::TYPE_STD_MAP, 'map'); return $this->parseStdMapBase($var, $expr, 'std::map', Type::STD_MAP, 'map');
} }
protected function parseStdOrderedMap(string $var, Expr\StaticCall $expr): string protected function parseStdOrderedMap(string $var, Expr\StaticCall $expr): string
{ {
return $this->parseStdMapBase($var, $expr, 'std::ordered_map', self::TYPE_STD_ORDERED_MAP, 'ordered_map'); return $this->parseStdMapBase($var, $expr, 'std::ordered_map', Type::STD_ORDERED_MAP, 'ordered_map');
} }
private function parseStdMapBase(string $var, Expr\StaticCall $expr, string $funcName, string $containerType, string $kind): string private function parseStdMapBase(string $var, Expr\StaticCall $expr, string $funcName, string $containerType, string $kind): string

@ -7,6 +7,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
trait SwitchTrait trait SwitchTrait
@ -30,7 +32,7 @@ trait SwitchTrait
$localVars = $this->context->localVars; $localVars = $this->context->localVars;
$code = $this->parseBeforeStmtLines() . PHP_EOL; $code = $this->parseBeforeStmtLines() . PHP_EOL;
if ($type === self::TYPE_INT or $type === self::TYPE_BOOL) { if ($type === Type::INT or $type === Type::BOOL) {
$code .= 'do {' . PHP_EOL; $code .= 'do {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$code .= $this->getIndent() . 'switch (' . $tmp_var . ') {' . PHP_EOL; $code .= $this->getIndent() . 'switch (' . $tmp_var . ') {' . PHP_EOL;
@ -118,7 +120,7 @@ trait SwitchTrait
$code .= $this->getIndent() . 'if (!' . $switchMatched . ' && !' . $groupMatched . ') {' . PHP_EOL; $code .= $this->getIndent() . 'if (!' . $switchMatched . ' && !' . $groupMatched . ') {' . PHP_EOL;
$this->appendCapturedStmtLines($code, $caseBeforeStmts); $this->appendCapturedStmtLines($code, $caseBeforeStmts);
if ($caseAfterStmts) { if ($caseAfterStmts) {
$caseTmpVar = $this->addTmpVar(self::TYPE_VAR); $caseTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $caseTmpVar . ' = ' . $caseCondExpr . ';' . PHP_EOL; $code .= $this->getIndent() . $caseTmpVar . ' = ' . $caseCondExpr . ';' . PHP_EOL;
$this->appendCapturedStmtLines($code, $caseAfterStmts); $this->appendCapturedStmtLines($code, $caseAfterStmts);
$caseCondExpr = $caseTmpVar; $caseCondExpr = $caseTmpVar;

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -15,13 +17,13 @@ trait TypeConversionTrait
{ {
protected function convertExprToStringByType(string $expr, $type): string protected function convertExprToStringByType(string $expr, $type): string
{ {
if ($type === self::TYPE_BIGINT) { if ($type === Type::BIGINT) {
return 'php::BigInt::toString(' . $expr . ')'; return 'php::BigInt::toString(' . $expr . ')';
} }
if ($type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGFLOAT) {
return 'php::BigFloat::toString(' . $expr . ')'; return 'php::BigFloat::toString(' . $expr . ')';
} }
if ($type === self::TYPE_DECIMAL) { if ($type === Type::DECIMAL) {
return 'php::Decimal::toString(' . $expr . ')'; return 'php::Decimal::toString(' . $expr . ')';
} }
return $this->convertStringExpr($expr); return $this->convertStringExpr($expr);
@ -47,7 +49,7 @@ trait TypeConversionTrait
protected function convertDecimalExpr(string $expr, string $fromType = '', ?NodeAbstract $node = null): string protected function convertDecimalExpr(string $expr, string $fromType = '', ?NodeAbstract $node = null): string
{ {
if ($fromType === self::TYPE_FLOAT) { if ($fromType === Type::FLOAT) {
if ($node instanceof Node\Scalar\Float_) { if ($node instanceof Node\Scalar\Float_) {
$rawValue = $node->getAttribute('rawValue'); $rawValue = $node->getAttribute('rawValue');
$clean = $rawValue !== null ? $this->stripNumericUnderscores($rawValue) : (string) $node->value; $clean = $rawValue !== null ? $this->stripNumericUnderscores($rawValue) : (string) $node->value;
@ -55,16 +57,16 @@ trait TypeConversionTrait
} }
$this->fatalError($node, 'Cannot convert float expression to Decimal, use a literal value or string instead'); $this->fatalError($node, 'Cannot convert float expression to Decimal, use a literal value or string instead');
} }
if ($fromType === self::TYPE_STR) { if ($fromType === Type::STR) {
if ($node instanceof Node\Scalar\String_) { if ($node instanceof Node\Scalar\String_) {
return 'php::toDecimal(' . $this->getLiteralString($node->value) . ')'; return 'php::toDecimal(' . $this->getLiteralString($node->value) . ')';
} }
return 'php::toDecimal(php::toString(' . $expr . '))'; return 'php::toDecimal(php::toString(' . $expr . '))';
} }
if ($fromType === self::TYPE_INT) { if ($fromType === Type::INT) {
return 'php::toDecimal(php::toString(' . $expr . '))'; return 'php::toDecimal(php::toString(' . $expr . '))';
} }
if ($fromType === self::TYPE_BIGINT) { if ($fromType === Type::BIGINT) {
return 'php::toDecimal(php::BigInt::toString(' . $expr . '))'; return 'php::toDecimal(php::BigInt::toString(' . $expr . '))';
} }
return $expr; return $expr;
@ -72,13 +74,13 @@ trait TypeConversionTrait
protected function convertBigIntExpr(string $expr, string $fromType = ''): string protected function convertBigIntExpr(string $expr, string $fromType = ''): string
{ {
if ($fromType === self::TYPE_INT) { if ($fromType === Type::INT) {
return 'php::toBigInt(' . $expr . ')'; return 'php::toBigInt(' . $expr . ')';
} }
if ($fromType === self::TYPE_FLOAT) { if ($fromType === Type::FLOAT) {
$this->error('Cannot convert float to BigInt, use string or int instead'); $this->error('Cannot convert float to BigInt, use string or int instead');
} }
if ($fromType === self::TYPE_STR) { if ($fromType === Type::STR) {
return 'php::toBigInt(php::toString(' . $expr . '))'; return 'php::toBigInt(php::toString(' . $expr . '))';
} }
return $expr; return $expr;
@ -86,19 +88,19 @@ trait TypeConversionTrait
protected function convertBigFloatExpr(string $expr, string $fromType = ''): string protected function convertBigFloatExpr(string $expr, string $fromType = ''): string
{ {
if ($fromType === self::TYPE_INT) { if ($fromType === Type::INT) {
return 'php::toBigFloat(' . $expr . ')'; return 'php::toBigFloat(' . $expr . ')';
} }
if ($fromType === self::TYPE_FLOAT) { if ($fromType === Type::FLOAT) {
return 'php::toBigFloat(' . $expr . ')'; return 'php::toBigFloat(' . $expr . ')';
} }
if ($fromType === self::TYPE_STR) { if ($fromType === Type::STR) {
return 'php::toBigFloat(php::toString(' . $expr . '))'; return 'php::toBigFloat(php::toString(' . $expr . '))';
} }
if ($fromType === self::TYPE_BIGINT) { if ($fromType === Type::BIGINT) {
return 'php::BigFloat::newInstance(php::BigInt::toString(' . $expr . '))'; return 'php::BigFloat::newInstance(php::BigInt::toString(' . $expr . '))';
} }
if ($fromType === self::TYPE_DECIMAL) { if ($fromType === Type::DECIMAL) {
return 'php::BigFloat::newInstance(php::Decimal::toString(' . $expr . '))'; return 'php::BigFloat::newInstance(php::Decimal::toString(' . $expr . '))';
} }
return $expr; return $expr;
@ -145,13 +147,13 @@ trait TypeConversionTrait
protected function convertExprType(string $expr, $leftType, $rightType): string protected function convertExprType(string $expr, $leftType, $rightType): string
{ {
if ($leftType === self::TYPE_FLOAT or $rightType === self::TYPE_FLOAT) { if ($leftType === Type::FLOAT or $rightType === Type::FLOAT) {
return $this->convertFloatExpr($expr); return $this->convertFloatExpr($expr);
} }
if ($leftType === self::TYPE_INT or $rightType === self::TYPE_INT) { if ($leftType === Type::INT or $rightType === Type::INT) {
return $this->convertIntExpr($expr); return $this->convertIntExpr($expr);
} }
if ($leftType === self::TYPE_BOOL or $rightType === self::TYPE_BOOL) { if ($leftType === Type::BOOL or $rightType === Type::BOOL) {
return $this->convertBoolExpr($expr); return $this->convertBoolExpr($expr);
} }
@ -160,33 +162,33 @@ trait TypeConversionTrait
protected function getNativeType(string $type): string protected function getNativeType(string $type): string
{ {
if ($type === self::TYPE_INT && $this->bigintTypes) { if ($type === Type::INT && $this->bigintTypes) {
return self::TYPE_BIGINT; return Type::BIGINT;
} }
if ($type === self::TYPE_FLOAT && $this->decimalTypes) { if ($type === Type::FLOAT && $this->decimalTypes) {
return self::TYPE_DECIMAL; return Type::DECIMAL;
} }
return $this->nativeTypes ? $type : self::TYPE_VAR; return $this->nativeTypes ? $type : Type::VAR;
} }
protected function convertExprFromType(string $type, string $expr): string protected function convertExprFromType(string $type, string $expr): string
{ {
if ($type === self::TYPE_FLOAT) { if ($type === Type::FLOAT) {
return $this->convertFloatExpr($expr); return $this->convertFloatExpr($expr);
} }
if ($type === self::TYPE_INT) { if ($type === Type::INT) {
return $this->convertIntExpr($expr); return $this->convertIntExpr($expr);
} }
if ($type === self::TYPE_BOOL) { if ($type === Type::BOOL) {
return $this->convertBoolExpr($expr); return $this->convertBoolExpr($expr);
} }
if ($type === self::TYPE_STR) { if ($type === Type::STR) {
return $this->convertStringExpr($expr); return $this->convertStringExpr($expr);
} }
if ($type === self::TYPE_ARRAY) { if ($type === Type::ARRAY) {
return $this->convertArrayExpr($expr); return $this->convertArrayExpr($expr);
} }
if ($type === self::TYPE_OBJECT) { if ($type === Type::OBJECT) {
return $this->convertObjectExpr($expr); return $this->convertObjectExpr($expr);
} }
@ -207,7 +209,7 @@ trait TypeConversionTrait
$this->checkLeftValue($expr); $this->checkLeftValue($expr);
$var = $this->parseIdentifier($expr); $var = $this->parseIdentifier($expr);
if ($this->isVarExpr($expr) and $this->isNativeTypeVar($var)) { if ($this->isVarExpr($expr) and $this->isNativeTypeVar($var)) {
$this->context->localVars[$var] = self::TYPE_VAR; $this->context->localVars[$var] = Type::VAR;
} }
return $this->parseIdentifier($expr) . '.toReference()'; return $this->parseIdentifier($expr) . '.toReference()';
} }

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\Resolver\Reflection; use TypePhp\Resolver\Reflection;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
@ -76,7 +78,7 @@ trait TypeDetectionTrait
protected function isNativeType(string $type): bool protected function isNativeType(string $type): bool
{ {
return in_array($type, [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL]); return in_array($type, [Type::INT, Type::FLOAT, Type::BOOL]);
} }
protected function isNativeTypeVar(string $var): bool protected function isNativeTypeVar(string $var): bool
@ -140,7 +142,7 @@ trait TypeDetectionTrait
protected function isArrayVar($var): bool protected function isArrayVar($var): bool
{ {
return $this->isVarExpr($var) and $this->hasVar($var->name) and $this->getVarType($var->name) === self::TYPE_ARRAY; return $this->isVarExpr($var) and $this->hasVar($var->name) and $this->getVarType($var->name) === Type::ARRAY;
} }
} }

@ -8,6 +8,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
trait UnaryExpressionTrait trait UnaryExpressionTrait
@ -16,7 +18,7 @@ trait UnaryExpressionTrait
{ {
$type = $this->detectTypeOfExpr($expr->expr); $type = $this->detectTypeOfExpr($expr->expr);
$this->assertExprCanBeUsedAsValue($expr->expr, 'bitwise operand'); $this->assertExprCanBeUsedAsValue($expr->expr, 'bitwise operand');
if ($type === self::TYPE_BIGINT) { if ($type === Type::BIGINT) {
return 'php::BigInt::bitNot(' . $this->parseExpr($expr->expr) . ')'; return 'php::BigInt::bitNot(' . $this->parseExpr($expr->expr) . ')';
} }
$var = $this->parseIdentifier($expr->expr); $var = $this->parseIdentifier($expr->expr);
@ -60,13 +62,13 @@ trait UnaryExpressionTrait
{ {
$type = $this->detectTypeOfExpr($expr->expr); $type = $this->detectTypeOfExpr($expr->expr);
$this->assertExprCanBeUsedAsValue($expr->expr, 'unary operand'); $this->assertExprCanBeUsedAsValue($expr->expr, 'unary operand');
if ($type === self::TYPE_BIGFLOAT) { if ($type === Type::BIGFLOAT) {
return 'php::BigFloat::neg(' . $this->parseExprAsValue($expr->expr) . ')'; return 'php::BigFloat::neg(' . $this->parseExprAsValue($expr->expr) . ')';
} }
if ($type === self::TYPE_BIGINT) { if ($type === Type::BIGINT) {
return 'php::BigInt::neg(' . $this->parseExprAsValue($expr->expr) . ')'; return 'php::BigInt::neg(' . $this->parseExprAsValue($expr->expr) . ')';
} }
if ($type === self::TYPE_DECIMAL) { if ($type === Type::DECIMAL) {
return 'php::Decimal::neg(' . $this->parseExprAsValue($expr->expr) . ')'; return 'php::Decimal::neg(' . $this->parseExprAsValue($expr->expr) . ')';
} }
$code = $this->parseExprAsValue($expr->expr); $code = $this->parseExprAsValue($expr->expr);

@ -2,6 +2,8 @@
namespace TypePhp\Parser; namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\CompilerBase; use TypePhp\CompilerBase;
use TypePhp\Resolver\Reflection; use TypePhp\Resolver\Reflection;
@ -12,314 +14,314 @@ use PhpParser\NodeAbstract;
trait UniversalMethodCall trait UniversalMethodCall
{ {
protected const array UNIVERSAL_METHODS = [ protected const array UNIVERSAL_METHODS = [
CompilerBase::TYPE_INT => [ Type::INT => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'mod' => ['handler' => 'calc_op', 'op' => '%', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'mod' => ['handler' => 'calc_op', 'op' => '%', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'inc' => ['handler' => 'calc_inc', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'dec' => ['handler' => 'calc_dec', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
// math // math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 2], 'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 2],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], 'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 1],
'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 1], 'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 1],
'log10' => ['handler' => 'php_fn', 'fn' => 'log10', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'log10' => ['handler' => 'php_fn', 'fn' => 'log10', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'sin' => ['handler' => 'php_fn', 'fn' => 'sin', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'sin' => ['handler' => 'php_fn', 'fn' => 'sin', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'tan' => ['handler' => 'php_fn', 'fn' => 'tan', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'tan' => ['handler' => 'php_fn', 'fn' => 'tan', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'acos' => ['handler' => 'php_fn', 'fn' => 'acos', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'acos' => ['handler' => 'php_fn', 'fn' => 'acos', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'atan2' => ['handler' => 'php_fn', 'fn' => 'atan2', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'atan2' => ['handler' => 'php_fn', 'fn' => 'atan2', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'rad2deg' => ['handler' => 'php_fn', 'fn' => 'rad2deg', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'rad2deg' => ['handler' => 'php_fn', 'fn' => 'rad2deg', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'min' => ['handler' => 'php_fn', 'fn' => 'min', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'min' => ['handler' => 'php_fn', 'fn' => 'min', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
], ],
CompilerBase::TYPE_FLOAT => [ Type::FLOAT => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'inc' => ['handler' => 'calc_inc', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'dec' => ['handler' => 'calc_dec', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
// math // math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'ceil' => ['handler' => 'php_fn', 'fn' => 'ceil', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 2], 'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 2],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 1], 'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 1],
'log10' => ['handler' => 'php_fn', 'fn' => 'log10', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'log10' => ['handler' => 'php_fn', 'fn' => 'log10', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'sin' => ['handler' => 'php_fn', 'fn' => 'sin', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'sin' => ['handler' => 'php_fn', 'fn' => 'sin', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'tan' => ['handler' => 'php_fn', 'fn' => 'tan', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'tan' => ['handler' => 'php_fn', 'fn' => 'tan', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'acos' => ['handler' => 'php_fn', 'fn' => 'acos', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'acos' => ['handler' => 'php_fn', 'fn' => 'acos', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'atan2' => ['handler' => 'php_fn', 'fn' => 'atan2', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'atan2' => ['handler' => 'php_fn', 'fn' => 'atan2', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'rad2deg' => ['handler' => 'php_fn', 'fn' => 'rad2deg', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], 'rad2deg' => ['handler' => 'php_fn', 'fn' => 'rad2deg', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'min' => ['handler' => 'php_fn', 'fn' => 'min', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], 'min' => ['handler' => 'php_fn', 'fn' => 'min', 'return_type' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
], ],
CompilerBase::TYPE_BOOL => [ Type::BOOL => [
], ],
CompilerBase::TYPE_STR => [ Type::STR => [
// --- stdext string_methods (all use PHP standard functions) --- // --- stdext string_methods (all use PHP standard functions) ---
'length' => ['handler' => 'php_fn', 'fn' => 'strlen', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'length' => ['handler' => 'php_fn', 'fn' => 'strlen', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'lower' => ['handler' => 'php_fn', 'fn' => 'strtolower', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'lower' => ['handler' => 'php_fn', 'fn' => 'strtolower', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'upper' => ['handler' => 'php_fn', 'fn' => 'strtoupper', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'upper' => ['handler' => 'php_fn', 'fn' => 'strtoupper', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'lowerFirst' => ['handler' => 'php_fn', 'fn' => 'lcfirst', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'lowerFirst' => ['handler' => 'php_fn', 'fn' => 'lcfirst', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'upperFirst' => ['handler' => 'php_fn', 'fn' => 'ucfirst', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'upperFirst' => ['handler' => 'php_fn', 'fn' => 'ucfirst', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'upperWords' => ['handler' => 'php_fn', 'fn' => 'ucwords', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'upperWords' => ['handler' => 'php_fn', 'fn' => 'ucwords', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'addCSlashes' => ['handler' => 'php_fn', 'fn' => 'addcslashes', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 1], 'addCSlashes' => ['handler' => 'php_fn', 'fn' => 'addcslashes', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'addSlashes' => ['handler' => 'php_fn', 'fn' => 'addslashes', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'addSlashes' => ['handler' => 'php_fn', 'fn' => 'addslashes', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'chunkSplit' => ['handler' => 'php_fn', 'fn' => 'chunk_split', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'chunkSplit' => ['handler' => 'php_fn', 'fn' => 'chunk_split', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'countChars' => ['handler' => 'php_fn', 'fn' => 'count_chars', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], 'countChars' => ['handler' => 'php_fn', 'fn' => 'count_chars', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 1],
'htmlEntityDecode' => ['handler' => 'php_fn', 'fn' => 'html_entity_decode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'htmlEntityDecode' => ['handler' => 'php_fn', 'fn' => 'html_entity_decode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'htmlEntityEncode' => ['handler' => 'php_fn', 'fn' => 'htmlentities', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 3], 'htmlEntityEncode' => ['handler' => 'php_fn', 'fn' => 'htmlentities', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 3],
'htmlSpecialCharsEncode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 3], 'htmlSpecialCharsEncode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 3],
'htmlSpecialCharsDecode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars_decode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'htmlSpecialCharsDecode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars_decode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'trim' => ['handler' => 'php_fn', 'fn' => 'trim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'trim' => ['handler' => 'php_fn', 'fn' => 'trim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'rTrim' => ['handler' => 'php_fn', 'fn' => 'rtrim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'rTrim' => ['handler' => 'php_fn', 'fn' => 'rtrim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'parseStr' => ['handler' => 'cpp_fn', 'fn' => 'php::fn::parse_str', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'parseStr' => ['handler' => 'cpp_fn', 'fn' => 'php::fn::parse_str', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'parseUrl' => ['handler' => 'php_fn', 'fn' => 'parse_url', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], 'parseUrl' => ['handler' => 'php_fn', 'fn' => 'parse_url', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 1],
'contains' => ['handler' => 'php_fn', 'fn' => 'str_contains', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'contains' => ['handler' => 'php_fn', 'fn' => 'str_contains', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'incr' => ['handler' => 'php_fn', 'fn' => 'str_increment', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'incr' => ['handler' => 'php_fn', 'fn' => 'str_increment', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'decr' => ['handler' => 'php_fn', 'fn' => 'str_decrement', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'decr' => ['handler' => 'php_fn', 'fn' => 'str_decrement', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'pad' => ['handler' => 'php_fn', 'fn' => 'str_pad', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 3], 'pad' => ['handler' => 'php_fn', 'fn' => 'str_pad', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 3],
'repeat' => ['handler' => 'php_fn', 'fn' => 'str_repeat', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 1], 'repeat' => ['handler' => 'php_fn', 'fn' => 'str_repeat', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'replace' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 2, 'max_args' => 3], 'replace' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => Type::STR, 'min_args' => 2, 'max_args' => 3],
'iReplace' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 2, 'max_args' => 3], 'iReplace' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => Type::STR, 'min_args' => 2, 'max_args' => 3],
'shuffle' => ['handler' => 'php_fn', 'fn' => 'str_shuffle', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'shuffle' => ['handler' => 'php_fn', 'fn' => 'str_shuffle', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'split' => ['handler' => 'php_fn', 'fn' => 'explode', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], 'split' => ['handler' => 'php_fn', 'fn' => 'explode', 'receiver_pos' => 2, 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
'startsWith' => ['handler' => 'php_fn', 'fn' => 'str_starts_with', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'startsWith' => ['handler' => 'php_fn', 'fn' => 'str_starts_with', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'endsWith' => ['handler' => 'php_fn', 'fn' => 'str_ends_with', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'endsWith' => ['handler' => 'php_fn', 'fn' => 'str_ends_with', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'wordCount' => ['handler' => 'php_fn', 'fn' => 'str_word_count', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 2], 'wordCount' => ['handler' => 'php_fn', 'fn' => 'str_word_count', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 2],
'iCompare' => ['handler' => 'php_fn', 'fn' => 'strcasecmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'iCompare' => ['handler' => 'php_fn', 'fn' => 'strcasecmp', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'compare' => ['handler' => 'php_fn', 'fn' => 'strcmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'compare' => ['handler' => 'php_fn', 'fn' => 'strcmp', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'find' => ['handler' => 'php_fn', 'fn' => 'strstr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'find' => ['handler' => 'php_fn', 'fn' => 'strstr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'iFind' => ['handler' => 'php_fn', 'fn' => 'stristr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'iFind' => ['handler' => 'php_fn', 'fn' => 'stristr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'stripTags' => ['handler' => 'php_fn', 'fn' => 'strip_tags', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'stripTags' => ['handler' => 'php_fn', 'fn' => 'strip_tags', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'stripCSlashes' => ['handler' => 'php_fn', 'fn' => 'stripcslashes', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'stripCSlashes' => ['handler' => 'php_fn', 'fn' => 'stripcslashes', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'stripSlashes' => ['handler' => 'php_fn', 'fn' => 'stripslashes', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'stripSlashes' => ['handler' => 'php_fn', 'fn' => 'stripslashes', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'iIndexOf' => ['handler' => 'php_fn', 'fn' => 'stripos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'iIndexOf' => ['handler' => 'php_fn', 'fn' => 'stripos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'indexOf' => ['handler' => 'php_fn', 'fn' => 'strpos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'indexOf' => ['handler' => 'php_fn', 'fn' => 'strpos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'lastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrpos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'lastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrpos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'iLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strripos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'iLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strripos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'lastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrchr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], 'lastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrchr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 1],
'substr' => ['handler' => 'php_fn', 'fn' => 'substr', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 2], 'substr' => ['handler' => 'php_fn', 'fn' => 'substr', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 2],
'substrCompare' => ['handler' => 'php_fn', 'fn' => 'substr_compare', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 4], 'substrCompare' => ['handler' => 'php_fn', 'fn' => 'substr_compare', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 4],
'substrCount' => ['handler' => 'php_fn', 'fn' => 'substr_count', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 3], 'substrCount' => ['handler' => 'php_fn', 'fn' => 'substr_count', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 3],
'substrReplace' => ['handler' => 'php_fn', 'fn' => 'substr_replace', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 3], 'substrReplace' => ['handler' => 'php_fn', 'fn' => 'substr_replace', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 3],
'reverse' => ['handler' => 'php_fn', 'fn' => 'strrev', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'reverse' => ['handler' => 'php_fn', 'fn' => 'strrev', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'md5' => ['handler' => 'php_fn', 'fn' => 'md5', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'md5' => ['handler' => 'php_fn', 'fn' => 'md5', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'sha1' => ['handler' => 'php_fn', 'fn' => 'sha1', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'sha1' => ['handler' => 'php_fn', 'fn' => 'sha1', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'crc32' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'crc32' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'hash' => ['handler' => 'php_fn', 'fn' => 'hash', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 2], 'hash' => ['handler' => 'php_fn', 'fn' => 'hash', 'receiver_pos' => 2, 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 2],
'hashCode' => ['handler' => 'direct_method', 'method' => 'hashCode', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'hashCode' => ['handler' => 'direct_method', 'method' => 'hashCode', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'base64Decode' => ['handler' => 'php_fn', 'fn' => 'base64_decode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'base64Decode' => ['handler' => 'php_fn', 'fn' => 'base64_decode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'base64Encode' => ['handler' => 'php_fn', 'fn' => 'base64_encode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'base64Encode' => ['handler' => 'php_fn', 'fn' => 'base64_encode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'urlDecode' => ['handler' => 'php_fn', 'fn' => 'urldecode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'urlDecode' => ['handler' => 'php_fn', 'fn' => 'urldecode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'urlEncode' => ['handler' => 'php_fn', 'fn' => 'urlencode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'urlEncode' => ['handler' => 'php_fn', 'fn' => 'urlencode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'rawUrlEncode' => ['handler' => 'php_fn', 'fn' => 'rawurlencode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'rawUrlEncode' => ['handler' => 'php_fn', 'fn' => 'rawurlencode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'rawUrlDecode' => ['handler' => 'php_fn', 'fn' => 'rawurldecode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'rawUrlDecode' => ['handler' => 'php_fn', 'fn' => 'rawurldecode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'match' => ['handler' => 'direct_method', 'method' => 'match', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]], 'match' => ['handler' => 'direct_method', 'method' => 'match', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]],
'matchAll' => ['handler' => 'direct_method', 'method' => 'matchAll', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]], 'matchAll' => ['handler' => 'direct_method', 'method' => 'matchAll', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]],
'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
// mbstring // mbstring
'mbUpperFirst' => ['handler' => 'php_fn', 'fn' => 'mb_ucfirst', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbUpperFirst' => ['handler' => 'php_fn', 'fn' => 'mb_ucfirst', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbLowerFirst' => ['handler' => 'php_fn', 'fn' => 'mb_lcfirst', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbLowerFirst' => ['handler' => 'php_fn', 'fn' => 'mb_lcfirst', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbTrim' => ['handler' => 'php_fn', 'fn' => 'mb_trim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbTrim' => ['handler' => 'php_fn', 'fn' => 'mb_trim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbSubstrCount' => ['handler' => 'php_fn', 'fn' => 'mb_substr_count', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 2], 'mbSubstrCount' => ['handler' => 'php_fn', 'fn' => 'mb_substr_count', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'mbSubstr' => ['handler' => 'php_fn', 'fn' => 'mb_substr', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 3], 'mbSubstr' => ['handler' => 'php_fn', 'fn' => 'mb_substr', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 3],
'mbUpper' => ['handler' => 'php_fn', 'fn' => 'mb_strtoupper', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbUpper' => ['handler' => 'php_fn', 'fn' => 'mb_strtoupper', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbLower' => ['handler' => 'php_fn', 'fn' => 'mb_strtolower', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbLower' => ['handler' => 'php_fn', 'fn' => 'mb_strtolower', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbFind' => ['handler' => 'php_fn', 'fn' => 'mb_strstr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbFind' => ['handler' => 'php_fn', 'fn' => 'mb_strstr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strpos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strpos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrpos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrpos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbILastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strripos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbILastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strripos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbLastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrchr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbLastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrchr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbILastCharIndex' => ['handler' => 'php_fn', 'fn' => 'mb_strrichr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbILastCharIndex' => ['handler' => 'php_fn', 'fn' => 'mb_strrichr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbLength' => ['handler' => 'php_fn', 'fn' => 'mb_strlen', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 1], 'mbLength' => ['handler' => 'php_fn', 'fn' => 'mb_strlen', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 1],
'mbIFind' => ['handler' => 'php_fn', 'fn' => 'mb_stristr', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbIFind' => ['handler' => 'php_fn', 'fn' => 'mb_stristr', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbIIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_stripos', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'mbIIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_stripos', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'mbCut' => ['handler' => 'php_fn', 'fn' => 'mb_strcut', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 3], 'mbCut' => ['handler' => 'php_fn', 'fn' => 'mb_strcut', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 3],
'mbRTrim' => ['handler' => 'php_fn', 'fn' => 'mb_rtrim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbRTrim' => ['handler' => 'php_fn', 'fn' => 'mb_rtrim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbLTrim' => ['handler' => 'php_fn', 'fn' => 'mb_ltrim', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'mbLTrim' => ['handler' => 'php_fn', 'fn' => 'mb_ltrim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'mbDetectEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_detect_encoding', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'mbDetectEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_detect_encoding', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'mbConvertEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_convert_encoding', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 2], 'mbConvertEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_convert_encoding', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 2],
'mbConvertCase' => ['handler' => 'php_fn', 'fn' => 'mb_convert_case', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'mbConvertCase' => ['handler' => 'php_fn', 'fn' => 'mb_convert_case', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// serialize // serialize
'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'unmarshal' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'unmarshal' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'true']], 'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'true']],
'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => CompilerBase::TYPE_OBJECT, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'false']], 'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => Type::OBJECT, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'false']],
// phpx C++ methods (no PHP function equivalent) // phpx C++ methods (no PHP function equivalent)
'equals' => ['handler' => 'direct_method', 'method' => 'equals', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], 'equals' => ['handler' => 'direct_method', 'method' => 'equals', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
], ],
CompilerBase::TYPE_ARRAY => [ Type::ARRAY => [
// --- stdext array_methods (all use PHP standard functions) --- // --- stdext array_methods (all use PHP standard functions) ---
'all' => ['handler' => 'php_fn', 'fn' => 'array_all', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'all' => ['handler' => 'php_fn', 'fn' => 'array_all', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'any' => ['handler' => 'php_fn', 'fn' => 'array_any', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'any' => ['handler' => 'php_fn', 'fn' => 'array_any', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'changeKeyCase' => ['handler' => 'php_fn', 'fn' => 'array_change_key_case', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], 'changeKeyCase' => ['handler' => 'php_fn', 'fn' => 'array_change_key_case', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 1],
'chunk' => ['handler' => 'php_fn', 'fn' => 'array_chunk', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], 'chunk' => ['handler' => 'php_fn', 'fn' => 'array_chunk', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
'column' => ['handler' => 'php_fn', 'fn' => 'array_column', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], 'column' => ['handler' => 'php_fn', 'fn' => 'array_column', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
'countValues' => ['handler' => 'php_fn', 'fn' => 'array_count_values', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'countValues' => ['handler' => 'php_fn', 'fn' => 'array_count_values', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'diff' => ['handler' => 'php_fn', 'fn' => 'array_diff', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'diff' => ['handler' => 'php_fn', 'fn' => 'array_diff', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'diffAssoc' => ['handler' => 'php_fn', 'fn' => 'array_diff_assoc', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'diffAssoc' => ['handler' => 'php_fn', 'fn' => 'array_diff_assoc', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'diffKey' => ['handler' => 'php_fn', 'fn' => 'array_diff_key', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'diffKey' => ['handler' => 'php_fn', 'fn' => 'array_diff_key', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'filter' => ['handler' => 'php_fn', 'fn' => 'array_filter', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 2], 'filter' => ['handler' => 'php_fn', 'fn' => 'array_filter', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 2],
'find' => ['handler' => 'php_fn', 'fn' => 'array_find', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], 'find' => ['handler' => 'php_fn', 'fn' => 'array_find', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 1],
'flip' => ['handler' => 'php_fn', 'fn' => 'array_flip', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'flip' => ['handler' => 'php_fn', 'fn' => 'array_flip', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'intersect' => ['handler' => 'php_fn', 'fn' => 'array_intersect', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'intersect' => ['handler' => 'php_fn', 'fn' => 'array_intersect', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'intersectAssoc' => ['handler' => 'php_fn', 'fn' => 'array_intersect_assoc', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'intersectAssoc' => ['handler' => 'php_fn', 'fn' => 'array_intersect_assoc', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'isList' => ['handler' => 'php_fn', 'fn' => 'array_is_list', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isList' => ['handler' => 'php_fn', 'fn' => 'array_is_list', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'keyExists' => ['handler' => 'php_fn', 'fn' => 'array_key_exists', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'keyExists' => ['handler' => 'php_fn', 'fn' => 'array_key_exists', 'receiver_pos' => 2, 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'keyFirst' => ['handler' => 'php_fn', 'fn' => 'array_key_first', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'keyFirst' => ['handler' => 'php_fn', 'fn' => 'array_key_first', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'keyLast' => ['handler' => 'php_fn', 'fn' => 'array_key_last', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'keyLast' => ['handler' => 'php_fn', 'fn' => 'array_key_last', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'keys' => ['handler' => 'php_fn', 'fn' => 'array_keys', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 2], 'keys' => ['handler' => 'php_fn', 'fn' => 'array_keys', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 2],
'map' => ['handler' => 'php_fn', 'fn' => 'array_map', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'map' => ['handler' => 'php_fn', 'fn' => 'array_map', 'receiver_pos' => 2, 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'pad' => ['handler' => 'php_fn', 'fn' => 'array_pad', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 2, 'max_args' => 2], 'pad' => ['handler' => 'php_fn', 'fn' => 'array_pad', 'return_type' => Type::ARRAY, 'min_args' => 2, 'max_args' => 2],
'product' => ['handler' => 'php_fn', 'fn' => 'array_product', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'product' => ['handler' => 'php_fn', 'fn' => 'array_product', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'rand' => ['handler' => 'php_fn', 'fn' => 'array_rand', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], 'rand' => ['handler' => 'php_fn', 'fn' => 'array_rand', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 1],
'reduce' => ['handler' => 'php_fn', 'fn' => 'array_reduce', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'reduce' => ['handler' => 'php_fn', 'fn' => 'array_reduce', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'replace' => ['handler' => 'php_fn', 'fn' => 'array_replace', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'replace' => ['handler' => 'php_fn', 'fn' => 'array_replace', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'reverse' => ['handler' => 'php_fn', 'fn' => 'array_reverse', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], 'reverse' => ['handler' => 'php_fn', 'fn' => 'array_reverse', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 1],
'search' => ['handler' => 'php_fn', 'fn' => 'array_search', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], 'search' => ['handler' => 'php_fn', 'fn' => 'array_search', 'receiver_pos' => 2, 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'slice' => ['handler' => 'php_fn', 'fn' => 'array_slice', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3], 'slice' => ['handler' => 'php_fn', 'fn' => 'array_slice', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3],
'sum' => ['handler' => 'php_fn', 'fn' => 'array_sum', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'sum' => ['handler' => 'php_fn', 'fn' => 'array_sum', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'unique' => ['handler' => 'php_fn', 'fn' => 'array_unique', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], 'unique' => ['handler' => 'php_fn', 'fn' => 'array_unique', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 1],
'values' => ['handler' => 'php_fn', 'fn' => 'array_values', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'values' => ['handler' => 'php_fn', 'fn' => 'array_values', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'count' => ['handler' => 'php_fn', 'fn' => 'count', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'count' => ['handler' => 'php_fn', 'fn' => 'count', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'merge' => ['handler' => 'php_fn', 'fn' => 'array_merge', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], 'merge' => ['handler' => 'php_fn', 'fn' => 'array_merge', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'contains' => ['handler' => 'php_fn', 'fn' => 'in_array', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], 'contains' => ['handler' => 'php_fn', 'fn' => 'in_array', 'receiver_pos' => 2, 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'join' => ['handler' => 'php_fn', 'fn' => 'implode', 'receiver_pos' => 2, 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 1], 'join' => ['handler' => 'php_fn', 'fn' => 'implode', 'receiver_pos' => 2, 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
// mutating via PHP reference functions // mutating via PHP reference functions
'sort' => ['handler' => 'php_fn_ref', 'fn' => 'sort', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'sort' => ['handler' => 'php_fn_ref', 'fn' => 'sort', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'pop' => ['handler' => 'php_fn_ref', 'fn' => 'array_pop', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'pop' => ['handler' => 'php_fn_ref', 'fn' => 'array_pop', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'push' => ['handler' => 'php_fn_ref', 'fn' => 'array_push', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => -1], 'push' => ['handler' => 'php_fn_ref', 'fn' => 'array_push', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => -1],
'shift' => ['handler' => 'php_fn_ref', 'fn' => 'array_shift', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], 'shift' => ['handler' => 'php_fn_ref', 'fn' => 'array_shift', 'return_type' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'unshift' => ['handler' => 'php_fn_ref', 'fn' => 'array_unshift', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => -1], 'unshift' => ['handler' => 'php_fn_ref', 'fn' => 'array_unshift', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => -1],
'splice' => ['handler' => 'php_fn_ref', 'fn' => 'array_splice', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3], 'splice' => ['handler' => 'php_fn_ref', 'fn' => 'array_splice', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3],
'walk' => ['handler' => 'php_fn_ref', 'fn' => 'array_walk', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], 'walk' => ['handler' => 'php_fn_ref', 'fn' => 'array_walk', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'sortDesc' => ['handler' => 'php_fn_ref', 'fn' => 'rsort', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'sortDesc' => ['handler' => 'php_fn_ref', 'fn' => 'rsort', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'keySort' => ['handler' => 'php_fn_ref', 'fn' => 'ksort', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'keySort' => ['handler' => 'php_fn_ref', 'fn' => 'ksort', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'valueSort' => ['handler' => 'php_fn_ref', 'fn' => 'asort', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'valueSort' => ['handler' => 'php_fn_ref', 'fn' => 'asort', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'combine' => ['handler' => 'php_fn', 'fn' => 'array_combine', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], 'combine' => ['handler' => 'php_fn', 'fn' => 'array_combine', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'fillKeys' => ['handler' => 'php_fn', 'fn' => 'array_fill_keys', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], 'fillKeys' => ['handler' => 'php_fn', 'fn' => 'array_fill_keys', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'replaceStr' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], 'replaceStr' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
'iReplaceStr' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], 'iReplaceStr' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
// serialize // serialize
'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'marshal' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'marshal' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'jsonEncode' => ['handler' => 'php_fn', 'fn' => 'json_encode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'jsonEncode' => ['handler' => 'php_fn', 'fn' => 'json_encode', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// phpx C++ methods (no PHP function equivalent) // phpx C++ methods (no PHP function equivalent)
'set' => ['handler' => 'direct_method_mutate', 'method' => 'set', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 2, 'max_args' => 2], 'set' => ['handler' => 'direct_method_mutate', 'method' => 'set', 'return_type' => Type::ARRAY, 'min_args' => 2, 'max_args' => 2],
'get' => ['handler' => 'direct_method', 'method' => 'get', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], 'get' => ['handler' => 'direct_method', 'method' => 'get', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 1],
'del' => ['handler' => 'direct_method_mutate', 'method' => 'del', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], 'del' => ['handler' => 'direct_method_mutate', 'method' => 'del', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'clean' => ['handler' => 'direct_method_mutate', 'method' => 'clean', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'clean' => ['handler' => 'direct_method_mutate', 'method' => 'clean', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
], ],
CompilerBase::TYPE_STREAM => [ Type::STREAM => [
// --- stdext stream_methods --- // --- stdext stream_methods ---
'write' => ['handler' => 'php_fn', 'fn' => 'fwrite', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 2], 'write' => ['handler' => 'php_fn', 'fn' => 'fwrite', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'read' => ['handler' => 'php_fn', 'fn' => 'fread', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 1], 'read' => ['handler' => 'php_fn', 'fn' => 'fread', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'close' => ['handler' => 'php_fn', 'fn' => 'fclose', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'close' => ['handler' => 'php_fn', 'fn' => 'fclose', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'dataSync' => ['handler' => 'php_fn', 'fn' => 'fdatasync', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'dataSync' => ['handler' => 'php_fn', 'fn' => 'fdatasync', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'sync' => ['handler' => 'php_fn', 'fn' => 'fsync', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'sync' => ['handler' => 'php_fn', 'fn' => 'fsync', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'truncate' => ['handler' => 'php_fn', 'fn' => 'ftruncate', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], 'truncate' => ['handler' => 'php_fn', 'fn' => 'ftruncate', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'stat' => ['handler' => 'php_fn', 'fn' => 'fstat', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'stat' => ['handler' => 'php_fn', 'fn' => 'fstat', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'seek' => ['handler' => 'php_fn', 'fn' => 'fseek', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 2], 'seek' => ['handler' => 'php_fn', 'fn' => 'fseek', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'tell' => ['handler' => 'php_fn', 'fn' => 'ftell', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'tell' => ['handler' => 'php_fn', 'fn' => 'ftell', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'lock' => ['handler' => 'php_fn', 'fn' => 'flock', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], 'lock' => ['handler' => 'php_fn', 'fn' => 'flock', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'eof' => ['handler' => 'php_fn', 'fn' => 'feof', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'eof' => ['handler' => 'php_fn', 'fn' => 'feof', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'getChar' => ['handler' => 'php_fn', 'fn' => 'fgetc', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 0], 'getChar' => ['handler' => 'php_fn', 'fn' => 'fgetc', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'getLine' => ['handler' => 'php_fn', 'fn' => 'fgets', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1], 'getLine' => ['handler' => 'php_fn', 'fn' => 'fgets', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 1],
// --- stream_* functions --- // --- stream_* functions ---
'getContents' => ['handler' => 'php_fn', 'fn' => 'stream_get_contents', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'getContents' => ['handler' => 'php_fn', 'fn' => 'stream_get_contents', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'getMetaData' => ['handler' => 'php_fn', 'fn' => 'stream_get_meta_data', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], 'getMetaData' => ['handler' => 'php_fn', 'fn' => 'stream_get_meta_data', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'isLocal' => ['handler' => 'php_fn', 'fn' => 'stream_is_local', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isLocal' => ['handler' => 'php_fn', 'fn' => 'stream_is_local', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'isTTY' => ['handler' => 'php_fn', 'fn' => 'stream_isatty', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'isTTY' => ['handler' => 'php_fn', 'fn' => 'stream_isatty', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'setBlocking' => ['handler' => 'php_fn', 'fn' => 'stream_set_blocking', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'setBlocking' => ['handler' => 'php_fn', 'fn' => 'stream_set_blocking', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'setChunkSize' => ['handler' => 'php_fn', 'fn' => 'stream_set_chunk_size', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'setChunkSize' => ['handler' => 'php_fn', 'fn' => 'stream_set_chunk_size', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'setReadBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_read_buffer', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'setReadBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_read_buffer', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'setTimeout' => ['handler' => 'php_fn', 'fn' => 'stream_set_timeout', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], 'setTimeout' => ['handler' => 'php_fn', 'fn' => 'stream_set_timeout', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'setWriteBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_write_buffer', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'setWriteBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_write_buffer', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'supportsLock' => ['handler' => 'php_fn', 'fn' => 'stream_supports_lock', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], 'supportsLock' => ['handler' => 'php_fn', 'fn' => 'stream_supports_lock', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'copy' => ['handler' => 'php_fn', 'fn' => 'stream_copy_to_stream', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 3], 'copy' => ['handler' => 'php_fn', 'fn' => 'stream_copy_to_stream', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 3],
// --- stream_socket_* functions --- // --- stream_socket_* functions ---
'accept' => ['handler' => 'php_fn', 'fn' => 'stream_socket_accept', 'return_type' => CompilerBase::TYPE_STREAM, 'min_args' => 0, 'max_args' => 1], 'accept' => ['handler' => 'php_fn', 'fn' => 'stream_socket_accept', 'return_type' => Type::STREAM, 'min_args' => 0, 'max_args' => 1],
'enableCrypto' => ['handler' => 'php_fn', 'fn' => 'stream_socket_enable_crypto', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 3], 'enableCrypto' => ['handler' => 'php_fn', 'fn' => 'stream_socket_enable_crypto', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 3],
'getSocketName' => ['handler' => 'php_fn', 'fn' => 'stream_socket_get_name', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 1], 'getSocketName' => ['handler' => 'php_fn', 'fn' => 'stream_socket_get_name', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'recvFrom' => ['handler' => 'php_fn', 'fn' => 'stream_socket_recvfrom', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 1, 'max_args' => 2], 'recvFrom' => ['handler' => 'php_fn', 'fn' => 'stream_socket_recvfrom', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 2],
'sendTo' => ['handler' => 'php_fn', 'fn' => 'stream_socket_sendto', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 3], 'sendTo' => ['handler' => 'php_fn', 'fn' => 'stream_socket_sendto', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 3],
'shutdown' => ['handler' => 'php_fn', 'fn' => 'stream_socket_shutdown', 'return_type' => CompilerBase::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], 'shutdown' => ['handler' => 'php_fn', 'fn' => 'stream_socket_shutdown', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'getRecord' => ['handler' => 'php_fn', 'fn' => 'stream_get_line', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2], 'getRecord' => ['handler' => 'php_fn', 'fn' => 'stream_get_line', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// --- stream filters --- // --- stream filters ---
'appendFilter' => ['handler' => 'php_fn', 'fn' => 'stream_filter_append', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'appendFilter' => ['handler' => 'php_fn', 'fn' => 'stream_filter_append', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
'prependFilter' => ['handler' => 'php_fn', 'fn' => 'stream_filter_prepend', 'return_type' => CompilerBase::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], 'prependFilter' => ['handler' => 'php_fn', 'fn' => 'stream_filter_prepend', 'return_type' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
], ],
CompilerBase::TYPE_BIGINT => [ Type::BIGINT => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::add', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::add', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sub', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sub', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mul', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mul', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::div', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'div' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::div', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mod', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mod', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'pow' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::pow', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'pow' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::pow', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::neg', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 0, 'max_args' => 0], 'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::neg', 'return_type' => Type::BIGINT, 'min_args' => 0, 'max_args' => 0],
'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::cmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::cmp', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::abs', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 0, 'max_args' => 0], 'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::abs', 'return_type' => Type::BIGINT, 'min_args' => 0, 'max_args' => 0],
'gcd' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::gcd', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'gcd' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::gcd', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::divmod', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], 'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::divmod', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::powmod', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 2, 'max_args' => 2], 'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::powmod', 'return_type' => Type::BIGINT, 'min_args' => 2, 'max_args' => 2],
'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sqrt', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 0, 'max_args' => 0], 'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sqrt', 'return_type' => Type::BIGINT, 'min_args' => 0, 'max_args' => 0],
'bitAnd' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitAnd', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'bitAnd' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitAnd', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitOr' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitOr', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'bitOr' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitOr', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitXor' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitXor', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'bitXor' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitXor', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitNot' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitNot', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 0, 'max_args' => 0], 'bitNot' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitNot', 'return_type' => Type::BIGINT, 'min_args' => 0, 'max_args' => 0],
'testBit' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::testBit', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'testBit' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::testBit', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'popCount' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::popCount', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0], 'popCount' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::popCount', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'bitShiftLeft' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitShiftLeft', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'bitShiftLeft' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitShiftLeft', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'bitShiftRight' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitShiftRight', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1], 'bitShiftRight' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitShiftRight', 'return_type' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
], ],
CompilerBase::TYPE_DECIMAL => [ Type::DECIMAL => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::add', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'add' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::add', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::sub', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::sub', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mul', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mul', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::div', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'div' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::div', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mod', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mod', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'pow' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::pow', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 1, 'max_args' => 1], 'pow' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::pow', 'return_type' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::neg', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0], 'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::neg', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::cmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::cmp', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::abs', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0], 'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::abs', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::divmod', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], 'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::divmod', 'return_type' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::powmod', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 2, 'max_args' => 2], 'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::powmod', 'return_type' => Type::DECIMAL, 'min_args' => 2, 'max_args' => 2],
'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::sqrt', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0], 'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::sqrt', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'floor' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::floor', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0], 'floor' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::floor', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'ceil' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::ceil', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 0], 'ceil' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::ceil', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::round', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 1], 'round' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::round', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 1],
], ],
CompilerBase::TYPE_BIGFLOAT => [ Type::BIGFLOAT => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::add', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 1, 'max_args' => 1], 'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::add', 'return_type' => Type::BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::sub', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 1, 'max_args' => 1], 'sub' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::sub', 'return_type' => Type::BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::mul', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 1, 'max_args' => 1], 'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::mul', 'return_type' => Type::BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::div', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 1, 'max_args' => 1], 'div' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::div', 'return_type' => Type::BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::neg', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 0, 'max_args' => 0], 'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::neg', 'return_type' => Type::BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::cmp', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1], 'cmp' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::cmp', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::abs', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 0, 'max_args' => 0], 'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::abs', 'return_type' => Type::BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::sqrt', 'return_type' => CompilerBase::TYPE_BIGFLOAT, 'min_args' => 0, 'max_args' => 0], 'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::sqrt', 'return_type' => Type::BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
], ],
]; ];
@ -336,16 +338,16 @@ trait UniversalMethodCall
} }
protected const array TYPE_EXTENSION_PREFIX = [ protected const array TYPE_EXTENSION_PREFIX = [
CompilerBase::TYPE_INT => 'int', Type::INT => 'int',
CompilerBase::TYPE_FLOAT => 'float', Type::FLOAT => 'float',
CompilerBase::TYPE_BOOL => 'bool', Type::BOOL => 'bool',
CompilerBase::TYPE_STR => 'str', Type::STR => 'str',
CompilerBase::TYPE_ARRAY => 'array', Type::ARRAY => 'array',
CompilerBase::TYPE_STREAM => 'stream', Type::STREAM => 'stream',
CompilerBase::TYPE_BIGINT => 'bigint', Type::BIGINT => 'bigint',
CompilerBase::TYPE_DECIMAL => 'decimal', Type::DECIMAL => 'decimal',
CompilerBase::TYPE_BIGFLOAT => 'bigfloat', Type::BIGFLOAT => 'bigfloat',
CompilerBase::TYPE_BOX => 'box', Type::BOX => 'box',
]; ];
protected function camelToSnake(string $name): string protected function camelToSnake(string $name): string
@ -378,9 +380,9 @@ trait UniversalMethodCall
} }
protected const array TO_CONVERT_FN = [ protected const array TO_CONVERT_FN = [
CompilerBase::TYPE_BIGINT => ['toInt' => 'php::BigInt::toInt', 'toFloat' => 'php::BigInt::toFloat', 'toString' => 'php::BigInt::toString'], Type::BIGINT => ['toInt' => 'php::BigInt::toInt', 'toFloat' => 'php::BigInt::toFloat', 'toString' => 'php::BigInt::toString'],
CompilerBase::TYPE_BIGFLOAT => ['toInt' => 'php::BigFloat::toInt', 'toFloat' => 'php::BigFloat::toFloat', 'toString' => 'php::BigFloat::toString'], Type::BIGFLOAT => ['toInt' => 'php::BigFloat::toInt', 'toFloat' => 'php::BigFloat::toFloat', 'toString' => 'php::BigFloat::toString'],
CompilerBase::TYPE_DECIMAL => ['toInt' => 'php::Decimal::toInt', 'toFloat' => 'php::Decimal::toFloat', 'toString' => 'php::Decimal::toString'], Type::DECIMAL => ['toInt' => 'php::Decimal::toInt', 'toFloat' => 'php::Decimal::toFloat', 'toString' => 'php::Decimal::toString'],
]; ];
/** /**
@ -438,7 +440,7 @@ trait UniversalMethodCall
} }
$receiver = $funcDef->argInfoList[0]; $receiver = $funcDef->argInfoList[0];
if ($receiver->byRef if ($receiver->byRef
|| $receiver->type !== CompilerBase::TYPE_OBJECT || $receiver->type !== Type::OBJECT
|| !$this->isSameClassName($receiver->declaredClass, $class)) { || !$this->isSameClassName($receiver->declaredClass, $class)) {
continue; continue;
} }
@ -513,7 +515,7 @@ trait UniversalMethodCall
continue; continue;
} }
$firstParam = $funcDef->argInfoList[0]; $firstParam = $funcDef->argInfoList[0];
if ($firstParam->type !== CompilerBase::TYPE_VAR) { if ($firstParam->type !== Type::VAR) {
continue; continue;
} }
@ -564,7 +566,7 @@ trait UniversalMethodCall
} }
$phpType = Reflection::getFunctionReturnType($funcName); $phpType = Reflection::getFunctionReturnType($funcName);
$returnType = $phpType ? ($this->zendTypeMap[$phpType] ?? CompilerBase::TYPE_VAR) : CompilerBase::TYPE_VAR; $returnType = $phpType ? ($this->zendTypeMap[$phpType] ?? Type::VAR) : Type::VAR;
$requiredParams = $ref->getNumberOfRequiredParameters(); $requiredParams = $ref->getNumberOfRequiredParameters();
$minArgs = max(0, $requiredParams - 1); $minArgs = max(0, $requiredParams - 1);
@ -594,14 +596,14 @@ trait UniversalMethodCall
} }
$firstParam = $funcDef->argInfoList[0]; $firstParam = $funcDef->argInfoList[0];
// Stream/Box are PHP pseudo-types; their params may be typed or untyped // Stream/Box are PHP pseudo-types; their params may be typed or untyped
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) { if ($type === Type::STREAM || $type === Type::BOX) {
return $firstParam->byRef || $firstParam->type === self::TYPE_VAR || $firstParam->type === self::TYPE_REF || $firstParam->type === $type; return $firstParam->byRef || $firstParam->type === Type::VAR || $firstParam->type === Type::REF || $firstParam->type === $type;
} }
if ($firstParam->byRef) { if ($firstParam->byRef) {
return $type === self::TYPE_ARRAY; return $type === Type::ARRAY;
} }
$paramType = $firstParam->type; $paramType = $firstParam->type;
if ($paramType === self::TYPE_VAR) { if ($paramType === Type::VAR) {
return false; return false;
} }
return $paramType === $type; return $paramType === $type;
@ -614,11 +616,11 @@ trait UniversalMethodCall
return false; return false;
} }
// Stream pseudo-type: accept untyped or by-reference first params // Stream pseudo-type: accept untyped or by-reference first params
if ($type === self::TYPE_STREAM) { if ($type === Type::STREAM) {
return true; return true;
} }
if ($param->isPassedByReference()) { if ($param->isPassedByReference()) {
return $type === self::TYPE_ARRAY; return $type === Type::ARRAY;
} }
$paramType = $param->getType(); $paramType = $param->getType();
if ($paramType === null) { if ($paramType === null) {
@ -678,7 +680,7 @@ trait UniversalMethodCall
$this->validateUniversalMethodArgs($expr, $method, $def, false); $this->validateUniversalMethodArgs($expr, $method, $def, false);
// Evaluate receiver into temp var to avoid double evaluation // Evaluate receiver into temp var to avoid double evaluation
$streamVar = $this->addTmpVar(self::TYPE_VAR); $streamVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = $streamVar . ' = ' . $receiver . ';'; $this->context->beforeStmtLines[] = $streamVar . ' = ' . $receiver . ';';
$methodCall = match ($def['handler']) { $methodCall = match ($def['handler']) {
@ -688,7 +690,7 @@ trait UniversalMethodCall
default => null, default => null,
}; };
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = "{$tmpVar} = {$methodCall};"; $this->context->beforeStmtLines[] = "{$tmpVar} = {$methodCall};";
return $tmpVar; return $tmpVar;
} }
@ -700,11 +702,11 @@ trait UniversalMethodCall
protected function wrapUniversalReceiver(string $type, string $expr): string protected function wrapUniversalReceiver(string $type, string $expr): string
{ {
$convFns = [ $convFns = [
self::TYPE_ARRAY => 'toArray', Type::ARRAY => 'toArray',
self::TYPE_STR => 'toString', Type::STR => 'toString',
self::TYPE_INT => 'toInt', Type::INT => 'toInt',
self::TYPE_FLOAT => 'toFloat', Type::FLOAT => 'toFloat',
self::TYPE_BOOL => 'toBool', Type::BOOL => 'toBool',
]; ];
if (isset($convFns[$type])) { if (isset($convFns[$type])) {
return 'php::' . $convFns[$type] . '(' . $expr . ')'; return 'php::' . $convFns[$type] . '(' . $expr . ')';
@ -898,8 +900,8 @@ trait UniversalMethodCall
protected function genUniversalPhpFnRef(string $receiver, string $phpFunc, array $args, string $returnType): string protected function genUniversalPhpFnRef(string $receiver, string $phpFunc, array $args, string $returnType): string
{ {
$tmpRef = $this->addTmpVar(self::TYPE_REF); $tmpRef = $this->addTmpVar(Type::REF);
$tmpVar = $this->addTmpVar(self::TYPE_VAR); $tmpVar = $this->addTmpVar(Type::VAR);
$argExprs = ['&' . $tmpRef]; $argExprs = ['&' . $tmpRef];
foreach ($args as $arg) { foreach ($args as $arg) {
$argExprs[] = $this->parseExpr($arg->value); $argExprs[] = $this->parseExpr($arg->value);

@ -69,8 +69,8 @@ class Preprocessor extends CompilerBase
protected function genArgumentDeclaration(ArgInfo $argInfo): string protected function genArgumentDeclaration(ArgInfo $argInfo): string
{ {
$type = $argInfo->type; $type = $argInfo->type;
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) { if ($type === Type::STREAM || $type === Type::BOX) {
$type = self::TYPE_VAR; $type = Type::VAR;
} }
return $type . ' ' . $argInfo->name; return $type . ' ' . $argInfo->name;
} }
@ -267,7 +267,7 @@ class Preprocessor extends CompilerBase
protected function parseParameterType(Node\Param $param, ArgInfo $argInfo, string $var): string protected function parseParameterType(Node\Param $param, ArgInfo $argInfo, string $var): string
{ {
if ($param->byRef) { if ($param->byRef) {
return self::TYPE_REF; return Type::REF;
} }
$class = ''; $class = '';
$type = $this->parseTypeDecl($param->type, self::DECL_TYPE_OF_PARAM, $class); $type = $this->parseTypeDecl($param->type, self::DECL_TYPE_OF_PARAM, $class);
@ -367,7 +367,7 @@ class Preprocessor extends CompilerBase
} }
} }
if ($param->variadic) { if ($param->variadic) {
$list[] = self::TYPE_ARRAY . ' ' . $name; $list[] = Type::ARRAY . ' ' . $name;
} else { } else {
$list[] = $this->genArgumentDeclaration($argInfo); $list[] = $this->genArgumentDeclaration($argInfo);
} }
@ -438,7 +438,7 @@ class Preprocessor extends CompilerBase
$returnType = $this->parseTypeDecl($v->returnType, self::DECL_TYPE_OF_RETURN, $class); $returnType = $this->parseTypeDecl($v->returnType, self::DECL_TYPE_OF_RETURN, $class);
// 构造、析构、克隆方法不能有返回值 // 构造、析构、克隆方法不能有返回值
if ($this->method and in_array($this->method, ['__construct', '__destruct', '__clone'])) { if ($this->method and in_array($this->method, ['__construct', '__destruct', '__clone'])) {
$returnType = self::TYPE_VOID; $returnType = Type::VOID;
} }
$functionDef = new FunctionDef($fnName, $returnType, $this->namespace); $functionDef = new FunctionDef($fnName, $returnType, $this->namespace);
@ -470,13 +470,13 @@ class Preprocessor extends CompilerBase
if (count($v->params) != 2) { if (count($v->params) != 2) {
$this->fatalError($v, 'The parameters of the main function must be `(int $argc, array $argv)`.'); $this->fatalError($v, 'The parameters of the main function must be `(int $argc, array $argv)`.');
} }
if ($returnType !== self::TYPE_VOID) { if ($returnType !== Type::VOID) {
$this->fatalError($v, 'main function must return void'); $this->fatalError($v, 'main function must return void');
} }
if (!$this->checkArgType($functionDef->argInfoList[0]->type, self::TYPE_INT)) { if (!$this->checkArgType($functionDef->argInfoList[0]->type, Type::INT)) {
$this->fatalError($v, 'The first parameter of the main function must be of type `int`.'); $this->fatalError($v, 'The first parameter of the main function must be of type `int`.');
} }
if (!$this->checkArgType($functionDef->argInfoList[1]->type, self::TYPE_ARRAY)) { if (!$this->checkArgType($functionDef->argInfoList[1]->type, Type::ARRAY)) {
$this->fatalError($v, 'The second parameter of the main function must be of type `array`.'); $this->fatalError($v, 'The second parameter of the main function must be of type `array`.');
} }
} }
@ -648,9 +648,9 @@ class Preprocessor extends CompilerBase
$type = $declaredType; $type = $declaredType;
if ($type === null) { if ($type === null) {
$type = match ($const->value->getType()) { $type = match ($const->value->getType()) {
'Expr_Array' => self::TYPE_ARRAY, 'Expr_Array' => Type::ARRAY,
'Scalar_String' => self::TYPE_STR, 'Scalar_String' => Type::STR,
default => self::TYPE_VAR, default => Type::VAR,
}; };
} }
$constName = $this->parseIdentifier($const->name); $constName = $this->parseIdentifier($const->name);
@ -697,7 +697,7 @@ class Preprocessor extends CompilerBase
$arrayInitPlan = null; $arrayInitPlan = null;
if ($defaultNode !== null) { if ($defaultNode !== null) {
if ($defaultNode instanceof Node\Expr\Array_) { if ($defaultNode instanceof Node\Expr\Array_) {
$type = self::TYPE_ARRAY; $type = Type::ARRAY;
$arrayInitPlan = $this->buildLiteralArrayInitPlan($defaultNode); $arrayInitPlan = $this->buildLiteralArrayInitPlan($defaultNode);
$default = $arrayInitPlan->expr; $default = $arrayInitPlan->expr;
} else { } else {
@ -868,9 +868,9 @@ class Preprocessor extends CompilerBase
$type = $stmt->type $type = $stmt->type
? $this->parseTypeDecl($stmt->type, self::DECL_TYPE_OF_CONST, $class) ? $this->parseTypeDecl($stmt->type, self::DECL_TYPE_OF_CONST, $class)
: match ($const->value->getType()) { : match ($const->value->getType()) {
'Expr_Array' => self::TYPE_ARRAY, 'Expr_Array' => Type::ARRAY,
'Scalar_String' => self::TYPE_STR, 'Scalar_String' => Type::STR,
default => self::TYPE_VAR, default => Type::VAR,
}; };
$constInfo = $this->parseClassLikeConstant($const, $this->parseModifiers($stmt->flags), $type, $class); $constInfo = $this->parseClassLikeConstant($const, $this->parseModifiers($stmt->flags), $type, $class);
$this->interfaceDef->constants[$constName] = $constInfo; $this->interfaceDef->constants[$constName] = $constInfo;

@ -7,6 +7,8 @@
namespace TypePhp\Resolver; namespace TypePhp\Resolver;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
trait DeclarationSymbolTrait trait DeclarationSymbolTrait
@ -51,16 +53,16 @@ trait DeclarationSymbolTrait
protected function detectStrValueType(mixed $constant): string protected function detectStrValueType(mixed $constant): string
{ {
if ($this->isIntStr($constant)) { if ($this->isIntStr($constant)) {
return self::TYPE_INT; return Type::INT;
} }
if ($this->isFloatStr($constant)) { if ($this->isFloatStr($constant)) {
return self::TYPE_FLOAT; return Type::FLOAT;
} }
if ($this->isBoolStr($constant)) { if ($this->isBoolStr($constant)) {
return self::TYPE_BOOL; return Type::BOOL;
} }
return self::TYPE_VAR; return Type::VAR;
} }

@ -8,6 +8,8 @@
namespace TypePhp\Resolver; namespace TypePhp\Resolver;
use TypePhp\Type;
use TypePhp\Entity\MethodDef; use TypePhp\Entity\MethodDef;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -91,110 +93,110 @@ trait MagicMethodDetector
if ($nameLower == '__call' or $nameLower == '__callstatic') { if ($nameLower == '__call' or $nameLower == '__callstatic') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_STR; $argInfoList[0]->type = Type::STR;
} elseif ($argInfoList[0]->type !== self::TYPE_STR) { } elseif ($argInfoList[0]->type !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must take string as first argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take string as first argument');
} }
if ($argInfoList[1]->undeclared) { if ($argInfoList[1]->undeclared) {
$argInfoList[1]->type = self::TYPE_ARRAY; $argInfoList[1]->type = Type::ARRAY;
} elseif ($argInfoList[1]->type !== self::TYPE_ARRAY) { } elseif ($argInfoList[1]->type !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must take array as second argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take array as second argument');
} }
} elseif ($nameLower == '__set') { } elseif ($nameLower == '__set') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_STR; $argInfoList[0]->type = Type::STR;
} elseif ($argInfoList[0]->type !== self::TYPE_STR) { } elseif ($argInfoList[0]->type !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must take string as first argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take string as first argument');
} }
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_VOID; $fnDef->returnType = Type::VOID;
} elseif ($fnDef->returnType !== self::TYPE_VOID) { } elseif ($fnDef->returnType !== Type::VOID) {
$this->fatalError($v, 'Method ' . $methodName . '() must return void'); $this->fatalError($v, 'Method ' . $methodName . '() must return void');
} }
} elseif ($nameLower == '__get') { } elseif ($nameLower == '__get') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_STR; $argInfoList[0]->type = Type::STR;
} elseif ($argInfoList[0]->type !== self::TYPE_STR) { } elseif ($argInfoList[0]->type !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must take string as argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take string as argument');
} }
} elseif ($nameLower == '__tostring') { } elseif ($nameLower == '__tostring') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_STR; $fnDef->returnType = Type::STR;
} elseif ($fnDef->returnType !== self::TYPE_STR) { } elseif ($fnDef->returnType !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must return string'); $this->fatalError($v, 'Method ' . $methodName . '() must return string');
} }
} elseif ($nameLower == '__serialize') { } elseif ($nameLower == '__serialize') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_ARRAY; $fnDef->returnType = Type::ARRAY;
} elseif ($fnDef->returnType !== self::TYPE_ARRAY) { } elseif ($fnDef->returnType !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must return array'); $this->fatalError($v, 'Method ' . $methodName . '() must return array');
} }
} elseif ($nameLower == '__unserialize') { } elseif ($nameLower == '__unserialize') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_ARRAY; $argInfoList[0]->type = Type::ARRAY;
} elseif ($argInfoList[0]->type !== self::TYPE_ARRAY) { } elseif ($argInfoList[0]->type !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must take array as argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take array as argument');
} }
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_VOID; $fnDef->returnType = Type::VOID;
} elseif ($fnDef->returnType !== self::TYPE_VOID) { } elseif ($fnDef->returnType !== Type::VOID) {
$this->fatalError($v, 'Method ' . $methodName . '() must return void'); $this->fatalError($v, 'Method ' . $methodName . '() must return void');
} }
} elseif ($nameLower == '__isset') { } elseif ($nameLower == '__isset') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_STR; $argInfoList[0]->type = Type::STR;
} elseif ($argInfoList[0]->type !== self::TYPE_STR) { } elseif ($argInfoList[0]->type !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must take string as argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take string as argument');
} }
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_BOOL; $fnDef->returnType = Type::BOOL;
} elseif ($fnDef->returnType !== self::TYPE_BOOL) { } elseif ($fnDef->returnType !== Type::BOOL) {
$this->fatalError($v, 'Method ' . $methodName . '() must return bool'); $this->fatalError($v, 'Method ' . $methodName . '() must return bool');
} }
} elseif ($nameLower == '__unset') { } elseif ($nameLower == '__unset') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_STR; $argInfoList[0]->type = Type::STR;
} elseif ($argInfoList[0]->type !== self::TYPE_STR) { } elseif ($argInfoList[0]->type !== Type::STR) {
$this->fatalError($v, 'Method ' . $methodName . '() must take string as argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take string as argument');
} }
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_VOID; $fnDef->returnType = Type::VOID;
} elseif ($fnDef->returnType !== self::TYPE_VOID) { } elseif ($fnDef->returnType !== Type::VOID) {
$this->fatalError($v, 'Method ' . $methodName . '() must return void'); $this->fatalError($v, 'Method ' . $methodName . '() must return void');
} }
} elseif ($nameLower == '__set_state') { } elseif ($nameLower == '__set_state') {
if ($argInfoList[0]->undeclared) { if ($argInfoList[0]->undeclared) {
$argInfoList[0]->type = self::TYPE_ARRAY; $argInfoList[0]->type = Type::ARRAY;
} elseif ($argInfoList[0]->type !== self::TYPE_ARRAY) { } elseif ($argInfoList[0]->type !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must take array as argument'); $this->fatalError($v, 'Method ' . $methodName . '() must take array as argument');
} }
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_OBJECT; $fnDef->returnType = Type::OBJECT;
} elseif ($fnDef->returnType !== self::TYPE_OBJECT) { } elseif ($fnDef->returnType !== Type::OBJECT) {
$this->fatalError($v, 'Method ' . $methodName . '() must return object'); $this->fatalError($v, 'Method ' . $methodName . '() must return object');
} }
} elseif ($nameLower == '__debuginfo') { } elseif ($nameLower == '__debuginfo') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_ARRAY; $fnDef->returnType = Type::ARRAY;
} elseif ($fnDef->returnType !== self::TYPE_ARRAY) { } elseif ($fnDef->returnType !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must return array'); $this->fatalError($v, 'Method ' . $methodName . '() must return array');
} }
} elseif ($nameLower == '__sleep') { } elseif ($nameLower == '__sleep') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_ARRAY; $fnDef->returnType = Type::ARRAY;
} elseif ($fnDef->returnType !== self::TYPE_ARRAY) { } elseif ($fnDef->returnType !== Type::ARRAY) {
$this->fatalError($v, 'Method ' . $methodName . '() must return array'); $this->fatalError($v, 'Method ' . $methodName . '() must return array');
} }
} elseif ($nameLower == '__wakeup') { } elseif ($nameLower == '__wakeup') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_VOID; $fnDef->returnType = Type::VOID;
} elseif ($fnDef->returnType !== self::TYPE_VOID) { } elseif ($fnDef->returnType !== Type::VOID) {
$this->fatalError($v, 'Method ' . $methodName . '() must return void'); $this->fatalError($v, 'Method ' . $methodName . '() must return void');
} }
} elseif ($nameLower == '__clone') { } elseif ($nameLower == '__clone') {
if ($returnTypeUndeclared) { if ($returnTypeUndeclared) {
$fnDef->returnType = self::TYPE_VOID; $fnDef->returnType = Type::VOID;
} elseif ($fnDef->returnType !== self::TYPE_VOID) { } elseif ($fnDef->returnType !== Type::VOID) {
$this->fatalError($v, 'Method ' . $methodName . '() must return void'); $this->fatalError($v, 'Method ' . $methodName . '() must return void');
} }
} }
@ -203,11 +205,11 @@ trait MagicMethodDetector
$list = []; $list = [];
foreach ($fnDef->argInfoList as $argInfo) { foreach ($fnDef->argInfoList as $argInfo) {
if ($argInfo->variadic) { if ($argInfo->variadic) {
$list[] = self::TYPE_ARRAY . ' ' . $argInfo->name; $list[] = Type::ARRAY . ' ' . $argInfo->name;
} else { } else {
$type = $argInfo->type; $type = $argInfo->type;
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) { if ($type === Type::STREAM || $type === Type::BOX) {
$type = self::TYPE_VAR; $type = Type::VAR;
} }
$list[] = $type . ' ' . $argInfo->name; $list[] = $type . ' ' . $argInfo->name;
} }
@ -217,7 +219,7 @@ trait MagicMethodDetector
protected function checkArgType(string $givenType, string $expectType, bool $canBeVar = true): bool protected function checkArgType(string $givenType, string $expectType, bool $canBeVar = true): bool
{ {
if ($canBeVar and $givenType == self::TYPE_VAR) { if ($canBeVar and $givenType == Type::VAR) {
return true; return true;
} }
return $givenType == $expectType; return $givenType == $expectType;

@ -8,6 +8,8 @@
namespace TypePhp\Resolver; namespace TypePhp\Resolver;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\Node\IntersectionType; use PhpParser\Node\IntersectionType;
use PhpParser\Node\NullableType; use PhpParser\Node\NullableType;
@ -131,11 +133,11 @@ trait NameResolutionTrait
{ {
// 未定义类型视为 var (mixed, any) // 未定义类型视为 var (mixed, any)
if ($type === null) { if ($type === null) {
return self::TYPE_VAR; return Type::VAR;
} }
if ($type instanceof UnionType || $type instanceof NullableType || $type instanceof IntersectionType) { if ($type instanceof UnionType || $type instanceof NullableType || $type instanceof IntersectionType) {
// 复杂类型静态阶段统一按 mixed/var 处理,运行时再由 typeCheck 兜底。 // 复杂类型静态阶段统一按 mixed/var 处理,运行时再由 typeCheck 兜底。
return self::TYPE_VAR; return Type::VAR;
} else { } else {
$typeName = $this->parseIdentifier($type); $typeName = $this->parseIdentifier($type);
$typeNameLower = strtolower($typeName); $typeNameLower = strtolower($typeName);
@ -162,7 +164,7 @@ trait NameResolutionTrait
if ($class and $this->classDef and $this->classDef->trait) { if ($class and $this->classDef and $this->classDef->trait) {
$type->name = $class; $type->name = $class;
} }
return self::TYPE_OBJECT; return Type::OBJECT;
} }
} }
} }

@ -8,7 +8,8 @@
namespace TypePhp\Resolver; namespace TypePhp\Resolver;
use TypePhp\CompilerBase; use TypePhp\Type;
use TypePhp\Entity\PropertyDef; use TypePhp\Entity\PropertyDef;
final class PropertyAssignTypeInfo final class PropertyAssignTypeInfo
@ -16,11 +17,11 @@ final class PropertyAssignTypeInfo
public function getFixedDefaultValue(PropertyDef $def): ?string public function getFixedDefaultValue(PropertyDef $def): ?string
{ {
return match ($def->type) { return match ($def->type) {
CompilerBase::TYPE_INT => $def->default ?? '0', Type::INT => $def->default ?? '0',
CompilerBase::TYPE_FLOAT => $def->default ?? '0.0', Type::FLOAT => $def->default ?? '0.0',
CompilerBase::TYPE_BOOL => $def->default ?? 'false', Type::BOOL => $def->default ?? 'false',
CompilerBase::TYPE_STR => $def->default ?? CompilerBase::TYPE_STR . '()', Type::STR => $def->default ?? Type::STR . '()',
CompilerBase::TYPE_ARRAY => $def->default ?? CompilerBase::TYPE_ARRAY . '{}', Type::ARRAY => $def->default ?? Type::ARRAY . '{}',
default => null, default => null,
}; };
} }
@ -28,11 +29,11 @@ final class PropertyAssignTypeInfo
public function isFixed(PropertyDef $def): bool public function isFixed(PropertyDef $def): bool
{ {
return in_array($def->type, [ return in_array($def->type, [
CompilerBase::TYPE_INT, Type::INT,
CompilerBase::TYPE_FLOAT, Type::FLOAT,
CompilerBase::TYPE_BOOL, Type::BOOL,
CompilerBase::TYPE_STR, Type::STR,
CompilerBase::TYPE_ARRAY, Type::ARRAY,
], true) && !$def->nullable; ], true) && !$def->nullable;
} }
@ -46,17 +47,17 @@ final class PropertyAssignTypeInfo
$check[] = ['kind' => 'isNull']; $check[] = ['kind' => 'isNull'];
} }
$scalarCheck = match ($def->type) { $scalarCheck = match ($def->type) {
CompilerBase::TYPE_INT => [['kind' => 'isInt']], Type::INT => [['kind' => 'isInt']],
CompilerBase::TYPE_FLOAT => [['kind' => 'isFloat'], ['kind' => 'isInt']], Type::FLOAT => [['kind' => 'isFloat'], ['kind' => 'isInt']],
CompilerBase::TYPE_BOOL => [['kind' => 'isBool']], Type::BOOL => [['kind' => 'isBool']],
CompilerBase::TYPE_STR => [['kind' => 'isString']], Type::STR => [['kind' => 'isString']],
CompilerBase::TYPE_ARRAY => [['kind' => 'isArray']], Type::ARRAY => [['kind' => 'isArray']],
default => null, default => null,
}; };
if ($scalarCheck !== null) { if ($scalarCheck !== null) {
return array_merge($check, $scalarCheck); return array_merge($check, $scalarCheck);
} }
if ($def->type !== CompilerBase::TYPE_OBJECT || $def->class === '') { if ($def->type !== Type::OBJECT || $def->class === '') {
return []; return [];
} }
@ -73,12 +74,12 @@ final class PropertyAssignTypeInfo
return ($def->nullable ? '?' : '') . $def->class; return ($def->nullable ? '?' : '') . $def->class;
} }
return match ($def->type) { return match ($def->type) {
CompilerBase::TYPE_INT => 'int', Type::INT => 'int',
CompilerBase::TYPE_FLOAT => 'float', Type::FLOAT => 'float',
CompilerBase::TYPE_BOOL => 'bool', Type::BOOL => 'bool',
CompilerBase::TYPE_STR => 'string', Type::STR => 'string',
CompilerBase::TYPE_ARRAY => 'array', Type::ARRAY => 'array',
CompilerBase::TYPE_OBJECT => 'object', Type::OBJECT => 'object',
default => $def->type, default => $def->type,
}; };
} }

@ -649,12 +649,12 @@ class Translator extends Preprocessor
$lines[] = '#include <phpx.h>'; $lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL; $lines[] = PHP_EOL;
foreach ($this->globalVars as $name => $type) { foreach ($this->globalVars as $name => $type) {
$lines[] = 'extern THREAD_LOCAL ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';'; $lines[] = 'extern THREAD_LOCAL ' . Type::VAR . ' ' . $this->escapeGlobalVar($name) . ';';
} }
if ($this->literalStrings) { if ($this->literalStrings) {
$literalStringsCount = count($this->literalStrings); $literalStringsCount = count($this->literalStrings);
$lines[] = 'extern ' . self::TYPE_STR . ' ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL; $lines[] = 'extern ' . Type::STR . ' ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
} }
// 确保数组大小至少为 1,避免 C/C++ 编译错误 // 确保数组大小至少为 1,避免 C/C++ 编译错误
@ -669,9 +669,9 @@ class Translator extends Preprocessor
foreach ($this->getClassLikesWithConstants() as $classDef) { foreach ($this->getClassLikesWithConstants() as $classDef) {
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name);
$lines[] = 'extern ' . self::TYPE_VAR . ' ' . $constName . ';' . PHP_EOL; $lines[] = 'extern ' . Type::VAR . ' ' . $constName . ';' . PHP_EOL;
} }
} }
} }
@ -713,7 +713,7 @@ class Translator extends Preprocessor
$code .= "// global vars \n"; $code .= "// global vars \n";
foreach ($this->globalVars as $name => $type) { foreach ($this->globalVars as $name => $type) {
$code .= 'THREAD_LOCAL ' . self::TYPE_VAR . ' ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL; $code .= 'THREAD_LOCAL ' . Type::VAR . ' ' . $this->escapeGlobalVar($name) . ';' . PHP_EOL;
} }
$code .= "// class register functions \n"; $code .= "// class register functions \n";
@ -767,9 +767,9 @@ CODE;
$code .= "// literal strings \n"; $code .= "// literal strings \n";
if ($this->literalStrings) { if ($this->literalStrings) {
$code .= self::TYPE_STR . ' ' . self::LITERAL_STRINGS . '[] = {' . PHP_EOL; $code .= Type::STR . ' ' . self::LITERAL_STRINGS . '[] = {' . PHP_EOL;
foreach ($this->literalStrings as $str => $index) { foreach ($this->literalStrings as $str => $index) {
$code .= self::TYPE_STR . '{ZEND_STRL("' . $this->escapeString($str) . '"), true}, // [' . $index . ']' . PHP_EOL; $code .= Type::STR . '{ZEND_STRL("' . $this->escapeString($str) . '"), true}, // [' . $index . ']' . PHP_EOL;
} }
$code .= '};' . PHP_EOL . PHP_EOL; $code .= '};' . PHP_EOL . PHP_EOL;
} else { } else {
@ -788,9 +788,9 @@ CODE;
$code .= 'static zend_object_handlers property_handlers_' . $classDef->getNamespacedName() . ";\n"; $code .= 'static zend_object_handlers property_handlers_' . $classDef->getNamespacedName() . ";\n";
} }
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name);
$code .= self::TYPE_VAR . ' ' . $constName . ";\n"; $code .= Type::VAR . ' ' . $constName . ";\n";
} }
} }
} }
@ -880,7 +880,7 @@ CODE;
} }
} }
foreach ($this->constants as $name => $const) { foreach ($this->constants as $name => $const) {
if ($const->type !== self::TYPE_VAR) { if ($const->type !== Type::VAR) {
continue; continue;
} }
$code .= $name . '.unset();' . PHP_EOL; $code .= $name . '.unset();' . PHP_EOL;
@ -889,7 +889,7 @@ CODE;
$code .= '// class array constants' . PHP_EOL; $code .= '// class array constants' . PHP_EOL;
foreach ($this->getClassLikesWithConstants() as $classDef) { foreach ($this->getClassLikesWithConstants() as $classDef) {
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name);
$code .= $constName . ".unset();\n"; $code .= $constName . ".unset();\n";
@ -904,7 +904,7 @@ CODE;
foreach ($this->symbols->classes() as $className => $classDef) { foreach ($this->symbols->classes() as $className => $classDef) {
$ownConstNames = []; $ownConstNames = [];
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
} }
} }
@ -913,7 +913,7 @@ CODE;
while ($parentName && $this->symbols->hasClass($parentName)) { while ($parentName && $this->symbols->hasClass($parentName)) {
$parentDef = $this->symbols->class($parentName); $parentDef = $this->symbols->class($parentName);
foreach ($parentDef->constants as $constant) { foreach ($parentDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) { if ($constant->type === Type::ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true); $classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name); $classConstStr = $this->genCharPtr($constant->name);
@ -929,7 +929,7 @@ CODE;
} }
$interfaceDef = $this->getInterface($interfaceName); $interfaceDef = $this->getInterface($interfaceName);
foreach ($interfaceDef->constants as $constant) { foreach ($interfaceDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) { if ($constant->type === Type::ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true); $classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name); $classConstStr = $this->genCharPtr($constant->name);
@ -1427,21 +1427,21 @@ CODE;
// 函数的默认值可能会使用字符串字面量,需要提前声明 // 函数的默认值可能会使用字符串字面量,需要提前声明
if ($this->literalStrings) { if ($this->literalStrings) {
$literalStringsCount = count($this->literalStrings); $literalStringsCount = count($this->literalStrings);
$code .= 'extern ' . self::TYPE_STR . ' ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL; $code .= 'extern ' . Type::STR . ' ' . self::LITERAL_STRINGS . '[' . $literalStringsCount . '];' . PHP_EOL;
} }
$code .= $this->genDefaultArgumentHelpers(); $code .= $this->genDefaultArgumentHelpers();
foreach ($this->symbols->functions() as $name => $func) { foreach ($this->symbols->functions() as $name => $func) {
$code .= 'extern ' . ($func->returnsByRef ? self::TYPE_REF : $func->returnType) . ' ' . self::PREFIX . $name . '('; $code .= 'extern ' . ($func->returnsByRef ? Type::REF : $func->returnType) . ' ' . self::PREFIX . $name . '(';
$list = []; $list = [];
if ($func->method) { if ($func->method) {
$list[] = self::TYPE_OBJECT . ' &this_'; $list[] = Type::OBJECT . ' &this_';
} }
$argInfoList = $func->argInfoList; $argInfoList = $func->argInfoList;
if ($argInfoList) { if ($argInfoList) {
foreach ($argInfoList as $argInfo) { foreach ($argInfoList as $argInfo) {
if ($argInfo->variadic) { if ($argInfo->variadic) {
$arg = self::TYPE_ARRAY . ' ' . $argInfo->name . ' = {}'; $arg = Type::ARRAY . ' ' . $argInfo->name . ' = {}';
} else { } else {
$arg = $this->genArgumentDeclaration($argInfo); $arg = $this->genArgumentDeclaration($argInfo);
if ($argInfo->default && !$this->isConstructorNativeFunction($func)) { if ($argInfo->default && !$this->isConstructorNativeFunction($func)) {
@ -1599,7 +1599,7 @@ CODE;
$code = ''; $code = '';
foreach ($this->getClassLikesWithConstants() as $classDef) { foreach ($this->getClassLikesWithConstants() as $classDef) {
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $classDef->namespace, $classDef->name);
$code .= "do {\n"; $code .= "do {\n";
$code .= $constant->arrayExpr; $code .= $constant->arrayExpr;
@ -1616,7 +1616,7 @@ CODE;
foreach ($this->symbols->classes() as $className => $classDef) { foreach ($this->symbols->classes() as $className => $classDef) {
$ownConstNames = []; $ownConstNames = [];
foreach ($classDef->constants as $constant) { foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) { if ($constant->type === Type::ARRAY) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
} }
} }
@ -1625,7 +1625,7 @@ CODE;
while ($parentName && $this->symbols->hasClass($parentName)) { while ($parentName && $this->symbols->hasClass($parentName)) {
$parentDef = $this->symbols->class($parentName); $parentDef = $this->symbols->class($parentName);
foreach ($parentDef->constants as $constant) { foreach ($parentDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) { if ($constant->type === Type::ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
$constName = self::PREFIX . $this->getNativeName($constant->name, $parentDef->namespace, $parentDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $parentDef->namespace, $parentDef->name);
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true); $classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
@ -1642,7 +1642,7 @@ CODE;
} }
$interfaceDef = $this->getInterface($interfaceName); $interfaceDef = $this->getInterface($interfaceName);
foreach ($interfaceDef->constants as $constant) { foreach ($interfaceDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY && !isset($ownConstNames[$constant->name])) { if ($constant->type === Type::ARRAY && !isset($ownConstNames[$constant->name])) {
$ownConstNames[$constant->name] = true; $ownConstNames[$constant->name] = true;
$constName = self::PREFIX . $this->getNativeName($constant->name, $interfaceDef->namespace, $interfaceDef->name); $constName = self::PREFIX . $this->getNativeName($constant->name, $interfaceDef->namespace, $interfaceDef->name);
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true); $classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
@ -2584,7 +2584,7 @@ CODE;
foreach ($functionDef->argInfoList as $k => $argInfo) { foreach ($functionDef->argInfoList as $k => $argInfo) {
$var = 'arg_' . $argInfo->name; $var = 'arg_' . $argInfo->name;
if ($argInfo->variadic) { if ($argInfo->variadic) {
$cppCode .= $this->getIndent() . self::TYPE_ARRAY . ' ' . $var . ';' . PHP_EOL; $cppCode .= $this->getIndent() . Type::ARRAY . ' ' . $var . ';' . PHP_EOL;
$cppCode .= $this->getIndent() . 'for (uint32_t i = ' . $k . '; i < php::getCallArgNum(); i++) {' . PHP_EOL; $cppCode .= $this->getIndent() . 'for (uint32_t i = ' . $k . '; i < php::getCallArgNum(); i++) {' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL; $cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL;
@ -2608,7 +2608,7 @@ CODE;
} }
$cppType = $this->getDefaultArgumentType($argInfo); $cppType = $this->getDefaultArgumentType($argInfo);
$declaredClass = $argInfo->declaredClass ?: $argInfo->class; $declaredClass = $argInfo->declaredClass ?: $argInfo->class;
if ($argInfo->type === self::TYPE_OBJECT && $declaredClass !== '') { if ($argInfo->type === Type::OBJECT && $declaredClass !== '') {
$expr = $this->convertObjectExpr($argExpr, $this->getClassEntryPtr($declaredClass)); $expr = $this->convertObjectExpr($argExpr, $this->getClassEntryPtr($declaredClass));
} else { } else {
$expr = $this->convertExprFromType($argInfo->type, $argExpr); $expr = $this->convertExprFromType($argInfo->type, $argExpr);
@ -2624,7 +2624,7 @@ CODE;
$callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : ''; $callParams = $functionDef->argInfoList ? rtrim($callParams, ',') : '';
} }
if ($functionDef->returnType !== self::TYPE_VOID) { if ($functionDef->returnType !== Type::VOID) {
$cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(' . $callParams . ');' . PHP_EOL; $cppCode .= $this->getIndent() . 'auto retval = ' . $fn . '(' . $callParams . ');' . PHP_EOL;
$cppCode .= $this->getIndent() . 'php::move(retval, return_value);' . PHP_EOL; $cppCode .= $this->getIndent() . 'php::move(retval, return_value);' . PHP_EOL;
if (!$functionDef->returnsByRef) { if (!$functionDef->returnsByRef) {
@ -2662,7 +2662,7 @@ CODE;
{ {
$name = $classDef->getNamespacedName(); $name = $classDef->getNamespacedName();
$cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . '){' . PHP_EOL; $cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . '){' . PHP_EOL;
$cppCode .= $this->getIndent() . self::TYPE_OBJECT . ' this_(&execute_data->This);' . PHP_EOL; $cppCode .= $this->getIndent() . Type::OBJECT . ' this_(&execute_data->This);' . PHP_EOL;
$fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef); $fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef);
$cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef, $classDef->getNamespacedName(false) . '::' . $methodDef->name); $cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef, $classDef->getNamespacedName(false) . '::' . $methodDef->name);
@ -2690,7 +2690,7 @@ CODE;
if ($classDef instanceof ClassDef) { if ($classDef instanceof ClassDef) {
$arrayPropCount = 0; $arrayPropCount = 0;
foreach ($classDef->properties as $property) { foreach ($classDef->properties as $property) {
if ($property->type === self::TYPE_ARRAY && $property->arrayInitPlan && $property->default && !$property->isStatic()) { if ($property->type === Type::ARRAY && $property->arrayInitPlan && $property->default && !$property->isStatic()) {
$arrayPropCount++; $arrayPropCount++;
} }
} }
@ -2800,10 +2800,10 @@ CODE;
} }
if ($this->class) { if ($this->class) {
$this->addArgument('this_', self::TYPE_OBJECT); $this->addArgument('this_', Type::OBJECT);
} }
foreach ($this->functionDef->argInfoList as $argInfo) { foreach ($this->functionDef->argInfoList as $argInfo) {
$this->addArgument($argInfo->name, $argInfo->variadic ? self::TYPE_ARRAY : $argInfo->type); $this->addArgument($argInfo->name, $argInfo->variadic ? Type::ARRAY : $argInfo->type);
if (!$argInfo->variadic and $argInfo->declaredClass) { if (!$argInfo->variadic and $argInfo->declaredClass) {
$this->addObject($argInfo->name, $argInfo->declaredClass); $this->addObject($argInfo->name, $argInfo->declaredClass);
} }
@ -2853,10 +2853,10 @@ CODE;
$stmts = $this->genReturnCode(); $stmts = $this->genReturnCode();
} }
$cppReturnType = $this->functionDef->returnsByRef ? self::TYPE_REF : $this->getReturnType(); $cppReturnType = $this->functionDef->returnsByRef ? Type::REF : $this->getReturnType();
$functionDeclCode = $cppReturnType . ' ' . self::PREFIX . $name . '('; $functionDeclCode = $cppReturnType . ' ' . self::PREFIX . $name . '(';
if ($this->class) { if ($this->class) {
$functionDeclCode .= self::TYPE_OBJECT . ' &this_'; $functionDeclCode .= Type::OBJECT . ' &this_';
if ($this->functionDef->params) { if ($this->functionDef->params) {
$functionDeclCode .= ', '; $functionDeclCode .= ', ';
} }
@ -3043,13 +3043,13 @@ CODE;
if ($parentFuncDef->returnTypeCheck || $childFuncDef->returnTypeCheck) { if ($parentFuncDef->returnTypeCheck || $childFuncDef->returnTypeCheck) {
return $parentFuncDef->returnTypeStr === $childFuncDef->returnTypeStr; return $parentFuncDef->returnTypeStr === $childFuncDef->returnTypeStr;
} }
if ($parentFuncDef->returnType === self::TYPE_VAR) { if ($parentFuncDef->returnType === Type::VAR) {
return true; return true;
} }
if ($childFuncDef->returnType !== $parentFuncDef->returnType) { if ($childFuncDef->returnType !== $parentFuncDef->returnType) {
return false; return false;
} }
if ($parentFuncDef->returnType !== self::TYPE_OBJECT) { if ($parentFuncDef->returnType !== Type::OBJECT) {
return true; return true;
} }
if ($childFuncDef->returnClass === $parentFuncDef->returnClass) { if ($childFuncDef->returnClass === $parentFuncDef->returnClass) {
@ -3084,7 +3084,7 @@ CODE;
if ($childArg->type !== $parentArg->type) { if ($childArg->type !== $parentArg->type) {
return false; return false;
} }
if ($parentArg->type !== self::TYPE_OBJECT) { if ($parentArg->type !== Type::OBJECT) {
return true; return true;
} }
if ($childArg->class === $parentArg->class) { if ($childArg->class === $parentArg->class) {
@ -3108,13 +3108,13 @@ CODE;
} }
return match ($arg->type) { return match ($arg->type) {
self::TYPE_INT => [['kind' => 'isInt']], Type::INT => [['kind' => 'isInt']],
self::TYPE_FLOAT => [['kind' => 'isFloat']], Type::FLOAT => [['kind' => 'isFloat']],
self::TYPE_BOOL => [['kind' => 'isBool']], Type::BOOL => [['kind' => 'isBool']],
self::TYPE_STR => [['kind' => 'isString']], Type::STR => [['kind' => 'isString']],
self::TYPE_ARRAY => [['kind' => 'isArray']], Type::ARRAY => [['kind' => 'isArray']],
self::TYPE_RESOURCE => [['kind' => 'isResource']], Type::RESOURCE => [['kind' => 'isResource']],
self::TYPE_OBJECT => $arg->class Type::OBJECT => $arg->class
? [['kind' => 'instanceof', 'class' => $arg->class]] ? [['kind' => 'instanceof', 'class' => $arg->class]]
: [['kind' => 'isObject']], : [['kind' => 'isObject']],
default => null, default => null,
@ -3484,10 +3484,10 @@ CODE;
} }
$argv = implode(', ', $argList); $argv = implode(', ', $argList);
$cppReturnType = $methodDef->functionDef->returnsByRef ? self::TYPE_REF : $methodDef->getReturnType(); $cppReturnType = $methodDef->functionDef->returnsByRef ? Type::REF : $methodDef->getReturnType();
$code = $cppReturnType . ' ' . self::PREFIX . $classMethodNativeName . '('; $code = $cppReturnType . ' ' . self::PREFIX . $classMethodNativeName . '(';
if ($this->class) { if ($this->class) {
$code .= self::TYPE_OBJECT . ' &this_'; $code .= Type::OBJECT . ' &this_';
if ($methodDef->functionDef->params) { if ($methodDef->functionDef->params) {
$code .= ', '; $code .= ', ';
} }
@ -3499,7 +3499,7 @@ CODE;
$code .= '{' . PHP_EOL; $code .= '{' . PHP_EOL;
$this->indentLevel++; $this->indentLevel++;
$methodCall = self::PREFIX . $traitMethodNativeName . '(' . $argv . ')'; $methodCall = self::PREFIX . $traitMethodNativeName . '(' . $argv . ')';
if ($cppReturnType !== self::TYPE_VOID) { if ($cppReturnType !== Type::VOID) {
$methodCall = 'return ' . $methodCall; $methodCall = 'return ' . $methodCall;
} }
$code .= $this->getIndent() . $methodCall . ';' . PHP_EOL; $code .= $this->getIndent() . $methodCall . ';' . PHP_EOL;
@ -3529,16 +3529,16 @@ CODE;
{ {
$obj = $objectExpr ?? $this->parseIdentifier($node->expr); $obj = $objectExpr ?? $this->parseIdentifier($node->expr);
$iterableVar = $this->genTmpVarName(); $iterableVar = $this->genTmpVarName();
$this->addLocalVar($iterableVar, self::TYPE_VAR); $this->addLocalVar($iterableVar, Type::VAR);
$iteratorObj = $this->genTmpVarName(); $iteratorObj = $this->genTmpVarName();
$this->addLocalVar($iteratorObj, self::TYPE_OBJECT); $this->addLocalVar($iteratorObj, Type::OBJECT);
$aggregateObj = $this->genTmpVarName(); $aggregateObj = $this->genTmpVarName();
$this->addLocalVar($aggregateObj, self::TYPE_OBJECT); $this->addLocalVar($aggregateObj, Type::OBJECT);
$tmpArrayVar = $this->genTmpVarName(); $tmpArrayVar = $this->genTmpVarName();
$this->addLocalVar($tmpArrayVar, self::TYPE_ARRAY); $this->addLocalVar($tmpArrayVar, Type::ARRAY);
$IteratorAggregateCe = $this->getClassEntryPtr('IteratorAggregate'); $IteratorAggregateCe = $this->getClassEntryPtr('IteratorAggregate');
$IteratorCe = $this->getClassEntryPtr('Iterator'); $IteratorCe = $this->getClassEntryPtr('Iterator');

@ -0,0 +1,27 @@
<?php
namespace TypePhp;
final class Type
{
public const string VAR = 'php::Var';
public const string BOOL = 'php::Bool';
public const string INT = 'php::Int';
public const string FLOAT = 'php::Float';
public const string OBJECT = 'php::Object';
public const string ARRAY = 'php::Array';
public const string RESOURCE = 'php::Resource';
public const string STREAM = 'php::Stream';
public const string BIGINT = 'php::BigInt';
public const string DECIMAL = 'php::Decimal';
public const string BIGFLOAT = 'php::BigFloat';
public const string BOX = 'php::Box';
public const string STD_ARRAY = 'php::StdArray';
public const string STD_VECTOR = 'php::StdVector';
public const string STD_MAP = 'php::StdMap';
public const string STD_ORDERED_MAP = 'php::StdOrderedMap';
public const string ARGS = 'php::Args';
public const string STR = 'php::Str';
public const string REF = 'php::Ref';
public const string VOID = 'void';
}

@ -7,6 +7,8 @@
namespace TypePhp\TypeSystem; namespace TypePhp\TypeSystem;
use TypePhp\Type;
use PhpParser\Node\Expr; use PhpParser\Node\Expr;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
@ -33,7 +35,7 @@ trait CompositeTypeCheckerTrait
// TYPE_VAR means that the expression is dynamic or its result cannot // TYPE_VAR means that the expression is dynamic or its result cannot
// be represented by the current scalar type system. It must retain the // be represented by the current scalar type system. It must retain the
// runtime type check. // runtime type check.
if ($this->detectTypeOfExpr($value) === self::TYPE_VAR && !$this->isNullExpr($value)) { if ($this->detectTypeOfExpr($value) === Type::VAR && !$this->isNullExpr($value)) {
return self::COMPOSITE_TYPE_UNKNOWN; return self::COMPOSITE_TYPE_UNKNOWN;
} }
@ -77,19 +79,19 @@ trait CompositeTypeCheckerTrait
$type = $this->detectTypeOfExpr($value); $type = $this->detectTypeOfExpr($value);
return match ($kind) { return match ($kind) {
'isInt' => $this->exactCompositeTypeRelation($type, self::TYPE_INT), 'isInt' => $this->exactCompositeTypeRelation($type, Type::INT),
// PHP permits int -> float widening. It is compatible but still // PHP permits int -> float widening. It is compatible but still
// needs conversion, so retain the runtime normalization path. // needs conversion, so retain the runtime normalization path.
'isFloat' => $type === self::TYPE_INT 'isFloat' => $type === Type::INT
? self::COMPOSITE_TYPE_UNKNOWN ? self::COMPOSITE_TYPE_UNKNOWN
: $this->exactCompositeTypeRelation($type, self::TYPE_FLOAT), : $this->exactCompositeTypeRelation($type, Type::FLOAT),
'isBool' => $this->exactCompositeTypeRelation($type, self::TYPE_BOOL), 'isBool' => $this->exactCompositeTypeRelation($type, Type::BOOL),
'isString' => $this->exactCompositeTypeRelation($type, self::TYPE_STR), 'isString' => $this->exactCompositeTypeRelation($type, Type::STR),
'isArray' => $this->exactCompositeTypeRelation($type, self::TYPE_ARRAY), 'isArray' => $this->exactCompositeTypeRelation($type, Type::ARRAY),
'isObject' => $this->exactCompositeTypeRelation($type, self::TYPE_OBJECT), 'isObject' => $this->exactCompositeTypeRelation($type, Type::OBJECT),
'isTrue' => $this->compositeLiteralBoolRelation($value, true), 'isTrue' => $this->compositeLiteralBoolRelation($value, true),
'isFalse' => $this->compositeLiteralBoolRelation($value, false), 'isFalse' => $this->compositeLiteralBoolRelation($value, false),
'isResource' => $this->exactCompositeTypeRelation($type, self::TYPE_RESOURCE), 'isResource' => $this->exactCompositeTypeRelation($type, Type::RESOURCE),
'callable' => $this->compositeCallableRelation($value, $type), 'callable' => $this->compositeCallableRelation($value, $type),
'iterable' => $this->compositeIterableRelation($value, $type), 'iterable' => $this->compositeIterableRelation($value, $type),
'instanceof' => $this->compositeObjectEntryRelation($value, $entry), 'instanceof' => $this->compositeObjectEntryRelation($value, $entry),
@ -108,14 +110,14 @@ trait CompositeTypeCheckerTrait
$actual = strcasecmp($value->name->toString(), 'true') === 0; $actual = strcasecmp($value->name->toString(), 'true') === 0;
return $actual === $expected ? self::COMPOSITE_TYPE_MATCH : self::COMPOSITE_TYPE_MISMATCH; return $actual === $expected ? self::COMPOSITE_TYPE_MATCH : self::COMPOSITE_TYPE_MISMATCH;
} }
return $this->detectTypeOfExpr($value) === self::TYPE_BOOL return $this->detectTypeOfExpr($value) === Type::BOOL
? self::COMPOSITE_TYPE_UNKNOWN ? self::COMPOSITE_TYPE_UNKNOWN
: self::COMPOSITE_TYPE_MISMATCH; : self::COMPOSITE_TYPE_MISMATCH;
} }
protected function compositeCallableRelation(NodeAbstract $value, string $type): int protected function compositeCallableRelation(NodeAbstract $value, string $type): int
{ {
if ($type === self::TYPE_STR || $type === self::TYPE_ARRAY || $type === self::TYPE_OBJECT) { if ($type === Type::STR || $type === Type::ARRAY || $type === Type::OBJECT) {
return self::COMPOSITE_TYPE_UNKNOWN; return self::COMPOSITE_TYPE_UNKNOWN;
} }
return self::COMPOSITE_TYPE_MISMATCH; return self::COMPOSITE_TYPE_MISMATCH;
@ -123,10 +125,10 @@ trait CompositeTypeCheckerTrait
protected function compositeIterableRelation(NodeAbstract $value, string $type): int protected function compositeIterableRelation(NodeAbstract $value, string $type): int
{ {
if ($type === self::TYPE_ARRAY) { if ($type === Type::ARRAY) {
return self::COMPOSITE_TYPE_MATCH; return self::COMPOSITE_TYPE_MATCH;
} }
if ($type !== self::TYPE_OBJECT) { if ($type !== Type::OBJECT) {
return self::COMPOSITE_TYPE_MISMATCH; return self::COMPOSITE_TYPE_MISMATCH;
} }
return $this->compositeObjectTypeRelation($value, 'Traversable'); return $this->compositeObjectTypeRelation($value, 'Traversable');
@ -134,7 +136,7 @@ trait CompositeTypeCheckerTrait
protected function compositeObjectEntryRelation(NodeAbstract $value, array $entry): int protected function compositeObjectEntryRelation(NodeAbstract $value, array $entry): int
{ {
if ($this->detectTypeOfExpr($value) !== self::TYPE_OBJECT) { if ($this->detectTypeOfExpr($value) !== Type::OBJECT) {
return self::COMPOSITE_TYPE_MISMATCH; return self::COMPOSITE_TYPE_MISMATCH;
} }
@ -182,12 +184,12 @@ trait CompositeTypeCheckerTrait
} }
$type = $this->detectTypeOfExpr($expr); $type = $this->detectTypeOfExpr($expr);
return match ($type) { return match ($type) {
self::TYPE_INT => 'int', Type::INT => 'int',
self::TYPE_FLOAT => 'float', Type::FLOAT => 'float',
self::TYPE_BOOL => 'bool', Type::BOOL => 'bool',
self::TYPE_STR => 'string', Type::STR => 'string',
self::TYPE_ARRAY => 'array', Type::ARRAY => 'array',
self::TYPE_OBJECT => 'object', Type::OBJECT => 'object',
default => 'mixed', default => 'mixed',
}; };
} }

@ -7,6 +7,8 @@
namespace TypePhp\TypeSystem; namespace TypePhp\TypeSystem;
use TypePhp\Type;
use PhpParser\Node; use PhpParser\Node;
use TypePhp\Entity\ArgInfo; use TypePhp\Entity\ArgInfo;
@ -15,8 +17,8 @@ trait NativeTypeCompatibilityTrait
protected function getReturnType(): string protected function getReturnType(): string
{ {
$type = $this->functionDef->returnType; $type = $this->functionDef->returnType;
if ($type === self::TYPE_STREAM) { if ($type === Type::STREAM) {
return self::TYPE_VAR; return Type::VAR;
} }
return $type; return $type;
} }
@ -169,7 +171,7 @@ trait NativeTypeCompatibilityTrait
$var = $this->parseVariable($arg->value); $var = $this->parseVariable($arg->value);
// 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用 // 若参数是引用类型,可以传入未定义变量,将立即创建变量作为引用
if (!$this->hasLocalVar($var)) { if (!$this->hasLocalVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR); $this->addLocalVar($var, Type::VAR);
} }
} }
return $this->convertToRef($arg->value); return $this->convertToRef($arg->value);
@ -180,14 +182,14 @@ trait NativeTypeCompatibilityTrait
$this->checkVarAssignExpr($arg, $argInfo->type, $type); $this->checkVarAssignExpr($arg, $argInfo->type, $type);
if ($argInfo->type === self::TYPE_VAR && $this->isVarExpr($arg->value)) { if ($argInfo->type === Type::VAR && $this->isVarExpr($arg->value)) {
$varName = $this->parseIdentifier($arg->value); $varName = $this->parseIdentifier($arg->value);
if ($this->isStdContainer($varName)) { if ($this->isStdContainer($varName)) {
return $varName; return $varName;
} }
} }
if ($argInfo->type === self::TYPE_OBJECT) { if ($argInfo->type === Type::OBJECT) {
$declaredClass = $argInfo->declaredClass ?: $argInfo->class; $declaredClass = $argInfo->declaredClass ?: $argInfo->class;
if ($declaredClass !== '') { if ($declaredClass !== '') {
$class = $this->detectDeclaredClassOfExpr($arg->value); $class = $this->detectDeclaredClassOfExpr($arg->value);
@ -197,7 +199,7 @@ trait NativeTypeCompatibilityTrait
// 如果无法证明,但右值是已知 concrete object,说明一定不兼容,直接编译期 fatal; // 如果无法证明,但右值是已知 concrete object,说明一定不兼容,直接编译期 fatal;
// 其他动态/外部库/any 场景保留 php::toObject() 作为运行时兜底。 // 其他动态/外部库/any 场景保留 php::toObject() 作为运行时兜底。
if ($this->isObjectClassStaticallyAssignableTo($class, $declaredClass)) { if ($this->isObjectClassStaticallyAssignableTo($class, $declaredClass)) {
return $type === self::TYPE_OBJECT ? $expr : $this->convertObjectExpr($expr); return $type === Type::OBJECT ? $expr : $this->convertObjectExpr($expr);
} }
if ($this->isKnownConcreteObjectExpr($arg->value, $class)) { if ($this->isKnownConcreteObjectExpr($arg->value, $class)) {
$argName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name); $argName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name);
@ -206,7 +208,7 @@ trait NativeTypeCompatibilityTrait
} }
return $this->convertObjectExpr($expr, $this->getClassEntryPtr($declaredClass)); return $this->convertObjectExpr($expr, $this->getClassEntryPtr($declaredClass));
} }
return $type === self::TYPE_OBJECT ? $expr : $this->convertObjectExpr($expr); return $type === Type::OBJECT ? $expr : $this->convertObjectExpr($expr);
} }
return $this->convertExprType($expr, $argInfo->type, $type); return $this->convertExprType($expr, $argInfo->type, $type);

Loading…
Cancel
Save