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

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

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

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

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

@ -143,19 +143,6 @@ class CompilerBase implements PropertyAccessContext
use LoopVarOptimizer;
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_DYNAMIC = 'dynamic';
protected const int COMPOSITE_TYPE_MISMATCH = -1;
@ -169,18 +156,18 @@ class CompilerBase implements PropertyAccessContext
* Use findKeywordMethod() for unified lookup including keyword extension methods.
*/
public const array KEYWORD_METHOD_MAP = [
'toInt' => self::TYPE_INT,
'toFloat' => self::TYPE_FLOAT,
'toString' => self::TYPE_STR,
'toBool' => self::TYPE_BOOL,
'toArray' => self::TYPE_ARRAY,
'toStream' => self::TYPE_STREAM,
'toBigInt' => self::TYPE_BIGINT,
'toBigFloat' => self::TYPE_BIGFLOAT,
'toDecimal' => self::TYPE_DECIMAL,
'toObject' => self::TYPE_OBJECT,
'toAny' => self::TYPE_VAR,
'toRef' => self::TYPE_REF,
'toInt' => Type::INT,
'toFloat' => Type::FLOAT,
'toString' => Type::STR,
'toBool' => Type::BOOL,
'toArray' => Type::ARRAY,
'toStream' => Type::STREAM,
'toBigInt' => Type::BIGINT,
'toBigFloat' => Type::BIGFLOAT,
'toDecimal' => Type::DECIMAL,
'toObject' => Type::OBJECT,
'toAny' => Type::VAR,
'toRef' => Type::REF,
];
private const array STREAM_FUNCTIONS = [
@ -191,15 +178,6 @@ class CompilerBase implements PropertyAccessContext
'stream_socket_accept',
'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_PROPERTY = 2;
public const int DECL_TYPE_OF_CONST = 3;
@ -267,30 +245,30 @@ class CompilerBase implements PropertyAccessContext
protected int $propIndex = 0;
protected array $propMap = [];
protected array $zendTypeMap = [
'int' => self::TYPE_INT,
'float' => self::TYPE_FLOAT,
'double' => self::TYPE_FLOAT,
'bool' => self::TYPE_BOOL,
'false' => self::TYPE_BOOL,
'true' => self::TYPE_BOOL,
'void' => self::TYPE_VOID,
'never' => self::TYPE_VOID,
'string' => self::TYPE_STR,
'array' => self::TYPE_ARRAY,
'object' => self::TYPE_OBJECT,
'mixed' => self::TYPE_VAR,
'null' => self::TYPE_VAR,
'any' => self::TYPE_VAR,
'int' => Type::INT,
'float' => Type::FLOAT,
'double' => Type::FLOAT,
'bool' => Type::BOOL,
'false' => Type::BOOL,
'true' => Type::BOOL,
'void' => Type::VOID,
'never' => Type::VOID,
'string' => Type::STR,
'array' => Type::ARRAY,
'object' => Type::OBJECT,
'mixed' => Type::VAR,
'null' => Type::VAR,
'any' => Type::VAR,
// callable 类型,可以是字符串、数组、对象
// 1) 'foo' 函数名称字符串, 2) [ $obj, 'bar' ] 对象方法数组, 3) Closure 对象, 4) [ 'class', 'staticMethod'] 类名+静态方法数组
'callable' => self::TYPE_VAR,
'callable' => Type::VAR,
// iterable 类型,可以是数组或者对象
'iterable' => self::TYPE_VAR,
'stream' => self::TYPE_STREAM,
'bigint' => self::TYPE_BIGINT,
'bigfloat' => self::TYPE_BIGFLOAT,
'decimal' => self::TYPE_DECIMAL,
'box' => self::TYPE_BOX,
'iterable' => Type::VAR,
'stream' => Type::STREAM,
'bigint' => Type::BIGINT,
'bigfloat' => Type::BIGFLOAT,
'decimal' => Type::DECIMAL,
'box' => Type::BOX,
];
protected array $globalHeaders = [
'phpx.h',
@ -408,15 +386,15 @@ class CompilerBase implements PropertyAccessContext
private ?DiagnosticReporter $diagnosticReporter = null;
protected FunctionContext $context;
protected array $superGlobalVars = [
'_GET' => self::TYPE_ARRAY,
'_POST' => self::TYPE_ARRAY,
'_COOKIE' => self::TYPE_ARRAY,
'_SERVER' => self::TYPE_ARRAY,
'_FILES' => self::TYPE_ARRAY,
'_SESSION' => self::TYPE_ARRAY,
'_REQUEST' => self::TYPE_ARRAY,
'_ENV' => self::TYPE_ARRAY,
'GLOBALS' => self::TYPE_ARRAY,
'_GET' => Type::ARRAY,
'_POST' => Type::ARRAY,
'_COOKIE' => Type::ARRAY,
'_SERVER' => Type::ARRAY,
'_FILES' => Type::ARRAY,
'_SESSION' => Type::ARRAY,
'_REQUEST' => Type::ARRAY,
'_ENV' => Type::ARRAY,
'GLOBALS' => Type::ARRAY,
];
protected array $globalVars = [];
protected bool $nativeTypes = false;
@ -649,7 +627,7 @@ class CompilerBase implements PropertyAccessContext
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
@ -976,7 +954,7 @@ class CompilerBase implements PropertyAccessContext
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
{
return $this->detectTypeOfExpr($expr) === self::TYPE_VOID;
return $this->detectTypeOfExpr($expr) === Type::VOID;
}
protected function wrapVoidExprAsNull(NodeAbstract $expr, string $exprCode): string
@ -1210,7 +1188,7 @@ class CompilerBase implements PropertyAccessContext
if (isset($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->ceWrappers[$className] = $object;
return $object;
@ -1259,7 +1237,7 @@ class CompilerBase implements PropertyAccessContext
*/
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
@ -1518,7 +1496,7 @@ class CompilerBase implements PropertyAccessContext
$code = '';
$this->appendCapturedStmtLines($code, $beforeStmts);
if ($afterStmts) {
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$tmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $condExpr . ';' . PHP_EOL;
$this->appendCapturedStmtLines($code, $afterStmts);
$condExpr = $tmpVar;
@ -1874,7 +1852,7 @@ class CompilerBase implements PropertyAccessContext
{
if ($this->functionDef->returnsByRef) {
if ($v->expr === null) {
return 'return ' . self::TYPE_REF . '{};';
return 'return ' . Type::REF . '{};';
}
if (!$this->isVarExpr($v->expr)
&& !$this->isPropertyFetch($v->expr)
@ -1887,7 +1865,7 @@ class CompilerBase implements PropertyAccessContext
if (!$this->hasVar($name)) {
$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;
foreach ($this->functionDef->argInfoList as $argInfo) {
if ($argInfo->name === $name) {
@ -1900,7 +1878,7 @@ class CompilerBase implements PropertyAccessContext
}
// The declaration is emitted after parsing the body, so a local can
// 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();';
}
@ -1928,7 +1906,7 @@ class CompilerBase implements PropertyAccessContext
'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;';
} elseif ($this->shouldCheckClosureReturnType()) {
return $this->genClosureCheckedReturn(self::VALUE_NULL);
@ -1965,11 +1943,11 @@ class CompilerBase implements PropertyAccessContext
// 匿名函数的返回值一定是 var
if (!$this->context->inClosure) {
if ($returnType === 'void') {
if ($returnType === Type::VOID) {
$this->fatalError($v, 'The return type is void, cannot return any value');
}
} else {
$returnType = self::TYPE_VAR;
$returnType = Type::VAR;
}
$returnObjectCheckClass = '';
@ -2053,7 +2031,7 @@ class CompilerBase implements PropertyAccessContext
protected function genCheckedReturnAssignment(string $exprCode, bool $closure): array
{
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
$code = $tmpVar . ' = ' . $exprCode . ';' . PHP_EOL;
$code .= $closure ? $this->genClosureReturnCheck($tmpVar) : $this->genUnionReturnCheck($tmpVar);
@ -2244,7 +2222,7 @@ class CompilerBase implements PropertyAccessContext
continue;
}
$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);
}
$expr->setAttribute('nativeConst', $interfaceConstDef);
@ -2257,7 +2235,7 @@ class CompilerBase implements PropertyAccessContext
if ($classDef instanceof ClassDef && !$this->checkAccessible($classDef, $constDef->flags)) {
$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);
} else {
$expr->setAttribute('nativeConst', $constDef);
@ -2321,7 +2299,7 @@ class CompilerBase implements PropertyAccessContext
}
$name = $this->parseIdentifier($var);
if ($this->isStdContainer($name)) {
return self::TYPE_ARRAY;
return Type::ARRAY;
}
return $this->getVarType($name);
}
@ -2334,29 +2312,29 @@ class CompilerBase implements PropertyAccessContext
return $this->detectTypeOfExpr($expr->expr);
case 'Expr_BitwiseNot':
$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_Cast_Int':
return self::TYPE_INT;
return Type::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_Double':
return self::TYPE_FLOAT;
return Type::FLOAT;
case 'Scalar_Float':
if ($this->isBigIntLiteral($expr)) {
return self::TYPE_BIGINT;
return Type::BIGINT;
}
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 'Scalar_Bool':
return self::TYPE_BOOL;
return Type::BOOL;
case 'Expr_Array':
case 'Expr_Cast_Array':
return self::TYPE_ARRAY;
return Type::ARRAY;
case 'Expr_BinaryOp_Plus':
case 'Expr_BinaryOp_Minus':
case 'Expr_BinaryOp_Mul':
@ -2371,24 +2349,24 @@ class CompilerBase implements PropertyAccessContext
case 'Expr_BinaryOp_BooleanAnd':
$leftType = $this->detectTypeOfExpr($expr->left);
$rightType = $this->detectTypeOfExpr($expr->right);
if ($leftType === self::TYPE_BIGFLOAT || $rightType === self::TYPE_BIGFLOAT) {
return self::TYPE_BIGFLOAT;
if ($leftType === Type::BIGFLOAT || $rightType === Type::BIGFLOAT) {
return Type::BIGFLOAT;
}
if ($leftType === self::TYPE_DECIMAL || $rightType === self::TYPE_DECIMAL) {
return self::TYPE_DECIMAL;
if ($leftType === Type::DECIMAL || $rightType === 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') {
// 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) {
return self::TYPE_FLOAT;
if ($leftType === Type::FLOAT || $rightType === Type::FLOAT) {
return Type::FLOAT;
}
if ($leftType === self::TYPE_INT || $rightType === self::TYPE_INT) {
return self::TYPE_INT;
if ($leftType === Type::INT || $rightType === Type::INT) {
return Type::INT;
}
break;
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)) {
$argType = $this->detectTypeOfExpr($expr->args[0]->value);
if (
$argType === self::TYPE_BIGINT
$argType === Type::BIGINT
&& in_array($name, ['abs', 'pow', 'sqrt'], true)
) {
return self::TYPE_BIGINT;
return Type::BIGINT;
}
if (
$argType === self::TYPE_DECIMAL
$argType === Type::DECIMAL
&& in_array($name, ['abs', 'pow', 'sqrt', 'floor', 'ceil', 'round'], true)
) {
return self::TYPE_DECIMAL;
return Type::DECIMAL;
}
if (
$argType === self::TYPE_BIGFLOAT
$argType === Type::BIGFLOAT
&& in_array($name, ['abs', 'sqrt'], true)
) {
return self::TYPE_BIGFLOAT;
return Type::BIGFLOAT;
}
}
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])) {
return self::TYPE_OBJECT;
return Type::OBJECT;
}
if ($this->hasFunction($name)) {
return $this->getFunction($name)->returnType;
@ -2440,7 +2418,7 @@ class CompilerBase implements PropertyAccessContext
$classDef = $this->resolveObjectClassDef($expr->var);
if ($classDef !== null && $classDef->hasMethod($method)) {
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT;
return Type::OBJECT;
}
return $classDef->getMethod($method)->getReturnType();
}
@ -2462,7 +2440,7 @@ class CompilerBase implements PropertyAccessContext
} else {
$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);
if ($retType !== null) {
return $retType;
@ -2474,19 +2452,19 @@ class CompilerBase implements PropertyAccessContext
if ($this->isNameExpr($expr->class) && $this->isIdExpr($expr->name)) {
// First-class callable syntax creates a Closure, not a method return value
if (count($expr->args) === 1 and $this->isPlaceholderExpr($expr->args[0])) {
return self::TYPE_OBJECT;
return Type::OBJECT;
}
$className = $this->parseIdentifier($expr->class);
if (strtolower($className) === 'std') {
$method = strtolower($this->parseIdentifier($expr->name));
return match ($method) {
'int' => self::TYPE_INT,
'float' => self::TYPE_FLOAT,
'bool' => self::TYPE_BOOL,
'bigint' => self::TYPE_BIGINT,
'decimal' => self::TYPE_DECIMAL,
'bigfloat' => self::TYPE_BIGFLOAT,
default => self::TYPE_VAR,
'int' => Type::INT,
'float' => Type::FLOAT,
'bool' => Type::BOOL,
'bigint' => Type::BIGINT,
'decimal' => Type::DECIMAL,
'bigfloat' => Type::BIGFLOAT,
default => Type::VAR,
};
}
if ($className === 'self') {
@ -2552,7 +2530,7 @@ class CompilerBase implements PropertyAccessContext
if ($attr['accessLevel'] === $attr['totalLevel']) {
return $this->context->stdArrays[$attr['var']]['type'];
} else {
return self::TYPE_ARRAY;
return Type::ARRAY;
}
}
if ($this->isStdContainerExpr($expr)) {
@ -2564,7 +2542,7 @@ class CompilerBase implements PropertyAccessContext
}
break;
case 'Expr_New':
return self::TYPE_OBJECT;
return Type::OBJECT;
case 'Expr_Assign':
case 'Expr_AssignOp_BitwiseAnd':
case 'Expr_AssignOp_BitwiseOr':
@ -2575,12 +2553,12 @@ class CompilerBase implements PropertyAccessContext
case 'Expr_ConstFetch':
return $this->detectConstType($expr);
case 'Scalar_String':
return self::TYPE_STR;
return Type::STR;
default:
break;
}
return self::TYPE_VAR;
return Type::VAR;
}
protected function genDynamicPropIncDec($var, string $op, bool $isPre): ?string
@ -2597,14 +2575,14 @@ class CompilerBase implements PropertyAccessContext
}
if ($getter !== null && $setter !== null) {
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
$read = $this->emitPropertyHookGetterCall($var, $getter);
if ($isPre) {
$set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($tmpVar));
$this->context->beforeStmtLines[] = "{$tmpVar} = {$read} {$op} 1; {$set};";
} else {
$nextVar = $this->genTmpVarName();
$this->addLocalVar($nextVar, self::TYPE_VAR);
$this->addLocalVar($nextVar, Type::VAR);
$set = $this->emitPropertyHookSetterCall($var, $setter, new Expr\Variable($nextVar));
$this->context->beforeStmtLines[] = "{$tmpVar} = {$read};";
$this->context->afterStmtLines[] = "{$nextVar} = {$tmpVar} {$op} 1; {$set};";
@ -2616,7 +2594,7 @@ class CompilerBase implements PropertyAccessContext
}
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
if ($isPre) {
$this->context->beforeStmtLines[] = "{$tmpVar} = " . $this->emitDynamicPropertyFetchRead($var, $target) . " {$op} 1; " . $this->emitDynamicPropertyFetchWrite($var, $tmpVar, $target) . ';';
} else {
@ -2636,7 +2614,7 @@ class CompilerBase implements PropertyAccessContext
}
$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).');
}
$result = '++' . $this->parseWritableIdentifier($expr->var);
@ -2884,7 +2862,7 @@ class CompilerBase implements PropertyAccessContext
$this->errorUndefinedVariable($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 === '+' ? '++' : '--';
$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);
$prop = $this->identifierToStr($expr->var->name);
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . Symbol::getStaticProperty() . '(' . $class . ', ' . $prop . ');';
$this->context->afterStmtLines[] = Symbol::setStaticProperty() . '(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);';
@ -2927,7 +2905,7 @@ class CompilerBase implements PropertyAccessContext
}
$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).');
}
$result = '--' . $this->parseWritableIdentifier($expr->var);
@ -3054,7 +3032,7 @@ class CompilerBase implements PropertyAccessContext
return 'php::instanceOf(' . $value . ', ' . $classPtr . ')';
} else {
[$value, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($expr->expr);
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$tmpVar = $this->addTmpVar(Type::VAR);
$this->appendCapturedStmtLinesToContext($beforeStmts);
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $value . ';';
$this->appendCapturedStmtLinesToContext($afterStmts);
@ -3107,10 +3085,10 @@ class CompilerBase implements PropertyAccessContext
foreach ($expr->vars as $v) {
$name = $this->parseVariable($v);
if (!$this->hasGlobalVar($name)) {
$this->addGlobalVar($name, self::TYPE_VAR);
$this->addGlobalVar($name, Type::VAR);
}
if (!$this->hasScopeGlobalVar($name)) {
$this->addScopeGlobalVar($name, self::TYPE_VAR);
$this->addScopeGlobalVar($name, Type::VAR);
}
}
return '';
@ -3143,13 +3121,13 @@ class CompilerBase implements PropertyAccessContext
$list = [];
foreach ($v->vars as $var) {
$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) {
$this->assertExprCanBeUsedAsValue($var->default, 'static variable default value');
}
$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) {
$initState = self::STATIC_VAR . $varName . '_initialized';
$initCode = $this->getIndent() . 'static bool ' . $initState . ' = false;';
@ -3324,16 +3302,16 @@ class CompilerBase implements PropertyAccessContext
$this->fatalError($expr, 'Cannot use [] for reading');
}
$dim = $this->parseIdentifier($expr->dim);
$list[] = '{php::ArrayDimFetch, ' . self::TYPE_VAR . '(' . $dim . ')}';
$list[] = '{php::ArrayDimFetch, ' . Type::VAR . '(' . $dim . ')}';
} elseif ($this->isPropertyFetch($expr)) {
$name = $this->identifierToStr($expr->name, literal: true);
$list[] = '{php::PropertyFetch, ' . self::TYPE_VAR . '(' . $name . ')}';
$list[] = '{php::PropertyFetch, ' . Type::VAR . '(' . $name . ')}';
} elseif ($this->isVarExpr($expr)) {
$var = $this->parseIdentifier($expr);
break;
} else {
$var = $this->genTmpVarName();
$this->addLocalVar($var, self::TYPE_VAR);
$this->addLocalVar($var, Type::VAR);
$this->context->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';';
break;
}
@ -3343,7 +3321,7 @@ class CompilerBase implements PropertyAccessContext
$list = array_reverse($list);
if ($getValue) {
$result = $this->addTmpVar(self::TYPE_VAR);
$result = $this->addTmpVar(Type::VAR);
$node->setAttribute('chainOpResult', $result);
return $fn . '(' . $var . ', {' . implode(', ', $list) . '}, ' . $result . ')';
} else {
@ -3390,7 +3368,7 @@ class CompilerBase implements PropertyAccessContext
return $this->getTypeFromZendType($returnType);
}
return self::TYPE_VAR;
return Type::VAR;
}
protected function detectMethodCallReturnType(string $class, string $method): string
@ -3399,7 +3377,7 @@ class CompilerBase implements PropertyAccessContext
if ($returnType) {
return $this->getTypeFromZendType($returnType);
}
return self::TYPE_VAR;
return Type::VAR;
}
protected function genObjvalCall(Expr\FuncCall $expr): string
@ -3612,7 +3590,7 @@ class CompilerBase implements PropertyAccessContext
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)) {
$this->addLocalVar($name, $defaultType);
@ -3632,11 +3610,11 @@ class CompilerBase implements PropertyAccessContext
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;
}
// 引用当前没有类型信息,按照 var 处理
if ($toType === self::TYPE_REF or $fromType === self::TYPE_REF) {
if ($toType === Type::REF or $fromType === Type::REF) {
return true;
}
// 类型一致,可以互相赋值
@ -3648,7 +3626,7 @@ class CompilerBase implements PropertyAccessContext
return true;
}
// 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)) {
return true;
}
@ -3727,7 +3705,7 @@ class CompilerBase implements PropertyAccessContext
continue;
}
$code .= $this->getIndent();
if ($type === self::TYPE_STD_ARRAY) {
if ($type === Type::STD_ARRAY) {
$info = $this->context->stdArrays[$name];
if (isset($info['boxExpr'])) {
$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 .= $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];
if (isset($info['boxExpr'])) {
$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 .= $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];
if (isset($info['boxExpr'])) {
$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 .= $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) {
$code .= self::TYPE_VAR . ' ' . $name . ';';
} elseif ($type === Type::STREAM || $type === Type::BIGINT || $type === Type::DECIMAL || $type === Type::BIGFLOAT) {
$code .= Type::VAR . ' ' . $name . ';';
} else {
$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 .= ';';
@ -3788,20 +3766,20 @@ class CompilerBase implements PropertyAccessContext
if ($name === 'GLOBALS') {
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) {
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 {
$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;
}
}
foreach ($this->context->staticPropRefs as $name => $info) {
$getter = Symbol::getStaticProperty() . '(' . $info['classPtr'] . ', ' . $info['offsetExpr'] . ')';
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 {
$code .= $this->getIndent() . 'zval *' . $name . ' = ' . $getter . '.unwrap_ptr();' . PHP_EOL;
}
@ -3812,20 +3790,20 @@ class CompilerBase implements PropertyAccessContext
protected function genReturnCode(): string
{
if ($this->functionDef->returnsByRef) {
return $this->getIndent() . 'return ' . self::TYPE_REF . '{};';
return $this->getIndent() . 'return ' . Type::REF . '{};';
}
if ($this->shouldCheckClosureReturnType()) {
return $this->genClosureCheckedReturn(self::VALUE_NULL);
}
if ($this->functionDef->returnType === self::TYPE_VOID) {
if ($this->functionDef->returnType === Type::VOID) {
return '';
}
if ($this->functionDef->returnTypeCheck && !$this->context->inClosure) {
return $this->genUnionCheckedReturn(self::VALUE_NULL);
}
if ($this->functionDef->returnType === self::TYPE_INT
or $this->functionDef->returnType === self::TYPE_FLOAT
or $this->functionDef->returnType === self::TYPE_BOOL) {
if ($this->functionDef->returnType === Type::INT
or $this->functionDef->returnType === Type::FLOAT
or $this->functionDef->returnType === Type::BOOL) {
return $this->getIndent() . 'return 0;';
} else {
return $this->getIndent() . 'return ' . self::VALUE_NULL . ';';

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

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

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

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

@ -8,6 +8,8 @@
namespace TypePhp\Generator;
use TypePhp\Type;
use PhpParser\Node;
use PhpParser\Node\Expr\Yield_;
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');
}
$functionDef->generator = true;
$functionDef->returnType = self::TYPE_VAR;
$functionDef->returnType = Type::VAR;
$functionDef->returnClass = '';
$functionDef->returnTypeCheck = null;
$functionDef->returnTypeStr = '';
@ -126,7 +128,7 @@ trait FiberGenerator
{
$payload = $this->genYieldPayload($expr);
$closed = $this->genTmpVarName();
$this->addLocalVar($closed, self::TYPE_BOOL);
$this->addLocalVar($closed, Type::BOOL);
return $closed . ' = false;' . PHP_EOL
. $this->getIndent() . $closed . ' = typephp_fiber_yield(' . $payload . ');' . PHP_EOL
. $this->getIndent() . 'if (' . $closed . ') {' . PHP_EOL
@ -137,7 +139,7 @@ trait FiberGenerator
protected function parseYieldFromStmt(YieldFrom $expr): string
{
$closed = $this->genTmpVarName();
$this->addLocalVar($closed, self::TYPE_BOOL);
$this->addLocalVar($closed, Type::BOOL);
return $closed . ' = false;' . PHP_EOL
. $this->getIndent() . 'typephp_fiber_yield_from(' . $this->parseExprAsValue($expr->expr) . ', &' . $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
{
$functionDeclCode = self::TYPE_VAR . ' ' . self::PREFIX . $nativeName . '(';
$functionDeclCode = Type::VAR . ' ' . self::PREFIX . $nativeName . '(';
if ($this->class) {
$functionDeclCode .= self::TYPE_OBJECT . ' &this_';
$functionDeclCode .= Type::OBJECT . ' &this_';
if ($functionDef->params) {
$functionDeclCode .= ', ';
}
@ -210,8 +212,8 @@ trait FiberGenerator
$closureVar = $this->genTmpVarName();
$code .= $this->getIndent() . 'php::ClosureFn ' . $closureVar . ' = []('
. 'INTERNAL_FUNCTION_PARAMETERS, '
. self::TYPE_OBJECT . ' &this_, '
. self::TYPE_ARGS . ' &vars_) -> ' . self::TYPE_VAR . ' {' . PHP_EOL;
. Type::OBJECT . ' &this_, '
. Type::ARGS . ' &vars_) -> ' . Type::VAR . ' {' . PHP_EOL;
$outerContext = $this->context;
$outerIndent = $this->indentLevel;
@ -222,11 +224,11 @@ trait FiberGenerator
$this->indentLevel++;
foreach ($functionDef->argInfoList as $i => $argInfo) {
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $argInfo->name . ' = vars_.get(' . $i . ');' . PHP_EOL;
$this->addArgument($argInfo->name, self::TYPE_VAR);
$code .= $this->getIndent() . Type::VAR . ' ' . $argInfo->name . ' = vars_.get(' . $i . ');' . PHP_EOL;
$this->addArgument($argInfo->name, Type::VAR);
}
if ($this->class) {
$this->addArgument('this_', self::TYPE_OBJECT);
$this->addArgument('this_', Type::OBJECT);
}
$body = '';

@ -8,6 +8,8 @@
namespace TypePhp\Generator;
use TypePhp\Type;
use TypePhp\Entity\ArgInfo;
use PhpParser\Node;
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;
$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)) {
$code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL;
$this->indentLevel++;
@ -293,7 +295,7 @@ trait TypeCheckGenerator
$this->indentLevel--;
$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;
$this->indentLevel++;
$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;
$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)) {
$code .= $this->getIndent() . 'if (' . $valueVar . '.isInt()) {' . PHP_EOL;
$this->indentLevel++;
@ -420,7 +422,7 @@ trait TypeCheckGenerator
$this->indentLevel--;
$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;
$this->indentLevel++;
$code .= $this->getIndent() . 'return php::throwException(zend_ce_type_error, (' . $msgExpr . ').toCString());' . PHP_EOL;

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

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

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

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

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

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

@ -8,6 +8,8 @@
namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\Resolver\PropertyWriteTarget;
use PhpParser\Node;
use PhpParser\Node\ArrayItem;
@ -26,19 +28,19 @@ trait AssignOpTrait
$target = $this->parseGlobalsArrayDimFetch($left);
$value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR);
$this->addLocalVar($tmp, Type::VAR);
return '((' . $tmp . ' = ' . $value . ', ' . $target . ' = ' . $tmp . '), ' . $tmp . ')';
}
$array = $this->parseWritableIdentifier($left->var);
$code = '';
if (!$this->hasVar($array) and $this->isVarExpr($left->var)) {
$this->addLocalVar($array, self::TYPE_ARRAY);
$this->addLocalVar($array, Type::ARRAY);
}
$value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR);
$this->addLocalVar($tmp, Type::VAR);
if ($left->dim === null) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')';
@ -62,7 +64,7 @@ trait AssignOpTrait
}
$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
return '((' . $tmp . ' = ' . $rightExpr . ', ' . $this->emitDynamicPropertyFetchWrite($left, $tmp, $target) . '), ' . $tmp . ')';
}
@ -77,7 +79,7 @@ trait AssignOpTrait
$next = $next->expr;
}
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
// 翻转赋值链
$chain = array_reverse($chain);
@ -108,7 +110,7 @@ trait AssignOpTrait
$code = '{';
$this->indentLevel++;
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; ';
foreach ($items as $k => $item) {
if (!$item) {
@ -118,13 +120,13 @@ trait AssignOpTrait
$key = $item->key ? $this->parseArrayKey($item->key) : (string) $k;
if ($item->value instanceof Expr\List_) {
$nestedTmp = $this->genTmpVarName();
$this->addLocalVar($nestedTmp, self::TYPE_ARRAY);
$this->addLocalVar($nestedTmp, Type::ARRAY);
$code .= "{$nestedTmp} = {$tmpVar}.item({$key}); ";
$code .= $this->parseAssignToList($item->value, new Variable($nestedTmp));
} else {
$var = $this->parseWritableIdentifier($item->value);
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}); ";
}
@ -148,8 +150,8 @@ trait AssignOpTrait
$type = $this->detectTypeOfExpr($right);
$finalVarType = $this->getNormalAssignType($type);
$runtimeObjectAssignClass = '';
if ($type === self::TYPE_VOID) {
$type = self::TYPE_VAR;
if ($type === Type::VOID) {
$type = Type::VAR;
}
if ($left instanceof Expr\PropertyFetch && ($setter = $this->getPropertyHookSetter($left)) !== null) {
@ -179,7 +181,7 @@ trait AssignOpTrait
// 右值是一个对象,已获得类的名称,左值必须与右值的类一致
if ($rightClass) {
if (!$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_OBJECT);
$this->addLocalVar($var, Type::OBJECT);
$this->addObject($var, $rightClass);
} elseif (($leftClass = $this->getDeclaredObjectType($var)) !== '') {
if ($this->isObjectClassStaticallyAssignableTo($rightClass, $leftClass)) {
@ -194,7 +196,7 @@ trait AssignOpTrait
$this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`");
}
} else {
$this->checkVarAssignExpr($left, $this->getVarType($var), self::TYPE_OBJECT);
$this->checkVarAssignExpr($left, $this->getVarType($var), Type::OBJECT);
}
} else {
if ($this->isMethodCall($right) and $this->isNamedMethod($right->name)) {
@ -210,7 +212,7 @@ trait AssignOpTrait
}
}
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)) {
$class = $this->parseIdentifier($right->class);
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");
}
if ($right->name->toString() === 'array') {
$this->addLocalVar($var, self::TYPE_STD_ARRAY);
$this->addLocalVar($var, Type::STD_ARRAY);
return $this->parseStdArray($var, $right);
}
if ($right->name->toString() === 'vector') {
$this->addLocalVar($var, self::TYPE_STD_VECTOR);
$this->addLocalVar($var, Type::STD_VECTOR);
return $this->parseStdVector($var, $right);
}
if ($right->name->toString() === 'map') {
$this->addLocalVar($var, self::TYPE_STD_MAP);
$this->addLocalVar($var, Type::STD_MAP);
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);
} else {
$valueExpr = $this->parseStdCall($right);
@ -246,7 +248,7 @@ trait AssignOpTrait
}
} elseif ($this->isVarExpr($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);
$leftClass = $this->getDeclaredObjectType($var);
$rightClass = $this->getDeclaredObjectType($rightVar);
@ -269,7 +271,7 @@ trait AssignOpTrait
$finalVarType = $this->getVarType($var);
$this->checkVarAssignExpr($left, $finalVarType, $type);
$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;
}
}
@ -278,7 +280,7 @@ trait AssignOpTrait
return $this->parseAssignPropertyFetch($left, $right, $propertyWriteTarget);
} elseif ($this->isArrayDimFetch($left) and $this->isVarExpr($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');
}
if ($this->isStdContainerExpr($left)) {
@ -304,12 +306,12 @@ trait AssignOpTrait
$leftExprType = $this->detectTypeOfExpr($left);
$rightExprType = $this->detectTypeOfExpr($right);
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
: $rightExprType;
return $var . ' = ' . $this->convertNativePropertyWriteExpr($propertyDef->type, $effectiveRightType, $rightExpr);
}
if ($finalVarType === self::TYPE_VAR) {
if ($finalVarType === Type::VAR) {
return $var . ' = ' . $rightExpr;
} else {
return $var . ' = ' . $this->convertExprType($rightExpr, $leftExprType, $rightExprType);
@ -330,7 +332,7 @@ trait AssignOpTrait
$rightExpr = $this->wrapPropertyWriteTypeCheck($target, $right, $rightExpr);
}
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR);
$this->addLocalVar($tmp, Type::VAR);
$call = $this->emitPropertyHookSetterCall($left, $setter, new Expr\Variable($tmp));
return '((' . $tmp . ' = ' . $rightExpr . ', ' . $call . '), ' . $tmp . ')';
}
@ -346,8 +348,8 @@ trait AssignOpTrait
return false;
}
return !in_array($def->type, [self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL, self::TYPE_STR, self::TYPE_ARRAY], true)
&& $rightType === self::TYPE_VAR;
return !in_array($def->type, [Type::INT, Type::FLOAT, Type::BOOL, Type::STR, Type::ARRAY], true)
&& $rightType === Type::VAR;
}
protected function parseStdContainerCopyAssign(string $leftVar, Expr $right): ?string
@ -398,7 +400,7 @@ trait AssignOpTrait
$right = $this->parseExprAsValue($node->expr);
$read = $this->emitPropertyHookGetterCall($node->var, $getter);
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR);
$this->addLocalVar($tmp, Type::VAR);
$binaryOp = $this->removeAssignOp($op);
$value = match ($binaryOp) {
'.' => 'php::concat(' . $read . ', ' . $right . ')',
@ -428,7 +430,7 @@ trait AssignOpTrait
// BigInt/BigDecimal/BigFloat are immutable Box types stored inside
// php::Var — Variant::operator+= calls ZendVM add_function which
// 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);
}
@ -468,7 +470,7 @@ trait AssignOpTrait
$this->context->beforeStmtLines[] = "{$tmpVar} = php::concat(" .
$this->convertVarType($tmpVar, $readVar) . ', ' .
$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);
$this->context->beforeStmtLines[] = "{$tmpVar} = {$bigAssign};";
} else {
@ -490,7 +492,7 @@ trait AssignOpTrait
}
$binaryOp = $this->removeAssignOp($op);
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->addLocalVar($tmpVar, Type::VAR);
$readProperty = $this->emitDynamicPropertyFetchRead($node->var, $propertyWriteTarget);
if ($this->isAssignOpConcat($op)) {
$this->context->beforeStmtLines[] = "{$tmpVar} = php::concat({$readProperty}, {$expr});";
@ -521,7 +523,7 @@ trait AssignOpTrait
}
$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(
$node->var,
'Cannot assign ' . $this->getPropertyAssignmentTypeName($rightType)
@ -535,15 +537,15 @@ trait AssignOpTrait
$var = $this->parseWritableIdentifier($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())';
}
$rightExpr = $this->parseIdentifier($node->expr);
if ($rightType === self::TYPE_VAR) {
if ($rightType === Type::VAR) {
$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
: $rightType;
@ -561,13 +563,13 @@ trait AssignOpTrait
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 match ($propertyType) {
self::TYPE_INT => in_array($op, ['+=', '-=', '*=', '%=', '<<=', '>>=', '&=', '|=', '^='], true),
self::TYPE_FLOAT => in_array($op, ['+=', '-=', '*=', '/='], true),
Type::INT => in_array($op, ['+=', '-=', '*=', '%=', '<<=', '>>=', '&=', '|=', '^='], true),
Type::FLOAT => in_array($op, ['+=', '-=', '*=', '/='], true),
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
{
[$class, $opMap] = match ($leftType) {
self::TYPE_BIGINT => ['BigInt', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor', '<<' => 'bitShiftLeft', '>>' => 'bitShiftRight']],
self::TYPE_DECIMAL => ['Decimal', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod']],
self::TYPE_BIGFLOAT => ['BigFloat', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div']],
Type::BIGINT => ['BigInt', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod', '&' => 'bitAnd', '|' => 'bitOr', '^' => 'bitXor', '<<' => 'bitShiftLeft', '>>' => 'bitShiftRight']],
Type::DECIMAL => ['Decimal', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div', '%' => 'mod']],
Type::BIGFLOAT => ['BigFloat', ['+' => 'add', '-' => 'sub', '*' => 'mul', '/' => 'div']],
};
$method = $opMap[$binaryOp] ?? null;
@ -595,9 +597,9 @@ trait AssignOpTrait
// For bitwise shifts, the right operand is a shift amount (Int), not BigInt
$isShift = ($binaryOp === '<<' || $binaryOp === '>>');
$convertedRight = match ($leftType) {
self::TYPE_BIGINT => $isShift ? $rightExpr : $this->convertBigIntExpr($rightExpr, $rightType),
self::TYPE_DECIMAL => $this->convertDecimalExpr($rightExpr, $rightType, $rightNode),
self::TYPE_BIGFLOAT => $this->convertBigFloatExpr($rightExpr, $rightType),
Type::BIGINT => $isShift ? $rightExpr : $this->convertBigIntExpr($rightExpr, $rightType),
Type::DECIMAL => $this->convertDecimalExpr($rightExpr, $rightType, $rightNode),
Type::BIGFLOAT => $this->convertBigFloatExpr($rightExpr, $rightType),
};
return 'php::' . $class . '::' . $method . '(' . $leftExpr . ', ' . $convertedRight . ')';
@ -681,16 +683,16 @@ trait AssignOpTrait
if ($this->isVarExpr($expr->var)) {
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
$this->addLocalVar($left, Type::REF);
} else {
$type = $this->getVarType($left);
if ($type !== self::TYPE_REF) {
if ($type !== Type::REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
}
}
}
$tmpVar = $this->addTmpVar(self::TYPE_REF);
$tmpVar = $this->addTmpVar(Type::REF);
$rightExpr = '';
if ($this->isVarExpr($expr->expr)) {
@ -778,7 +780,7 @@ trait AssignOpTrait
$value = $this->parseExprAsValue($right);
$tmp = $this->genTmpVarName();
$this->addLocalVar($tmp, self::TYPE_VAR);
$this->addLocalVar($tmp, Type::VAR);
if ($left->dim === null) {
return $code . '((' . $tmp . ' = ' . $value . ', ' . $this->emitDynamicPropertyFetchAppendArray($left->var, $tmp, $propertyWriteTarget) . '), ' . $tmp . ')';
@ -815,7 +817,7 @@ trait AssignOpTrait
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;
use TypePhp\Type;
use TypePhp\Generator\Symbol;
use PhpParser\Node;
use PhpParser\Node\Expr;
@ -31,18 +33,18 @@ trait BinaryOpTrait
$leftType = $this->detectTypeOfExpr($left);
$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
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.');
}
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.');
}
if ($leftType !== self::TYPE_BIGFLOAT) {
if ($leftType !== Type::BIGFLOAT) {
$leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType);
}
if ($rightType !== self::TYPE_BIGFLOAT) {
if ($rightType !== Type::BIGFLOAT) {
$rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType);
}
$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
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.');
}
if ($leftType !== self::TYPE_DECIMAL) {
if ($leftType !== Type::DECIMAL) {
$leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $left);
}
if ($rightType !== self::TYPE_DECIMAL) {
if ($rightType !== Type::DECIMAL) {
$rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $right);
}
$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
if ($op === '<<' || $op === '>>') {
if ($leftType !== self::TYPE_BIGINT) {
if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
}
if ($rightType === self::TYPE_BIGINT) {
if ($rightType === Type::BIGINT) {
$rightExpr = 'php::BigInt::toInt(' . $rightExpr . ')';
} elseif ($rightType !== self::TYPE_INT) {
$rightExpr = $this->convertExprType($rightExpr, $rightType, self::TYPE_INT);
} elseif ($rightType !== Type::INT) {
$rightExpr = $this->convertExprType($rightExpr, $rightType, Type::INT);
}
$method = ($op === '<<') ? 'bitShiftLeft' : 'bitShiftRight';
return 'php::BigInt::' . $method . '(' . $leftExpr . ', ' . $rightExpr . ')';
}
if ($leftType !== self::TYPE_BIGINT) {
if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
}
if ($rightType !== self::TYPE_BIGINT) {
if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
}
$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
$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)) {
$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
// php::Var, let the Variant operator handle type coercion so that
// run-time PHP type-juggling rules are followed correctly.
if ($leftType === self::TYPE_FLOAT && $rightType === self::TYPE_INT) {
$rightExpr = $this->convertExprType($rightExpr, self::TYPE_FLOAT, $rightType);
} elseif ($rightType === self::TYPE_FLOAT && $leftType === self::TYPE_INT) {
$leftExpr = $this->convertExprType($leftExpr, $leftType, self::TYPE_FLOAT);
if ($leftType === Type::FLOAT && $rightType === Type::INT) {
$rightExpr = $this->convertExprType($rightExpr, Type::FLOAT, $rightType);
} elseif ($rightType === Type::FLOAT && $leftType === Type::INT) {
$leftExpr = $this->convertExprType($leftExpr, $leftType, Type::FLOAT);
}
$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 . ')';
}
@ -191,7 +193,7 @@ trait BinaryOpTrait
{
if ($expr instanceof Expr\BinaryOp) {
$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 (
@ -200,7 +202,7 @@ trait BinaryOpTrait
|| $expr instanceof Expr\StaticCall
) {
$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) {
@ -215,7 +217,7 @@ trait BinaryOpTrait
return $def->type;
}
}
return self::TYPE_VAR;
return Type::VAR;
}
if ($expr instanceof Expr\StaticPropertyFetch) {
@ -223,11 +225,11 @@ trait BinaryOpTrait
if ($def && $this->isNativePropertyTypedValue($expr)) {
return $def->type;
}
return self::TYPE_VAR;
return Type::VAR;
}
if ($expr instanceof Expr\ArrayDimFetch) {
return self::TYPE_VAR;
return Type::VAR;
}
$type = $this->detectTypeOfExpr($expr);
@ -305,11 +307,11 @@ trait BinaryOpTrait
$this->assertExprCanBeUsedAsValue($expr->left, 'binary operand');
$this->assertExprCanBeUsedAsValue($expr->right, 'binary operand');
$leftType = $this->detectTypeOfExpr($expr->left);
if ($leftType === self::TYPE_BIGINT) {
if ($leftType === Type::BIGINT) {
$leftExpr = $this->parseOrderedOperand($expr->left, false);
$rightExpr = $this->parseOrderedOperand($expr->right, false);
$rightType = $this->detectTypeOfExpr($expr->right);
if ($rightType !== self::TYPE_BIGINT) {
if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
}
return 'php::BigInt::pow(' . $leftExpr . ', ' . $rightExpr . ')';
@ -376,7 +378,7 @@ trait BinaryOpTrait
*/
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);
$rightType = $this->detectTypeOfExpr($astRight);
@ -431,7 +433,7 @@ trait BinaryOpTrait
$code .= $this->getIndent() . 'if (' . $rightCondition . ') {';
$code .= $this->formatCapturedStmtLines($rightBeforeStmts);
if ($rightAfterStmts) {
$rightTmpVar = $this->addTmpVar(self::TYPE_VAR);
$rightTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';';
$code .= $this->formatCapturedStmtLines($rightAfterStmts);
$rightExpr = $rightTmpVar;
@ -470,35 +472,35 @@ trait BinaryOpTrait
$leftType = $this->detectTypeOfExpr($expr->left);
$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);
$rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_BIGFLOAT) {
if ($leftType !== Type::BIGFLOAT) {
$leftExpr = $this->convertBigFloatExpr($leftExpr, $leftType);
}
if ($rightType !== self::TYPE_BIGFLOAT) {
if ($rightType !== Type::BIGFLOAT) {
$rightExpr = $this->convertBigFloatExpr($rightExpr, $rightType);
}
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);
$rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_BIGINT) {
if ($leftType !== Type::BIGINT) {
$leftExpr = $this->convertBigIntExpr($leftExpr, $leftType);
}
if ($rightType !== self::TYPE_BIGINT) {
if ($rightType !== Type::BIGINT) {
$rightExpr = $this->convertBigIntExpr($rightExpr, $rightType);
}
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);
$rightExpr = $this->parseOrderedOperand($expr->right, false);
if ($leftType !== self::TYPE_DECIMAL) {
if ($leftType !== Type::DECIMAL) {
$leftExpr = $this->convertDecimalExpr($leftExpr, $leftType, $expr->left);
}
if ($rightType !== self::TYPE_DECIMAL) {
if ($rightType !== Type::DECIMAL) {
$rightExpr = $this->convertDecimalExpr($rightExpr, $rightType, $expr->right);
}
return 'php::Decimal::cmp(' . $leftExpr . ', ' . $rightExpr . ')' . $suffix;

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

@ -8,6 +8,8 @@
namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\MagicConst;
@ -123,15 +125,15 @@ trait ConstantExpressionTrait
return $this->getTypeFromZendType(gettype($this->internalConstants[$name]));
}
if (strcasecmp($name, 'true') === 0) {
return self::TYPE_BOOL;
return Type::BOOL;
}
if (strcasecmp($name, 'false') === 0) {
return self::TYPE_BOOL;
return Type::BOOL;
}
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

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

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

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

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

@ -7,6 +7,8 @@
namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
@ -166,9 +168,9 @@ trait MethodCallTrait
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();
$this->context->beforeStmtLines[] = self::TYPE_OBJECT . ' ' . $tmpVar . ' = ' . $object . ';';
$this->context->beforeStmtLines[] = Type::OBJECT . ' ' . $tmpVar . ' = ' . $object . ';';
$object = $tmpVar;
}
if (count($args) === 0) {
@ -181,35 +183,35 @@ trait MethodCallTrait
{
$func = strtolower($this->parseIdentifier($expr->name));
$type = match ($func) {
'int' => self::TYPE_INT,
'float' => self::TYPE_FLOAT,
'bool' => self::TYPE_BOOL,
'bigint' => self::TYPE_BIGINT,
'decimal' => self::TYPE_DECIMAL,
'bigfloat' => self::TYPE_BIGFLOAT,
'int' => Type::INT,
'float' => Type::FLOAT,
'bool' => Type::BOOL,
'bigint' => Type::BIGINT,
'decimal' => Type::DECIMAL,
'bigfloat' => Type::BIGFLOAT,
default => '',
};
if ($type) {
$expr->setAttribute('nativeType', $type);
$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);
}
$argType = $this->detectTypeOfExpr($expr->args[0]->value);
if ($argType === $type) {
return $valueExpr;
}
if ($type === self::TYPE_BIGINT) {
if ($argType === self::TYPE_FLOAT) {
if ($type === Type::BIGINT) {
if ($argType === Type::FLOAT) {
$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::BigInt::newInstance(' . $valueExpr . ')';
}
if ($type === self::TYPE_DECIMAL) {
if ($argType === self::TYPE_FLOAT) {
if ($type === Type::DECIMAL) {
if ($argType === Type::FLOAT) {
$argNode = $expr->args[0]->value;
if ($argNode instanceof Node\Scalar\Float_) {
$rawValue = $argNode->getAttribute('rawValue');
@ -218,16 +220,16 @@ trait MethodCallTrait
}
$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::Decimal::newInstance(' . $valueExpr . ')';
}
if ($type === self::TYPE_BIGFLOAT) {
if ($argType === self::TYPE_INT) {
if ($type === Type::BIGFLOAT) {
if ($argType === Type::INT) {
return 'php::toBigFloat(' . $valueExpr . ')';
}
if ($argType === self::TYPE_FLOAT) {
if ($argType === Type::FLOAT) {
return 'php::toBigFloat(' . $valueExpr . ')';
}
return 'php::BigFloat::newInstance(' . $valueExpr . ')';
@ -310,8 +312,8 @@ trait MethodCallTrait
if ($this->isNamedMethod($expr->name)) {
$methodName = $expr->name->toString();
$receiverType = $this->isVarExpr($expr->var) ? $this->getVarType($object) : $this->detectTypeOfExpr($expr->var);
if ($receiverType === self::TYPE_VOID) {
$receiverType = self::TYPE_VAR;
if ($receiverType === Type::VOID) {
$receiverType = Type::VAR;
}
// to* builtins
if (isset(self::KEYWORD_METHOD_MAP[$methodName])) {
@ -337,12 +339,12 @@ trait MethodCallTrait
if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->getVarType($object);
// 引用参数允许方法调用:有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();
// 非对象类型可使用内置方法
$fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) {
if ($type === self::TYPE_STREAM) {
if ($type === Type::STREAM) {
return $this->genStreamNullGuard($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()
if (!$this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$type = $this->detectTypeOfExpr($expr->var);
if ($type === self::TYPE_VOID) {
$type = self::TYPE_VAR;
if ($type === Type::VOID) {
$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();
$fn = $this->findUniversalMethodAnyType($type, $methodName);
if ($fn) {
@ -397,7 +399,7 @@ trait MethodCallTrait
if ($fn['handler'] === 'direct_method') {
$receiver = $this->wrapUniversalReceiver($type, $object);
}
if ($type === self::TYPE_STREAM) {
if ($type === Type::STREAM) {
return $this->genStreamNullGuard($expr, $receiver, $methodName, $fn);
}
return $this->parseUniversalMethodCall($expr, $receiver, $methodName, $fn, false);
@ -458,7 +460,7 @@ trait MethodCallTrait
$class = $this->getObjectType($var);
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) . '})';
} else {
$fn = 'php::concat({' . $this->identifierToStr($expr->class) . ', "::", ' . $this->identifierToStr($expr->name) . '})';

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

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

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

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

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

@ -8,6 +8,8 @@
namespace TypePhp\Parser;
use TypePhp\Type;
use PhpParser\Node;
use PhpParser\NodeAbstract;
@ -15,13 +17,13 @@ trait TypeConversionTrait
{
protected function convertExprToStringByType(string $expr, $type): string
{
if ($type === self::TYPE_BIGINT) {
if ($type === Type::BIGINT) {
return 'php::BigInt::toString(' . $expr . ')';
}
if ($type === self::TYPE_BIGFLOAT) {
if ($type === Type::BIGFLOAT) {
return 'php::BigFloat::toString(' . $expr . ')';
}
if ($type === self::TYPE_DECIMAL) {
if ($type === Type::DECIMAL) {
return 'php::Decimal::toString(' . $expr . ')';
}
return $this->convertStringExpr($expr);
@ -47,7 +49,7 @@ trait TypeConversionTrait
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_) {
$rawValue = $node->getAttribute('rawValue');
$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');
}
if ($fromType === self::TYPE_STR) {
if ($fromType === Type::STR) {
if ($node instanceof Node\Scalar\String_) {
return 'php::toDecimal(' . $this->getLiteralString($node->value) . ')';
}
return 'php::toDecimal(php::toString(' . $expr . '))';
}
if ($fromType === self::TYPE_INT) {
if ($fromType === Type::INT) {
return 'php::toDecimal(php::toString(' . $expr . '))';
}
if ($fromType === self::TYPE_BIGINT) {
if ($fromType === Type::BIGINT) {
return 'php::toDecimal(php::BigInt::toString(' . $expr . '))';
}
return $expr;
@ -72,13 +74,13 @@ trait TypeConversionTrait
protected function convertBigIntExpr(string $expr, string $fromType = ''): string
{
if ($fromType === self::TYPE_INT) {
if ($fromType === Type::INT) {
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');
}
if ($fromType === self::TYPE_STR) {
if ($fromType === Type::STR) {
return 'php::toBigInt(php::toString(' . $expr . '))';
}
return $expr;
@ -86,19 +88,19 @@ trait TypeConversionTrait
protected function convertBigFloatExpr(string $expr, string $fromType = ''): string
{
if ($fromType === self::TYPE_INT) {
if ($fromType === Type::INT) {
return 'php::toBigFloat(' . $expr . ')';
}
if ($fromType === self::TYPE_FLOAT) {
if ($fromType === Type::FLOAT) {
return 'php::toBigFloat(' . $expr . ')';
}
if ($fromType === self::TYPE_STR) {
if ($fromType === Type::STR) {
return 'php::toBigFloat(php::toString(' . $expr . '))';
}
if ($fromType === self::TYPE_BIGINT) {
if ($fromType === Type::BIGINT) {
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 $expr;
@ -145,13 +147,13 @@ trait TypeConversionTrait
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);
}
if ($leftType === self::TYPE_INT or $rightType === self::TYPE_INT) {
if ($leftType === Type::INT or $rightType === Type::INT) {
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);
}
@ -160,33 +162,33 @@ trait TypeConversionTrait
protected function getNativeType(string $type): string
{
if ($type === self::TYPE_INT && $this->bigintTypes) {
return self::TYPE_BIGINT;
if ($type === Type::INT && $this->bigintTypes) {
return Type::BIGINT;
}
if ($type === self::TYPE_FLOAT && $this->decimalTypes) {
return self::TYPE_DECIMAL;
if ($type === Type::FLOAT && $this->decimalTypes) {
return Type::DECIMAL;
}
return $this->nativeTypes ? $type : self::TYPE_VAR;
return $this->nativeTypes ? $type : Type::VAR;
}
protected function convertExprFromType(string $type, string $expr): string
{
if ($type === self::TYPE_FLOAT) {
if ($type === Type::FLOAT) {
return $this->convertFloatExpr($expr);
}
if ($type === self::TYPE_INT) {
if ($type === Type::INT) {
return $this->convertIntExpr($expr);
}
if ($type === self::TYPE_BOOL) {
if ($type === Type::BOOL) {
return $this->convertBoolExpr($expr);
}
if ($type === self::TYPE_STR) {
if ($type === Type::STR) {
return $this->convertStringExpr($expr);
}
if ($type === self::TYPE_ARRAY) {
if ($type === Type::ARRAY) {
return $this->convertArrayExpr($expr);
}
if ($type === self::TYPE_OBJECT) {
if ($type === Type::OBJECT) {
return $this->convertObjectExpr($expr);
}
@ -207,7 +209,7 @@ trait TypeConversionTrait
$this->checkLeftValue($expr);
$var = $this->parseIdentifier($expr);
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()';
}

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

@ -2,6 +2,8 @@
namespace TypePhp\Parser;
use TypePhp\Type;
use TypePhp\CompilerBase;
use TypePhp\Resolver\Reflection;
@ -12,314 +14,314 @@ use PhpParser\NodeAbstract;
trait UniversalMethodCall
{
protected const array UNIVERSAL_METHODS = [
CompilerBase::TYPE_INT => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'mod' => ['handler' => 'calc_op', 'op' => '%', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 0, 'max_args' => 0],
Type::INT => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => 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' => 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' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
// math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::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],
'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => CompilerBase::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],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => CompilerBase::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],
'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => CompilerBase::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],
'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => CompilerBase::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],
'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => CompilerBase::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],
'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => CompilerBase::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],
'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => CompilerBase::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],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => CompilerBase::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],
'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => CompilerBase::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],
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => Type::INT, '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' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 2],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => Type::VAR, 'min_args' => 1, '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' => 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' => 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' => 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' => 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' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => 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' => 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 => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1],
'inc' => ['handler' => 'calc_inc', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => CompilerBase::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0],
Type::FLOAT => [
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => 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' => 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' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'dec' => ['handler' => 'calc_dec', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
// math
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => CompilerBase::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],
'floor' => ['handler' => 'php_fn', 'fn' => 'floor', 'return_type' => CompilerBase::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],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => CompilerBase::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],
'log' => ['handler' => 'php_fn', 'fn' => 'log', 'return_type' => CompilerBase::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],
'exp' => ['handler' => 'php_fn', 'fn' => 'exp', 'return_type' => CompilerBase::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],
'cos' => ['handler' => 'php_fn', 'fn' => 'cos', 'return_type' => CompilerBase::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],
'asin' => ['handler' => 'php_fn', 'fn' => 'asin', 'return_type' => CompilerBase::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],
'atan' => ['handler' => 'php_fn', 'fn' => 'atan', 'return_type' => CompilerBase::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],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => CompilerBase::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],
'max' => ['handler' => 'php_fn', 'fn' => 'max', 'return_type' => CompilerBase::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],
'abs' => ['handler' => 'php_fn', 'fn' => 'abs', 'return_type' => 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' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'php_fn', 'fn' => 'round', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 2],
'sqrt' => ['handler' => 'php_fn', 'fn' => 'sqrt', 'return_type' => Type::FLOAT, 'min_args' => 0, 'max_args' => 0],
'pow' => ['handler' => 'php_fn', 'fn' => 'pow', 'return_type' => Type::FLOAT, 'min_args' => 1, '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' => 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' => 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' => 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' => 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' => Type::FLOAT, 'min_args' => 1, 'max_args' => 1],
'deg2rad' => ['handler' => 'php_fn', 'fn' => 'deg2rad', 'return_type' => 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' => 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) ---
'length' => ['handler' => 'php_fn', 'fn' => 'strlen', 'return_type' => CompilerBase::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],
'lower' => ['handler' => 'php_fn', 'fn' => 'strtolower', 'return_type' => CompilerBase::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],
'lowerFirst' => ['handler' => 'php_fn', 'fn' => 'lcfirst', 'return_type' => CompilerBase::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],
'upperWords' => ['handler' => 'php_fn', 'fn' => 'ucwords', 'return_type' => CompilerBase::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],
'addSlashes' => ['handler' => 'php_fn', 'fn' => 'addslashes', 'return_type' => CompilerBase::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],
'countChars' => ['handler' => 'php_fn', 'fn' => 'count_chars', 'return_type' => CompilerBase::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],
'htmlEntityEncode' => ['handler' => 'php_fn', 'fn' => 'htmlentities', 'return_type' => CompilerBase::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],
'htmlSpecialCharsDecode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars_decode', 'return_type' => CompilerBase::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],
'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => CompilerBase::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],
'parseStr' => ['handler' => 'cpp_fn', 'fn' => 'php::fn::parse_str', 'return_type' => CompilerBase::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],
'contains' => ['handler' => 'php_fn', 'fn' => 'str_contains', 'return_type' => CompilerBase::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],
'decr' => ['handler' => 'php_fn', 'fn' => 'str_decrement', 'return_type' => CompilerBase::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],
'repeat' => ['handler' => 'php_fn', 'fn' => 'str_repeat', 'return_type' => CompilerBase::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],
'iReplace' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => CompilerBase::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],
'split' => ['handler' => 'php_fn', 'fn' => 'explode', 'receiver_pos' => 2, 'return_type' => CompilerBase::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],
'endsWith' => ['handler' => 'php_fn', 'fn' => 'str_ends_with', 'return_type' => CompilerBase::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],
'iCompare' => ['handler' => 'php_fn', 'fn' => 'strcasecmp', 'return_type' => CompilerBase::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],
'find' => ['handler' => 'php_fn', 'fn' => 'strstr', 'return_type' => CompilerBase::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],
'stripTags' => ['handler' => 'php_fn', 'fn' => 'strip_tags', 'return_type' => CompilerBase::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],
'stripSlashes' => ['handler' => 'php_fn', 'fn' => 'stripslashes', 'return_type' => CompilerBase::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],
'indexOf' => ['handler' => 'php_fn', 'fn' => 'strpos', 'return_type' => CompilerBase::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],
'iLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strripos', 'return_type' => CompilerBase::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],
'substr' => ['handler' => 'php_fn', 'fn' => 'substr', 'return_type' => CompilerBase::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],
'substrCount' => ['handler' => 'php_fn', 'fn' => 'substr_count', 'return_type' => CompilerBase::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],
'reverse' => ['handler' => 'php_fn', 'fn' => 'strrev', 'return_type' => CompilerBase::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],
'sha1' => ['handler' => 'php_fn', 'fn' => 'sha1', 'return_type' => CompilerBase::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],
'hash' => ['handler' => 'php_fn', 'fn' => 'hash', 'receiver_pos' => 2, 'return_type' => CompilerBase::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],
'base64Decode' => ['handler' => 'php_fn', 'fn' => 'base64_decode', 'return_type' => CompilerBase::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],
'urlDecode' => ['handler' => 'php_fn', 'fn' => 'urldecode', 'return_type' => CompilerBase::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],
'rawUrlEncode' => ['handler' => 'php_fn', 'fn' => 'rawurlencode', 'return_type' => CompilerBase::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],
'match' => ['handler' => 'direct_method', 'method' => 'match', 'return_type' => CompilerBase::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]],
'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => CompilerBase::TYPE_BOOL, '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' => Type::BOOL, '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' => 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' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'upperWords' => ['handler' => 'php_fn', 'fn' => 'ucwords', 'return_type' => Type::STR, 'min_args' => 0, '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' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::VAR, 'min_args' => 0, 'max_args' => 1],
'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' => 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' => Type::STR, 'min_args' => 0, 'max_args' => 1],
'trim' => ['handler' => 'php_fn', 'fn' => 'trim', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => 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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'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' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'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' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'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' => Type::STR, 'min_args' => 2, 'max_args' => 3],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
'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' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'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' => 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' => 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' => Type::STR, 'min_args' => 0, 'max_args' => 2],
'stripCSlashes' => ['handler' => 'php_fn', 'fn' => 'stripcslashes', 'return_type' => 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' => 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' => 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' => Type::VAR, 'min_args' => 1, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 4],
'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' => Type::STR, 'min_args' => 1, 'max_args' => 3],
'reverse' => ['handler' => 'php_fn', 'fn' => 'strrev', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'md5' => ['handler' => 'php_fn', 'fn' => 'md5', 'return_type' => 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' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'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' => Type::INT, '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' => 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' => 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' => Type::STR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3, 'int_cast_args' => [1, 2]],
'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
// mbstring
'mbUpperFirst' => ['handler' => 'php_fn', 'fn' => 'mb_ucfirst', 'return_type' => CompilerBase::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],
'mbTrim' => ['handler' => 'php_fn', 'fn' => 'mb_trim', 'return_type' => CompilerBase::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],
'mbSubstr' => ['handler' => 'php_fn', 'fn' => 'mb_substr', 'return_type' => CompilerBase::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],
'mbLower' => ['handler' => 'php_fn', 'fn' => 'mb_strtolower', 'return_type' => CompilerBase::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],
'mbIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strpos', 'return_type' => CompilerBase::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],
'mbILastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strripos', 'return_type' => CompilerBase::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],
'mbILastCharIndex' => ['handler' => 'php_fn', 'fn' => 'mb_strrichr', 'return_type' => CompilerBase::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],
'mbIFind' => ['handler' => 'php_fn', 'fn' => 'mb_stristr', 'return_type' => CompilerBase::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],
'mbCut' => ['handler' => 'php_fn', 'fn' => 'mb_strcut', 'return_type' => CompilerBase::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],
'mbLTrim' => ['handler' => 'php_fn', 'fn' => 'mb_ltrim', 'return_type' => CompilerBase::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],
'mbConvertEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_convert_encoding', 'return_type' => CompilerBase::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],
'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' => 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' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'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' => 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' => 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' => 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' => 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' => Type::INT, 'min_args' => 0, 'max_args' => 1],
'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' => Type::VAR, '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' => 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' => Type::STR, 'min_args' => 0, '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' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// serialize
'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => CompilerBase::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],
'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'json_decode', 'return_type' => CompilerBase::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']],
'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => 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' => Type::VAR, 'min_args' => 0, 'max_args' => 2, 'const_args' => [1 => 'true']],
'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)
'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) ---
'all' => ['handler' => 'php_fn', 'fn' => 'array_all', 'return_type' => CompilerBase::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],
'changeKeyCase' => ['handler' => 'php_fn', 'fn' => 'array_change_key_case', 'return_type' => CompilerBase::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],
'column' => ['handler' => 'php_fn', 'fn' => 'array_column', 'return_type' => CompilerBase::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],
'diff' => ['handler' => 'php_fn', 'fn' => 'array_diff', 'return_type' => CompilerBase::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],
'diffKey' => ['handler' => 'php_fn', 'fn' => 'array_diff_key', 'return_type' => CompilerBase::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],
'find' => ['handler' => 'php_fn', 'fn' => 'array_find', 'return_type' => CompilerBase::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],
'intersect' => ['handler' => 'php_fn', 'fn' => 'array_intersect', 'return_type' => CompilerBase::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],
'isList' => ['handler' => 'php_fn', 'fn' => 'array_is_list', 'return_type' => CompilerBase::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],
'keyFirst' => ['handler' => 'php_fn', 'fn' => 'array_key_first', 'return_type' => CompilerBase::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],
'keys' => ['handler' => 'php_fn', 'fn' => 'array_keys', 'return_type' => CompilerBase::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],
'pad' => ['handler' => 'php_fn', 'fn' => 'array_pad', 'return_type' => CompilerBase::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],
'rand' => ['handler' => 'php_fn', 'fn' => 'array_rand', 'return_type' => CompilerBase::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],
'replace' => ['handler' => 'php_fn', 'fn' => 'array_replace', 'return_type' => CompilerBase::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],
'search' => ['handler' => 'php_fn', 'fn' => 'array_search', 'receiver_pos' => 2, 'return_type' => CompilerBase::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],
'sum' => ['handler' => 'php_fn', 'fn' => 'array_sum', 'return_type' => CompilerBase::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],
'values' => ['handler' => 'php_fn', 'fn' => 'array_values', 'return_type' => CompilerBase::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],
'merge' => ['handler' => 'php_fn', 'fn' => 'array_merge', 'return_type' => CompilerBase::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],
'join' => ['handler' => 'php_fn', 'fn' => 'implode', 'receiver_pos' => 2, 'return_type' => CompilerBase::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],
'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' => Type::BOOL, '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' => 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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'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' => 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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 2],
'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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'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' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'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' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => -1],
'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' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::VAR, 'min_args' => 1, 'max_args' => 2],
'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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 1],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3],
'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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'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' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'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' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
// mutating via PHP reference functions
'sort' => ['handler' => 'php_fn_ref', 'fn' => 'sort', 'return_type' => CompilerBase::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],
'push' => ['handler' => 'php_fn_ref', 'fn' => 'array_push', 'return_type' => CompilerBase::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],
'unshift' => ['handler' => 'php_fn_ref', 'fn' => 'array_unshift', 'return_type' => CompilerBase::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],
'walk' => ['handler' => 'php_fn_ref', 'fn' => 'array_walk', 'return_type' => CompilerBase::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],
'keySort' => ['handler' => 'php_fn_ref', 'fn' => 'ksort', 'return_type' => CompilerBase::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],
'combine' => ['handler' => 'php_fn', 'fn' => 'array_combine', 'return_type' => CompilerBase::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],
'replaceStr' => ['handler' => 'php_fn', 'fn' => 'str_replace', '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' => CompilerBase::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2],
'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' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::VAR, 'min_args' => 0, 'max_args' => 0],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 3],
'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' => 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' => Type::BOOL, 'min_args' => 0, '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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 2],
// serialize
'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => CompilerBase::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],
'jsonEncode' => ['handler' => 'php_fn', 'fn' => 'json_encode', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2],
'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => 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' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// 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],
'get' => ['handler' => 'direct_method', 'method' => 'get', 'return_type' => CompilerBase::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],
'clean' => ['handler' => 'direct_method_mutate', 'method' => 'clean', 'return_type' => CompilerBase::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0],
'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' => Type::VAR, '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' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
],
CompilerBase::TYPE_STREAM => [
Type::STREAM => [
// --- stdext stream_methods ---
'write' => ['handler' => 'php_fn', 'fn' => 'fwrite', 'return_type' => CompilerBase::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],
'close' => ['handler' => 'php_fn', 'fn' => 'fclose', 'return_type' => CompilerBase::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],
'sync' => ['handler' => 'php_fn', 'fn' => 'fsync', 'return_type' => CompilerBase::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],
'stat' => ['handler' => 'php_fn', 'fn' => 'fstat', 'return_type' => CompilerBase::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],
'tell' => ['handler' => 'php_fn', 'fn' => 'ftell', 'return_type' => CompilerBase::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],
'eof' => ['handler' => 'php_fn', 'fn' => 'feof', 'return_type' => CompilerBase::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],
'getLine' => ['handler' => 'php_fn', 'fn' => 'fgets', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 1],
'write' => ['handler' => 'php_fn', 'fn' => 'fwrite', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'read' => ['handler' => 'php_fn', 'fn' => 'fread', 'return_type' => Type::STR, 'min_args' => 1, 'max_args' => 1],
'close' => ['handler' => 'php_fn', 'fn' => 'fclose', 'return_type' => 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' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'truncate' => ['handler' => 'php_fn', 'fn' => 'ftruncate', 'return_type' => Type::BOOL, 'min_args' => 0, 'max_args' => 1],
'stat' => ['handler' => 'php_fn', 'fn' => 'fstat', 'return_type' => Type::ARRAY, 'min_args' => 0, 'max_args' => 0],
'seek' => ['handler' => 'php_fn', 'fn' => 'fseek', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 2],
'tell' => ['handler' => 'php_fn', 'fn' => 'ftell', 'return_type' => Type::INT, 'min_args' => 0, 'max_args' => 0],
'lock' => ['handler' => 'php_fn', 'fn' => 'flock', 'return_type' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'eof' => ['handler' => 'php_fn', 'fn' => 'feof', 'return_type' => Type::BOOL, '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' => Type::STR, 'min_args' => 0, 'max_args' => 1],
// --- stream_* functions ---
'getContents' => ['handler' => 'php_fn', 'fn' => 'stream_get_contents', 'return_type' => CompilerBase::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],
'isLocal' => ['handler' => 'php_fn', 'fn' => 'stream_is_local', 'return_type' => CompilerBase::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],
'setBlocking' => ['handler' => 'php_fn', 'fn' => 'stream_set_blocking', 'return_type' => CompilerBase::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],
'setReadBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_read_buffer', 'return_type' => CompilerBase::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],
'setWriteBuffer' => ['handler' => 'php_fn', 'fn' => 'stream_set_write_buffer', 'return_type' => CompilerBase::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],
'copy' => ['handler' => 'php_fn', 'fn' => 'stream_copy_to_stream', 'return_type' => CompilerBase::TYPE_INT, 'min_args' => 1, 'max_args' => 3],
'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' => Type::ARRAY, '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' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'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' => 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' => Type::BOOL, 'min_args' => 1, 'max_args' => 2],
'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' => Type::BOOL, 'min_args' => 0, 'max_args' => 0],
'copy' => ['handler' => 'php_fn', 'fn' => 'stream_copy_to_stream', 'return_type' => Type::INT, 'min_args' => 1, 'max_args' => 3],
// --- stream_socket_* functions ---
'accept' => ['handler' => 'php_fn', 'fn' => 'stream_socket_accept', 'return_type' => CompilerBase::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],
'getSocketName' => ['handler' => 'php_fn', 'fn' => 'stream_socket_get_name', 'return_type' => CompilerBase::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],
'sendTo' => ['handler' => 'php_fn', 'fn' => 'stream_socket_sendto', 'return_type' => CompilerBase::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],
'getRecord' => ['handler' => 'php_fn', 'fn' => 'stream_get_line', 'return_type' => CompilerBase::TYPE_STR, 'min_args' => 0, 'max_args' => 2],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 3],
'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' => Type::STR, 'min_args' => 1, 'max_args' => 2],
'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' => Type::BOOL, 'min_args' => 1, 'max_args' => 1],
'getRecord' => ['handler' => 'php_fn', 'fn' => 'stream_get_line', 'return_type' => Type::STR, 'min_args' => 0, 'max_args' => 2],
// --- stream filters ---
'appendFilter' => ['handler' => 'php_fn', 'fn' => 'stream_filter_append', 'return_type' => CompilerBase::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],
'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' => Type::VAR, 'min_args' => 1, 'max_args' => 3],
],
CompilerBase::TYPE_BIGINT => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::add', 'return_type' => CompilerBase::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],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mul', 'return_type' => CompilerBase::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],
'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::mod', 'return_type' => CompilerBase::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],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::neg', 'return_type' => CompilerBase::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],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::abs', 'return_type' => CompilerBase::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],
'divmod' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::divmod', 'return_type' => CompilerBase::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],
'sqrt' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::sqrt', 'return_type' => CompilerBase::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],
'bitOr' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitOr', 'return_type' => CompilerBase::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],
'bitNot' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitNot', 'return_type' => CompilerBase::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],
'popCount' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::popCount', 'return_type' => CompilerBase::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],
'bitShiftRight' => ['handler' => 'cpp_fn', 'fn' => 'php::BigInt::bitShiftRight', 'return_type' => CompilerBase::TYPE_BIGINT, 'min_args' => 1, 'max_args' => 1],
Type::BIGINT => [
'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' => 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' => 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' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::BIGINT, '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' => Type::BIGINT, 'min_args' => 2, 'max_args' => 2],
'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' => 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' => Type::BIGINT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'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' => 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 => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::add', 'return_type' => CompilerBase::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],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mul', 'return_type' => CompilerBase::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],
'mod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::mod', 'return_type' => CompilerBase::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],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::neg', 'return_type' => CompilerBase::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],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::abs', 'return_type' => CompilerBase::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],
'powmod' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::powmod', 'return_type' => CompilerBase::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],
'floor' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::floor', 'return_type' => CompilerBase::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],
'round' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::round', 'return_type' => CompilerBase::TYPE_DECIMAL, 'min_args' => 0, 'max_args' => 1],
Type::DECIMAL => [
'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' => 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' => 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' => Type::DECIMAL, 'min_args' => 1, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::ARRAY, 'min_args' => 1, 'max_args' => 1],
'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' => 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' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 0],
'round' => ['handler' => 'cpp_fn', 'fn' => 'php::Decimal::round', 'return_type' => Type::DECIMAL, 'min_args' => 0, 'max_args' => 1],
],
CompilerBase::TYPE_BIGFLOAT => [
'add' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::add', 'return_type' => CompilerBase::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],
'mul' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::mul', 'return_type' => CompilerBase::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],
'neg' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::neg', 'return_type' => CompilerBase::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],
'abs' => ['handler' => 'cpp_fn', 'fn' => 'php::BigFloat::abs', 'return_type' => CompilerBase::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],
Type::BIGFLOAT => [
'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' => 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' => Type::BIGFLOAT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::INT, 'min_args' => 1, 'max_args' => 1],
'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' => Type::BIGFLOAT, 'min_args' => 0, 'max_args' => 0],
],
];
@ -336,16 +338,16 @@ trait UniversalMethodCall
}
protected const array TYPE_EXTENSION_PREFIX = [
CompilerBase::TYPE_INT => 'int',
CompilerBase::TYPE_FLOAT => 'float',
CompilerBase::TYPE_BOOL => 'bool',
CompilerBase::TYPE_STR => 'str',
CompilerBase::TYPE_ARRAY => 'array',
CompilerBase::TYPE_STREAM => 'stream',
CompilerBase::TYPE_BIGINT => 'bigint',
CompilerBase::TYPE_DECIMAL => 'decimal',
CompilerBase::TYPE_BIGFLOAT => 'bigfloat',
CompilerBase::TYPE_BOX => 'box',
Type::INT => 'int',
Type::FLOAT => 'float',
Type::BOOL => 'bool',
Type::STR => 'str',
Type::ARRAY => 'array',
Type::STREAM => 'stream',
Type::BIGINT => 'bigint',
Type::DECIMAL => 'decimal',
Type::BIGFLOAT => 'bigfloat',
Type::BOX => 'box',
];
protected function camelToSnake(string $name): string
@ -378,9 +380,9 @@ trait UniversalMethodCall
}
protected const array TO_CONVERT_FN = [
CompilerBase::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'],
CompilerBase::TYPE_DECIMAL => ['toInt' => 'php::Decimal::toInt', 'toFloat' => 'php::Decimal::toFloat', 'toString' => 'php::Decimal::toString'],
Type::BIGINT => ['toInt' => 'php::BigInt::toInt', 'toFloat' => 'php::BigInt::toFloat', 'toString' => 'php::BigInt::toString'],
Type::BIGFLOAT => ['toInt' => 'php::BigFloat::toInt', 'toFloat' => 'php::BigFloat::toFloat', 'toString' => 'php::BigFloat::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];
if ($receiver->byRef
|| $receiver->type !== CompilerBase::TYPE_OBJECT
|| $receiver->type !== Type::OBJECT
|| !$this->isSameClassName($receiver->declaredClass, $class)) {
continue;
}
@ -513,7 +515,7 @@ trait UniversalMethodCall
continue;
}
$firstParam = $funcDef->argInfoList[0];
if ($firstParam->type !== CompilerBase::TYPE_VAR) {
if ($firstParam->type !== Type::VAR) {
continue;
}
@ -564,7 +566,7 @@ trait UniversalMethodCall
}
$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();
$minArgs = max(0, $requiredParams - 1);
@ -594,14 +596,14 @@ trait UniversalMethodCall
}
$firstParam = $funcDef->argInfoList[0];
// Stream/Box are PHP pseudo-types; their params may be typed or untyped
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) {
return $firstParam->byRef || $firstParam->type === self::TYPE_VAR || $firstParam->type === self::TYPE_REF || $firstParam->type === $type;
if ($type === Type::STREAM || $type === Type::BOX) {
return $firstParam->byRef || $firstParam->type === Type::VAR || $firstParam->type === Type::REF || $firstParam->type === $type;
}
if ($firstParam->byRef) {
return $type === self::TYPE_ARRAY;
return $type === Type::ARRAY;
}
$paramType = $firstParam->type;
if ($paramType === self::TYPE_VAR) {
if ($paramType === Type::VAR) {
return false;
}
return $paramType === $type;
@ -614,11 +616,11 @@ trait UniversalMethodCall
return false;
}
// Stream pseudo-type: accept untyped or by-reference first params
if ($type === self::TYPE_STREAM) {
if ($type === Type::STREAM) {
return true;
}
if ($param->isPassedByReference()) {
return $type === self::TYPE_ARRAY;
return $type === Type::ARRAY;
}
$paramType = $param->getType();
if ($paramType === null) {
@ -678,7 +680,7 @@ trait UniversalMethodCall
$this->validateUniversalMethodArgs($expr, $method, $def, false);
// 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 . ';';
$methodCall = match ($def['handler']) {
@ -688,7 +690,7 @@ trait UniversalMethodCall
default => null,
};
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$tmpVar = $this->addTmpVar(Type::VAR);
$this->context->beforeStmtLines[] = "{$tmpVar} = {$methodCall};";
return $tmpVar;
}
@ -700,11 +702,11 @@ trait UniversalMethodCall
protected function wrapUniversalReceiver(string $type, string $expr): string
{
$convFns = [
self::TYPE_ARRAY => 'toArray',
self::TYPE_STR => 'toString',
self::TYPE_INT => 'toInt',
self::TYPE_FLOAT => 'toFloat',
self::TYPE_BOOL => 'toBool',
Type::ARRAY => 'toArray',
Type::STR => 'toString',
Type::INT => 'toInt',
Type::FLOAT => 'toFloat',
Type::BOOL => 'toBool',
];
if (isset($convFns[$type])) {
return 'php::' . $convFns[$type] . '(' . $expr . ')';
@ -898,8 +900,8 @@ trait UniversalMethodCall
protected function genUniversalPhpFnRef(string $receiver, string $phpFunc, array $args, string $returnType): string
{
$tmpRef = $this->addTmpVar(self::TYPE_REF);
$tmpVar = $this->addTmpVar(self::TYPE_VAR);
$tmpRef = $this->addTmpVar(Type::REF);
$tmpVar = $this->addTmpVar(Type::VAR);
$argExprs = ['&' . $tmpRef];
foreach ($args as $arg) {
$argExprs[] = $this->parseExpr($arg->value);

@ -69,8 +69,8 @@ class Preprocessor extends CompilerBase
protected function genArgumentDeclaration(ArgInfo $argInfo): string
{
$type = $argInfo->type;
if ($type === self::TYPE_STREAM || $type === self::TYPE_BOX) {
$type = self::TYPE_VAR;
if ($type === Type::STREAM || $type === Type::BOX) {
$type = Type::VAR;
}
return $type . ' ' . $argInfo->name;
}
@ -267,7 +267,7 @@ class Preprocessor extends CompilerBase
protected function parseParameterType(Node\Param $param, ArgInfo $argInfo, string $var): string
{
if ($param->byRef) {
return self::TYPE_REF;
return Type::REF;
}
$class = '';
$type = $this->parseTypeDecl($param->type, self::DECL_TYPE_OF_PARAM, $class);
@ -367,7 +367,7 @@ class Preprocessor extends CompilerBase
}
}
if ($param->variadic) {
$list[] = self::TYPE_ARRAY . ' ' . $name;
$list[] = Type::ARRAY . ' ' . $name;
} else {
$list[] = $this->genArgumentDeclaration($argInfo);
}
@ -438,7 +438,7 @@ class Preprocessor extends CompilerBase
$returnType = $this->parseTypeDecl($v->returnType, self::DECL_TYPE_OF_RETURN, $class);
// 构造、析构、克隆方法不能有返回值
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);
@ -470,13 +470,13 @@ class Preprocessor extends CompilerBase
if (count($v->params) != 2) {
$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');
}
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`.');
}
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`.');
}
}
@ -648,9 +648,9 @@ class Preprocessor extends CompilerBase
$type = $declaredType;
if ($type === null) {
$type = match ($const->value->getType()) {
'Expr_Array' => self::TYPE_ARRAY,
'Scalar_String' => self::TYPE_STR,
default => self::TYPE_VAR,
'Expr_Array' => Type::ARRAY,
'Scalar_String' => Type::STR,
default => Type::VAR,
};
}
$constName = $this->parseIdentifier($const->name);
@ -697,7 +697,7 @@ class Preprocessor extends CompilerBase
$arrayInitPlan = null;
if ($defaultNode !== null) {
if ($defaultNode instanceof Node\Expr\Array_) {
$type = self::TYPE_ARRAY;
$type = Type::ARRAY;
$arrayInitPlan = $this->buildLiteralArrayInitPlan($defaultNode);
$default = $arrayInitPlan->expr;
} else {
@ -868,9 +868,9 @@ class Preprocessor extends CompilerBase
$type = $stmt->type
? $this->parseTypeDecl($stmt->type, self::DECL_TYPE_OF_CONST, $class)
: match ($const->value->getType()) {
'Expr_Array' => self::TYPE_ARRAY,
'Scalar_String' => self::TYPE_STR,
default => self::TYPE_VAR,
'Expr_Array' => Type::ARRAY,
'Scalar_String' => Type::STR,
default => Type::VAR,
};
$constInfo = $this->parseClassLikeConstant($const, $this->parseModifiers($stmt->flags), $type, $class);
$this->interfaceDef->constants[$constName] = $constInfo;

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

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

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

@ -649,12 +649,12 @@ class Translator extends Preprocessor
$lines[] = '#include <phpx.h>';
$lines[] = PHP_EOL;
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) {
$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++ 编译错误
@ -669,9 +669,9 @@ class Translator extends Preprocessor
foreach ($this->getClassLikesWithConstants() as $classDef) {
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);
$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";
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";
@ -767,9 +767,9 @@ CODE;
$code .= "// literal strings \n";
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) {
$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;
} else {
@ -788,9 +788,9 @@ CODE;
$code .= 'static zend_object_handlers property_handlers_' . $classDef->getNamespacedName() . ";\n";
}
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);
$code .= self::TYPE_VAR . ' ' . $constName . ";\n";
$code .= Type::VAR . ' ' . $constName . ";\n";
}
}
}
@ -880,7 +880,7 @@ CODE;
}
}
foreach ($this->constants as $name => $const) {
if ($const->type !== self::TYPE_VAR) {
if ($const->type !== Type::VAR) {
continue;
}
$code .= $name . '.unset();' . PHP_EOL;
@ -889,7 +889,7 @@ CODE;
$code .= '// class array constants' . PHP_EOL;
foreach ($this->getClassLikesWithConstants() as $classDef) {
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);
$code .= $constName . ".unset();\n";
@ -904,7 +904,7 @@ CODE;
foreach ($this->symbols->classes() as $className => $classDef) {
$ownConstNames = [];
foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) {
if ($constant->type === Type::ARRAY) {
$ownConstNames[$constant->name] = true;
}
}
@ -913,7 +913,7 @@ CODE;
while ($parentName && $this->symbols->hasClass($parentName)) {
$parentDef = $this->symbols->class($parentName);
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;
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name);
@ -929,7 +929,7 @@ CODE;
}
$interfaceDef = $this->getInterface($interfaceName);
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;
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
$classConstStr = $this->genCharPtr($constant->name);
@ -1427,21 +1427,21 @@ CODE;
// 函数的默认值可能会使用字符串字面量,需要提前声明
if ($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();
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 = [];
if ($func->method) {
$list[] = self::TYPE_OBJECT . ' &this_';
$list[] = Type::OBJECT . ' &this_';
}
$argInfoList = $func->argInfoList;
if ($argInfoList) {
foreach ($argInfoList as $argInfo) {
if ($argInfo->variadic) {
$arg = self::TYPE_ARRAY . ' ' . $argInfo->name . ' = {}';
$arg = Type::ARRAY . ' ' . $argInfo->name . ' = {}';
} else {
$arg = $this->genArgumentDeclaration($argInfo);
if ($argInfo->default && !$this->isConstructorNativeFunction($func)) {
@ -1599,7 +1599,7 @@ CODE;
$code = '';
foreach ($this->getClassLikesWithConstants() as $classDef) {
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);
$code .= "do {\n";
$code .= $constant->arrayExpr;
@ -1616,7 +1616,7 @@ CODE;
foreach ($this->symbols->classes() as $className => $classDef) {
$ownConstNames = [];
foreach ($classDef->constants as $constant) {
if ($constant->type === self::TYPE_ARRAY) {
if ($constant->type === Type::ARRAY) {
$ownConstNames[$constant->name] = true;
}
}
@ -1625,7 +1625,7 @@ CODE;
while ($parentName && $this->symbols->hasClass($parentName)) {
$parentDef = $this->symbols->class($parentName);
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;
$constName = self::PREFIX . $this->getNativeName($constant->name, $parentDef->namespace, $parentDef->name);
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
@ -1642,7 +1642,7 @@ CODE;
}
$interfaceDef = $this->getInterface($interfaceName);
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;
$constName = self::PREFIX . $this->getNativeName($constant->name, $interfaceDef->namespace, $interfaceDef->name);
$classNameStr = $this->genCharPtr($classDef->getNamespacedName(false), true);
@ -2584,7 +2584,7 @@ CODE;
foreach ($functionDef->argInfoList as $k => $argInfo) {
$var = 'arg_' . $argInfo->name;
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;
$this->indentLevel++;
$cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL;
@ -2608,7 +2608,7 @@ CODE;
}
$cppType = $this->getDefaultArgumentType($argInfo);
$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));
} else {
$expr = $this->convertExprFromType($argInfo->type, $argExpr);
@ -2624,7 +2624,7 @@ CODE;
$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() . 'php::move(retval, return_value);' . PHP_EOL;
if (!$functionDef->returnsByRef) {
@ -2662,7 +2662,7 @@ CODE;
{
$name = $classDef->getNamespacedName();
$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);
$cppCode .= $this->genWrapperFunctionArgs($fn, $methodDef->functionDef, $classDef->getNamespacedName(false) . '::' . $methodDef->name);
@ -2690,7 +2690,7 @@ CODE;
if ($classDef instanceof ClassDef) {
$arrayPropCount = 0;
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++;
}
}
@ -2800,10 +2800,10 @@ CODE;
}
if ($this->class) {
$this->addArgument('this_', self::TYPE_OBJECT);
$this->addArgument('this_', Type::OBJECT);
}
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) {
$this->addObject($argInfo->name, $argInfo->declaredClass);
}
@ -2853,10 +2853,10 @@ CODE;
$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 . '(';
if ($this->class) {
$functionDeclCode .= self::TYPE_OBJECT . ' &this_';
$functionDeclCode .= Type::OBJECT . ' &this_';
if ($this->functionDef->params) {
$functionDeclCode .= ', ';
}
@ -3043,13 +3043,13 @@ CODE;
if ($parentFuncDef->returnTypeCheck || $childFuncDef->returnTypeCheck) {
return $parentFuncDef->returnTypeStr === $childFuncDef->returnTypeStr;
}
if ($parentFuncDef->returnType === self::TYPE_VAR) {
if ($parentFuncDef->returnType === Type::VAR) {
return true;
}
if ($childFuncDef->returnType !== $parentFuncDef->returnType) {
return false;
}
if ($parentFuncDef->returnType !== self::TYPE_OBJECT) {
if ($parentFuncDef->returnType !== Type::OBJECT) {
return true;
}
if ($childFuncDef->returnClass === $parentFuncDef->returnClass) {
@ -3084,7 +3084,7 @@ CODE;
if ($childArg->type !== $parentArg->type) {
return false;
}
if ($parentArg->type !== self::TYPE_OBJECT) {
if ($parentArg->type !== Type::OBJECT) {
return true;
}
if ($childArg->class === $parentArg->class) {
@ -3108,13 +3108,13 @@ CODE;
}
return match ($arg->type) {
self::TYPE_INT => [['kind' => 'isInt']],
self::TYPE_FLOAT => [['kind' => 'isFloat']],
self::TYPE_BOOL => [['kind' => 'isBool']],
self::TYPE_STR => [['kind' => 'isString']],
self::TYPE_ARRAY => [['kind' => 'isArray']],
self::TYPE_RESOURCE => [['kind' => 'isResource']],
self::TYPE_OBJECT => $arg->class
Type::INT => [['kind' => 'isInt']],
Type::FLOAT => [['kind' => 'isFloat']],
Type::BOOL => [['kind' => 'isBool']],
Type::STR => [['kind' => 'isString']],
Type::ARRAY => [['kind' => 'isArray']],
Type::RESOURCE => [['kind' => 'isResource']],
Type::OBJECT => $arg->class
? [['kind' => 'instanceof', 'class' => $arg->class]]
: [['kind' => 'isObject']],
default => null,
@ -3484,10 +3484,10 @@ CODE;
}
$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 . '(';
if ($this->class) {
$code .= self::TYPE_OBJECT . ' &this_';
$code .= Type::OBJECT . ' &this_';
if ($methodDef->functionDef->params) {
$code .= ', ';
}
@ -3499,7 +3499,7 @@ CODE;
$code .= '{' . PHP_EOL;
$this->indentLevel++;
$methodCall = self::PREFIX . $traitMethodNativeName . '(' . $argv . ')';
if ($cppReturnType !== self::TYPE_VOID) {
if ($cppReturnType !== Type::VOID) {
$methodCall = 'return ' . $methodCall;
}
$code .= $this->getIndent() . $methodCall . ';' . PHP_EOL;
@ -3529,16 +3529,16 @@ CODE;
{
$obj = $objectExpr ?? $this->parseIdentifier($node->expr);
$iterableVar = $this->genTmpVarName();
$this->addLocalVar($iterableVar, self::TYPE_VAR);
$this->addLocalVar($iterableVar, Type::VAR);
$iteratorObj = $this->genTmpVarName();
$this->addLocalVar($iteratorObj, self::TYPE_OBJECT);
$this->addLocalVar($iteratorObj, Type::OBJECT);
$aggregateObj = $this->genTmpVarName();
$this->addLocalVar($aggregateObj, self::TYPE_OBJECT);
$this->addLocalVar($aggregateObj, Type::OBJECT);
$tmpArrayVar = $this->genTmpVarName();
$this->addLocalVar($tmpArrayVar, self::TYPE_ARRAY);
$this->addLocalVar($tmpArrayVar, Type::ARRAY);
$IteratorAggregateCe = $this->getClassEntryPtr('IteratorAggregate');
$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;
use TypePhp\Type;
use PhpParser\Node\Expr;
use PhpParser\NodeAbstract;
@ -33,7 +35,7 @@ trait CompositeTypeCheckerTrait
// TYPE_VAR means that the expression is dynamic or its result cannot
// be represented by the current scalar type system. It must retain the
// 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;
}
@ -77,19 +79,19 @@ trait CompositeTypeCheckerTrait
$type = $this->detectTypeOfExpr($value);
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
// needs conversion, so retain the runtime normalization path.
'isFloat' => $type === self::TYPE_INT
'isFloat' => $type === Type::INT
? self::COMPOSITE_TYPE_UNKNOWN
: $this->exactCompositeTypeRelation($type, self::TYPE_FLOAT),
'isBool' => $this->exactCompositeTypeRelation($type, self::TYPE_BOOL),
'isString' => $this->exactCompositeTypeRelation($type, self::TYPE_STR),
'isArray' => $this->exactCompositeTypeRelation($type, self::TYPE_ARRAY),
'isObject' => $this->exactCompositeTypeRelation($type, self::TYPE_OBJECT),
: $this->exactCompositeTypeRelation($type, Type::FLOAT),
'isBool' => $this->exactCompositeTypeRelation($type, Type::BOOL),
'isString' => $this->exactCompositeTypeRelation($type, Type::STR),
'isArray' => $this->exactCompositeTypeRelation($type, Type::ARRAY),
'isObject' => $this->exactCompositeTypeRelation($type, Type::OBJECT),
'isTrue' => $this->compositeLiteralBoolRelation($value, true),
'isFalse' => $this->compositeLiteralBoolRelation($value, false),
'isResource' => $this->exactCompositeTypeRelation($type, self::TYPE_RESOURCE),
'isResource' => $this->exactCompositeTypeRelation($type, Type::RESOURCE),
'callable' => $this->compositeCallableRelation($value, $type),
'iterable' => $this->compositeIterableRelation($value, $type),
'instanceof' => $this->compositeObjectEntryRelation($value, $entry),
@ -108,14 +110,14 @@ trait CompositeTypeCheckerTrait
$actual = strcasecmp($value->name->toString(), 'true') === 0;
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_MISMATCH;
}
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_MISMATCH;
@ -123,10 +125,10 @@ trait CompositeTypeCheckerTrait
protected function compositeIterableRelation(NodeAbstract $value, string $type): int
{
if ($type === self::TYPE_ARRAY) {
if ($type === Type::ARRAY) {
return self::COMPOSITE_TYPE_MATCH;
}
if ($type !== self::TYPE_OBJECT) {
if ($type !== Type::OBJECT) {
return self::COMPOSITE_TYPE_MISMATCH;
}
return $this->compositeObjectTypeRelation($value, 'Traversable');
@ -134,7 +136,7 @@ trait CompositeTypeCheckerTrait
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;
}
@ -182,12 +184,12 @@ trait CompositeTypeCheckerTrait
}
$type = $this->detectTypeOfExpr($expr);
return match ($type) {
self::TYPE_INT => 'int',
self::TYPE_FLOAT => 'float',
self::TYPE_BOOL => 'bool',
self::TYPE_STR => 'string',
self::TYPE_ARRAY => 'array',
self::TYPE_OBJECT => 'object',
Type::INT => 'int',
Type::FLOAT => 'float',
Type::BOOL => 'bool',
Type::STR => 'string',
Type::ARRAY => 'array',
Type::OBJECT => 'object',
default => 'mixed',
};
}

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

Loading…
Cancel
Save