TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

436 lines
17 KiB

<?php
namespace PhpAot\Tests;
use PHPUnit\Framework\TestCase;
use PhpAot\Php\CompilerTest;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;
class AstNodeTypeTest extends TestCase
{
private CompilerTest $compiler;
private \ReflectionClass $ref;
private string $tmpDir;
protected function setUp(): void
{
parent::setUp();
$this->tmpDir = sys_get_temp_dir() . '/ast_node_type_test_' . uniqid();
mkdir($this->tmpDir, 0777, true);
$this->compiler = CompilerTest::create($this->tmpDir);
$this->ref = new \ReflectionClass($this->compiler);
}
protected function tearDown(): void
{
parent::tearDown();
if (is_dir($this->tmpDir)) {
$this->removeDirectory($this->tmpDir);
}
}
private function removeDirectory(string $dir): void
{
if (!is_dir($dir)) {
return;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . DIRECTORY_SEPARATOR . $file;
is_dir($path) ? $this->removeDirectory($path) : unlink($path);
}
rmdir($dir);
}
private function invoke(string $method, ...$args): mixed
{
$m = $this->ref->getMethod($method);
$m->setAccessible(true);
return $m->invoke($this->compiler, ...$args);
}
// ========================================================================
// isArrayDimFetch
// ========================================================================
public function testIsArrayDimFetch(): void
{
$var = new Expr\Variable('arr');
$this->assertTrue($this->invoke('isArrayDimFetch', new Expr\ArrayDimFetch($var)));
$this->assertFalse($this->invoke('isArrayDimFetch', $var));
}
// ========================================================================
// isVarExpr
// ========================================================================
public function testIsVarExpr(): void
{
$this->assertTrue($this->invoke('isVarExpr', new Expr\Variable('foo')));
$this->assertFalse($this->invoke('isVarExpr', new Node\Scalar\Int_(42)));
}
// ========================================================================
// isIdExpr
// ========================================================================
public function testIsIdExpr(): void
{
$this->assertTrue($this->invoke('isIdExpr', new Node\Identifier('foo')));
$this->assertFalse($this->invoke('isIdExpr', new Expr\Variable('foo')));
}
// ========================================================================
// isPropertyFetch
// ========================================================================
public function testIsPropertyFetch(): void
{
$obj = new Expr\Variable('obj');
$this->assertTrue($this->invoke('isPropertyFetch', new Expr\PropertyFetch($obj, 'prop')));
$this->assertFalse($this->invoke('isPropertyFetch', $obj));
}
// ========================================================================
// isStaticPropertyFetch
// ========================================================================
public function testIsStaticPropertyFetch(): void
{
$class = new Node\Name('Foo');
$this->assertTrue($this->invoke('isStaticPropertyFetch', new Expr\StaticPropertyFetch($class, 'prop')));
$this->assertFalse($this->invoke('isStaticPropertyFetch', new Expr\Variable('a')));
}
// ========================================================================
// isClassConstFetch
// ========================================================================
public function testIsClassConstFetch(): void
{
$class = new Node\Name('Foo');
$this->assertTrue($this->invoke('isClassConstFetch', new Expr\ClassConstFetch($class, 'BAR')));
$this->assertFalse($this->invoke('isClassConstFetch', new Expr\Variable('a')));
}
// ========================================================================
// isNewExpr
// ========================================================================
public function testIsNewExpr(): void
{
$class = new Node\Name('Foo');
$this->assertTrue($this->invoke('isNewExpr', new Expr\New_($class)));
$this->assertFalse($this->invoke('isNewExpr', new Expr\Variable('a')));
}
// ========================================================================
// isNameExpr
// ========================================================================
public function testIsNameExpr(): void
{
$this->assertTrue($this->invoke('isNameExpr', new Node\Name('Foo')));
$this->assertFalse($this->invoke('isNameExpr', new Expr\Variable('Foo')));
}
// ========================================================================
// isFullNameExpr
// ========================================================================
public function testIsFullNameExpr(): void
{
$this->assertTrue($this->invoke('isFullNameExpr', new Node\Name\FullyQualified('Foo\\Bar')));
$this->assertFalse($this->invoke('isFullNameExpr', new Node\Name('Foo')));
}
// ========================================================================
// isNamedMethod
// ========================================================================
public function testIsNamedMethod(): void
{
$this->assertTrue($this->invoke('isNamedMethod', new Node\Identifier('methodName')));
$this->assertFalse($this->invoke('isNamedMethod', new Expr\Variable('a')));
}
// ========================================================================
// isScalarString
// ========================================================================
public function testIsScalarString(): void
{
$this->assertTrue($this->invoke('isScalarString', new Node\Scalar\String_('hello')));
$this->assertFalse($this->invoke('isScalarString', new Node\Scalar\Int_(1)));
}
// ========================================================================
// isFuncCallExpr
// ========================================================================
public function testIsFuncCallExpr(): void
{
$name = new Node\Name('foo');
$this->assertTrue($this->invoke('isFuncCallExpr', new Expr\FuncCall($name)));
$this->assertFalse($this->invoke('isFuncCallExpr', new Expr\Variable('a')));
}
// ========================================================================
// isRefvalCall
// ========================================================================
public function testIsRefvalCall(): void
{
$refvalCall = new Expr\FuncCall(new Node\Name('refval'));
$this->assertTrue($this->invoke('isRefvalCall', $refvalCall));
$otherCall = new Expr\FuncCall(new Node\Name('other'));
$this->assertFalse($this->invoke('isRefvalCall', $otherCall));
$this->assertFalse($this->invoke('isRefvalCall', new Expr\Variable('a')));
}
// ========================================================================
// isMethodCall
// ========================================================================
public function testIsMethodCall(): void
{
$obj = new Expr\Variable('obj');
$this->assertTrue($this->invoke('isMethodCall', new Expr\MethodCall($obj, 'method')));
$this->assertFalse($this->invoke('isMethodCall', $obj));
}
// ========================================================================
// isStaticCall
// ========================================================================
public function testIsStaticCall(): void
{
$class = new Node\Name('Foo');
$this->assertTrue($this->invoke('isStaticCall', new Expr\StaticCall($class, 'method')));
$this->assertFalse($this->invoke('isStaticCall', new Expr\Variable('a')));
}
// ========================================================================
// isScalar / isScalarInt / isScalarBool
// ========================================================================
public function testIsScalar(): void
{
$this->assertTrue($this->invoke('isScalar', new Node\Scalar\Int_(1)));
$this->assertTrue($this->invoke('isScalar', new Node\Scalar\String_('s')));
$this->assertTrue($this->invoke('isScalar', new Node\Scalar\Float_(1.0)));
$this->assertFalse($this->invoke('isScalar', new Expr\Variable('a')));
}
public function testIsScalarInt(): void
{
$this->assertTrue($this->invoke('isScalarInt', new Node\Scalar\Int_(42)));
$this->assertFalse($this->invoke('isScalarInt', new Node\Scalar\String_('42')));
}
public function testIsScalarBoolTrue(): void
{
$trueConst = new Expr\ConstFetch(new Node\Name('true'));
$this->assertTrue($this->invoke('isScalarBool', $trueConst));
$this->assertEquals('php::true_', $this->invoke('getBoolValue', $trueConst));
}
public function testIsScalarBoolFalse(): void
{
$falseConst = new Expr\ConstFetch(new Node\Name('false'));
$this->assertTrue($this->invoke('isScalarBool', $falseConst));
$this->assertEquals('php::false_', $this->invoke('getBoolValue', $falseConst));
}
public function testIsScalarBoolNotBool(): void
{
$nullConst = new Expr\ConstFetch(new Node\Name('null'));
$this->assertFalse($this->invoke('isScalarBool', $nullConst));
}
// ========================================================================
// isMatchExpr
// ========================================================================
public function testIsMatchExpr(): void
{
$cond = new Expr\Variable('x');
$this->assertTrue($this->invoke('isMatchExpr', new Expr\Match_($cond)));
$this->assertFalse($this->invoke('isMatchExpr', $cond));
}
// ========================================================================
// isConstFetch
// ========================================================================
public function testIsConstFetch(): void
{
$this->assertTrue($this->invoke('isConstFetch', new Expr\ConstFetch(new Node\Name('FOO'))));
$this->assertFalse($this->invoke('isConstFetch', new Expr\Variable('a')));
}
// ========================================================================
// isAssignOp / isAssignExpr
// ========================================================================
public function testIsAssignOp(): void
{
$var = new Expr\Variable('a');
$val = new Node\Scalar\Int_(1);
// Assign is also an AssignOp
$this->assertTrue($this->invoke('isAssignOp', new Expr\Assign($var, $val)));
$this->assertTrue($this->invoke('isAssignOp', new Expr\AssignOp\Plus($var, $val)));
$this->assertFalse($this->invoke('isAssignOp', $var));
}
public function testIsAssignExpr(): void
{
$var = new Expr\Variable('a');
$val = new Node\Scalar\Int_(1);
$this->assertTrue($this->invoke('isAssignExpr', new Expr\Assign($var, $val)));
$this->assertFalse($this->invoke('isAssignExpr', new Expr\AssignOp\Plus($var, $val)));
}
// ========================================================================
// isCallExpr
// ========================================================================
public function testIsCallExpr(): void
{
$name = new Node\Name('foo');
$obj = new Expr\Variable('obj');
$class = new Node\Name('Foo');
$this->assertTrue($this->invoke('isCallExpr', new Expr\FuncCall($name)));
$this->assertTrue($this->invoke('isCallExpr', new Expr\MethodCall($obj, 'bar')));
$this->assertTrue($this->invoke('isCallExpr', new Expr\StaticCall($class, 'baz')));
$this->assertFalse($this->invoke('isCallExpr', $obj));
}
// ========================================================================
// isPlaceholderExpr
// ========================================================================
public function testIsPlaceholderExpr(): void
{
$this->assertTrue($this->invoke('isPlaceholderExpr', new VariadicPlaceholder()));
$this->assertFalse($this->invoke('isPlaceholderExpr', new Expr\Variable('a')));
}
// ========================================================================
// isReturnExpr
// ========================================================================
public function testIsReturnExpr(): void
{
$this->assertTrue($this->invoke('isReturnExpr', new Node\Stmt\Return_(new Node\Scalar\Int_(1))));
$this->assertFalse($this->invoke('isReturnExpr', new Expr\Variable('a')));
}
// ========================================================================
// isBreakExpr
// ========================================================================
public function testIsBreakExpr(): void
{
$this->assertTrue($this->invoke('isBreakExpr', new Node\Stmt\Break_()));
$this->assertFalse($this->invoke('isBreakExpr', new Expr\Variable('a')));
}
// ========================================================================
// isThrowExpr
// ========================================================================
public function testIsThrowExprDirect(): void
{
$this->assertTrue($this->invoke('isThrowExpr', new Expr\Throw_(new Expr\Variable('e'))));
}
public function testIsThrowExprWrapped(): void
{
$throwExpr = new Expr\Throw_(new Expr\Variable('e'));
$wrapped = new Node\Stmt\Expression($throwExpr);
$this->assertTrue($this->invoke('isThrowExpr', $wrapped));
}
public function testIsThrowExprNotThrow(): void
{
$this->assertFalse($this->invoke('isThrowExpr', new Expr\Variable('a')));
}
// ========================================================================
// isExitExpr
// ========================================================================
public function testIsExitExprDirect(): void
{
$this->assertTrue($this->invoke('isExitExpr', new Expr\Exit_()));
}
public function testIsExitExprWrapped(): void
{
$exitExpr = new Expr\Exit_();
$wrapped = new Node\Stmt\Expression($exitExpr);
$this->assertTrue($this->invoke('isExitExpr', $wrapped));
}
public function testIsExitExprNotExit(): void
{
$this->assertFalse($this->invoke('isExitExpr', new Expr\Variable('a')));
}
// ========================================================================
// isEmptyArray
// ========================================================================
public function testIsEmptyArray(): void
{
$this->assertTrue($this->invoke('isEmptyArray', new Expr\Array_([])));
$this->assertFalse($this->invoke('isEmptyArray', new Expr\Array_([new Node\ArrayItem(new Node\Scalar\Int_(1))])));
}
public function testIsEmptyArrayNotArray(): void
{
$this->assertFalse($this->invoke('isEmptyArray', new Expr\Variable('a')));
}
// ========================================================================
// isNull
// ========================================================================
public function testIsNull(): void
{
$this->assertTrue($this->invoke('isNull', new Expr\ConstFetch(new Node\Name('null'))));
$this->assertFalse($this->invoke('isNull', new Expr\ConstFetch(new Node\Name('true'))));
$this->assertFalse($this->invoke('isNull', new Expr\Variable('a')));
}
// ========================================================================
// Cross-type verification: each is* is false for unrelated types
// ========================================================================
public function testNoCrossFalsePositives(): void
{
$var = new Expr\Variable('x');
$methods = [
'isArrayDimFetch', 'isPropertyFetch', 'isStaticPropertyFetch',
'isClassConstFetch', 'isNewExpr', 'isNameExpr', 'isFullNameExpr',
'isFuncCallExpr', 'isRefvalCall', 'isMethodCall', 'isStaticCall',
'isMatchExpr', 'isConstFetch', 'isAssignOp', 'isAssignExpr',
'isCallExpr', 'isPlaceholderExpr', 'isReturnExpr', 'isBreakExpr',
'isThrowExpr', 'isExitExpr', 'isEmptyArray', 'isNull',
];
foreach ($methods as $method) {
$this->assertFalse(
$this->invoke($method, $var),
"{$method} should return false for a plain Variable"
);
}
}
}