First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,15 @@
<?php
namespace PhpParser;
/* The autoloader is already active at this point, so we only check effects here. */
class AutoloaderTest extends \PHPUnit_Framework_TestCase {
public function testClassExists() {
$this->assertTrue(class_exists('PhpParser\NodeVisitorAbstract'));
$this->assertFalse(class_exists('PHPParser_NodeVisitor_NameResolver'));
$this->assertFalse(class_exists('PhpParser\FooBar'));
$this->assertFalse(class_exists('PHPParser_FooBar'));
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class ClassTest extends \PHPUnit_Framework_TestCase
{
protected function createClassBuilder($class) {
return new Class_($class);
}
public function testExtendsImplements() {
$node = $this->createClassBuilder('SomeLogger')
->extend('BaseLogger')
->implement('Namespaced\Logger', new Name('SomeInterface'))
->implement('\Fully\Qualified', 'namespace\NamespaceRelative')
->getNode()
;
$this->assertEquals(
new Stmt\Class_('SomeLogger', array(
'extends' => new Name('BaseLogger'),
'implements' => array(
new Name('Namespaced\Logger'),
new Name('SomeInterface'),
new Name\FullyQualified('Fully\Qualified'),
new Name\Relative('NamespaceRelative'),
),
)),
$node
);
}
public function testAbstract() {
$node = $this->createClassBuilder('Test')
->makeAbstract()
->getNode()
;
$this->assertEquals(
new Stmt\Class_('Test', array(
'flags' => Stmt\Class_::MODIFIER_ABSTRACT
)),
$node
);
}
public function testFinal() {
$node = $this->createClassBuilder('Test')
->makeFinal()
->getNode()
;
$this->assertEquals(
new Stmt\Class_('Test', array(
'flags' => Stmt\Class_::MODIFIER_FINAL
)),
$node
);
}
public function testStatementOrder() {
$method = new Stmt\ClassMethod('testMethod');
$property = new Stmt\Property(
Stmt\Class_::MODIFIER_PUBLIC,
array(new Stmt\PropertyProperty('testProperty'))
);
$const = new Stmt\ClassConst(array(
new Node\Const_('TEST_CONST', new Node\Scalar\String_('ABC'))
));
$use = new Stmt\TraitUse(array(new Name('SomeTrait')));
$node = $this->createClassBuilder('Test')
->addStmt($method)
->addStmt($property)
->addStmts(array($const, $use))
->getNode()
;
$this->assertEquals(
new Stmt\Class_('Test', array(
'stmts' => array($use, $const, $property, $method)
)),
$node
);
}
public function testDocComment() {
$docComment = <<<'DOC'
/**
* Test
*/
DOC;
$class = $this->createClassBuilder('Test')
->setDocComment($docComment)
->getNode();
$this->assertEquals(
new Stmt\Class_('Test', array(), array(
'comments' => array(
new Comment\Doc($docComment)
)
)),
$class
);
$class = $this->createClassBuilder('Test')
->setDocComment(new Comment\Doc($docComment))
->getNode();
$this->assertEquals(
new Stmt\Class_('Test', array(), array(
'comments' => array(
new Comment\Doc($docComment)
)
)),
$class
);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Unexpected node of type "Stmt_Echo"
*/
public function testInvalidStmtError() {
$this->createClassBuilder('Test')
->addStmt(new Stmt\Echo_(array()))
;
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Doc comment must be a string or an instance of PhpParser\Comment\Doc
*/
public function testInvalidDocComment() {
$this->createClassBuilder('Test')
->setDocComment(new Comment('Test'));
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Name cannot be empty
*/
public function testEmptyName() {
$this->createClassBuilder('Test')
->extend('');
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Name must be a string or an instance of PhpParser\Node\Name
*/
public function testInvalidName() {
$this->createClassBuilder('Test')
->extend(array('Foo'));
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Expr\Print_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
class FunctionTest extends \PHPUnit_Framework_TestCase
{
public function createFunctionBuilder($name) {
return new Function_($name);
}
public function testReturnByRef() {
$node = $this->createFunctionBuilder('test')
->makeReturnByRef()
->getNode()
;
$this->assertEquals(
new Stmt\Function_('test', array(
'byRef' => true
)),
$node
);
}
public function testParams() {
$param1 = new Node\Param('test1');
$param2 = new Node\Param('test2');
$param3 = new Node\Param('test3');
$node = $this->createFunctionBuilder('test')
->addParam($param1)
->addParams(array($param2, $param3))
->getNode()
;
$this->assertEquals(
new Stmt\Function_('test', array(
'params' => array($param1, $param2, $param3)
)),
$node
);
}
public function testStmts() {
$stmt1 = new Print_(new String_('test1'));
$stmt2 = new Print_(new String_('test2'));
$stmt3 = new Print_(new String_('test3'));
$node = $this->createFunctionBuilder('test')
->addStmt($stmt1)
->addStmts(array($stmt2, $stmt3))
->getNode()
;
$this->assertEquals(
new Stmt\Function_('test', array(
'stmts' => array($stmt1, $stmt2, $stmt3)
)),
$node
);
}
public function testDocComment() {
$node = $this->createFunctionBuilder('test')
->setDocComment('/** Test */')
->getNode();
$this->assertEquals(new Stmt\Function_('test', array(), array(
'comments' => array(new Comment\Doc('/** Test */'))
)), $node);
}
public function testReturnType() {
$node = $this->createFunctionBuilder('test')
->setReturnType('void')
->getNode();
$this->assertEquals(new Stmt\Function_('test', array(
'returnType' => 'void'
), array()), $node);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage void type cannot be nullable
*/
public function testInvalidNullableVoidType() {
$this->createFunctionBuilder('test')->setReturnType('?void');
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Expected parameter node, got "Name"
*/
public function testInvalidParamError() {
$this->createFunctionBuilder('test')
->addParam(new Node\Name('foo'))
;
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Stmt;
class InterfaceTest extends \PHPUnit_Framework_TestCase
{
/** @var Interface_ */
protected $builder;
protected function setUp() {
$this->builder = new Interface_('Contract');
}
private function dump($node) {
$pp = new \PhpParser\PrettyPrinter\Standard;
return $pp->prettyPrint(array($node));
}
public function testEmpty() {
$contract = $this->builder->getNode();
$this->assertInstanceOf('PhpParser\Node\Stmt\Interface_', $contract);
$this->assertSame('Contract', $contract->name);
}
public function testExtending() {
$contract = $this->builder->extend('Space\Root1', 'Root2')->getNode();
$this->assertEquals(
new Stmt\Interface_('Contract', array(
'extends' => array(
new Node\Name('Space\Root1'),
new Node\Name('Root2')
),
)), $contract
);
}
public function testAddMethod() {
$method = new Stmt\ClassMethod('doSomething');
$contract = $this->builder->addStmt($method)->getNode();
$this->assertSame(array($method), $contract->stmts);
}
public function testAddConst() {
$const = new Stmt\ClassConst(array(
new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458.0))
));
$contract = $this->builder->addStmt($const)->getNode();
$this->assertSame(299792458.0, $contract->stmts[0]->consts[0]->value->value);
}
public function testOrder() {
$const = new Stmt\ClassConst(array(
new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458))
));
$method = new Stmt\ClassMethod('doSomething');
$contract = $this->builder
->addStmt($method)
->addStmt($const)
->getNode()
;
$this->assertInstanceOf('PhpParser\Node\Stmt\ClassConst', $contract->stmts[0]);
$this->assertInstanceOf('PhpParser\Node\Stmt\ClassMethod', $contract->stmts[1]);
}
public function testDocComment() {
$node = $this->builder
->setDocComment('/** Test */')
->getNode();
$this->assertEquals(new Stmt\Interface_('Contract', array(), array(
'comments' => array(new Comment\Doc('/** Test */'))
)), $node);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Unexpected node of type "Stmt_PropertyProperty"
*/
public function testInvalidStmtError() {
$this->builder->addStmt(new Stmt\PropertyProperty('invalid'));
}
public function testFullFunctional() {
$const = new Stmt\ClassConst(array(
new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458))
));
$method = new Stmt\ClassMethod('doSomething');
$contract = $this->builder
->addStmt($method)
->addStmt($const)
->getNode()
;
eval($this->dump($contract));
$this->assertTrue(interface_exists('Contract', false));
}
}

View file

@ -0,0 +1,163 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Expr\Print_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
class MethodTest extends \PHPUnit_Framework_TestCase
{
public function createMethodBuilder($name) {
return new Method($name);
}
public function testModifiers() {
$node = $this->createMethodBuilder('test')
->makePublic()
->makeAbstract()
->makeStatic()
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'flags' => Stmt\Class_::MODIFIER_PUBLIC
| Stmt\Class_::MODIFIER_ABSTRACT
| Stmt\Class_::MODIFIER_STATIC,
'stmts' => null,
)),
$node
);
$node = $this->createMethodBuilder('test')
->makeProtected()
->makeFinal()
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'flags' => Stmt\Class_::MODIFIER_PROTECTED
| Stmt\Class_::MODIFIER_FINAL
)),
$node
);
$node = $this->createMethodBuilder('test')
->makePrivate()
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'type' => Stmt\Class_::MODIFIER_PRIVATE
)),
$node
);
}
public function testReturnByRef() {
$node = $this->createMethodBuilder('test')
->makeReturnByRef()
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'byRef' => true
)),
$node
);
}
public function testParams() {
$param1 = new Node\Param('test1');
$param2 = new Node\Param('test2');
$param3 = new Node\Param('test3');
$node = $this->createMethodBuilder('test')
->addParam($param1)
->addParams(array($param2, $param3))
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'params' => array($param1, $param2, $param3)
)),
$node
);
}
public function testStmts() {
$stmt1 = new Print_(new String_('test1'));
$stmt2 = new Print_(new String_('test2'));
$stmt3 = new Print_(new String_('test3'));
$node = $this->createMethodBuilder('test')
->addStmt($stmt1)
->addStmts(array($stmt2, $stmt3))
->getNode()
;
$this->assertEquals(
new Stmt\ClassMethod('test', array(
'stmts' => array($stmt1, $stmt2, $stmt3)
)),
$node
);
}
public function testDocComment() {
$node = $this->createMethodBuilder('test')
->setDocComment('/** Test */')
->getNode();
$this->assertEquals(new Stmt\ClassMethod('test', array(), array(
'comments' => array(new Comment\Doc('/** Test */'))
)), $node);
}
public function testReturnType() {
$node = $this->createMethodBuilder('test')
->setReturnType('bool')
->getNode();
$this->assertEquals(new Stmt\ClassMethod('test', array(
'returnType' => 'bool'
), array()), $node);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Cannot add statements to an abstract method
*/
public function testAddStmtToAbstractMethodError() {
$this->createMethodBuilder('test')
->makeAbstract()
->addStmt(new Print_(new String_('test')))
;
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Cannot make method with statements abstract
*/
public function testMakeMethodWithStmtsAbstractError() {
$this->createMethodBuilder('test')
->addStmt(new Print_(new String_('test')))
->makeAbstract()
;
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Expected parameter node, got "Name"
*/
public function testInvalidParamError() {
$this->createMethodBuilder('test')
->addParam(new Node\Name('foo'))
;
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class NamespaceTest extends \PHPUnit_Framework_TestCase
{
protected function createNamespaceBuilder($fqn) {
return new Namespace_($fqn);
}
public function testCreation() {
$stmt1 = new Stmt\Class_('SomeClass');
$stmt2 = new Stmt\Interface_('SomeInterface');
$stmt3 = new Stmt\Function_('someFunction');
$docComment = new Doc('/** Test */');
$expected = new Stmt\Namespace_(
new Node\Name('Name\Space'),
array($stmt1, $stmt2, $stmt3),
array('comments' => array($docComment))
);
$node = $this->createNamespaceBuilder('Name\Space')
->addStmt($stmt1)
->addStmts(array($stmt2, $stmt3))
->setDocComment($docComment)
->getNode()
;
$this->assertEquals($expected, $node);
$node = $this->createNamespaceBuilder(new Node\Name(array('Name', 'Space')))
->setDocComment($docComment)
->addStmts(array($stmt1, $stmt2))
->addStmt($stmt3)
->getNode()
;
$this->assertEquals($expected, $node);
$node = $this->createNamespaceBuilder(null)->getNode();
$this->assertNull($node->name);
$this->assertEmpty($node->stmts);
}
}

View file

@ -0,0 +1,171 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;
class ParamTest extends \PHPUnit_Framework_TestCase
{
public function createParamBuilder($name) {
return new Param($name);
}
/**
* @dataProvider provideTestDefaultValues
*/
public function testDefaultValues($value, $expectedValueNode) {
$node = $this->createParamBuilder('test')
->setDefault($value)
->getNode()
;
$this->assertEquals($expectedValueNode, $node->default);
}
public function provideTestDefaultValues() {
return array(
array(
null,
new Expr\ConstFetch(new Node\Name('null'))
),
array(
true,
new Expr\ConstFetch(new Node\Name('true'))
),
array(
false,
new Expr\ConstFetch(new Node\Name('false'))
),
array(
31415,
new Scalar\LNumber(31415)
),
array(
3.1415,
new Scalar\DNumber(3.1415)
),
array(
'Hallo World',
new Scalar\String_('Hallo World')
),
array(
array(1, 2, 3),
new Expr\Array_(array(
new Expr\ArrayItem(new Scalar\LNumber(1)),
new Expr\ArrayItem(new Scalar\LNumber(2)),
new Expr\ArrayItem(new Scalar\LNumber(3)),
))
),
array(
array('foo' => 'bar', 'bar' => 'foo'),
new Expr\Array_(array(
new Expr\ArrayItem(
new Scalar\String_('bar'),
new Scalar\String_('foo')
),
new Expr\ArrayItem(
new Scalar\String_('foo'),
new Scalar\String_('bar')
),
))
),
array(
new Scalar\MagicConst\Dir,
new Scalar\MagicConst\Dir
)
);
}
/**
* @dataProvider provideTestTypeHints
*/
public function testTypeHints($typeHint, $expectedType) {
$node = $this->createParamBuilder('test')
->setTypeHint($typeHint)
->getNode()
;
$type = $node->type;
/* Manually implement comparison to avoid __toString stupidity */
if ($expectedType instanceof Node\NullableType) {
$this->assertInstanceOf(get_class($expectedType), $type);
$expectedType = $expectedType->type;
$type = $type->type;
}
if ($expectedType instanceof Node\Name) {
$this->assertInstanceOf(get_class($expectedType), $type);
$this->assertEquals($expectedType, $type);
} else {
$this->assertSame($expectedType, $type);
}
}
public function provideTestTypeHints() {
return array(
array('array', 'array'),
array('callable', 'callable'),
array('bool', 'bool'),
array('int', 'int'),
array('float', 'float'),
array('string', 'string'),
array('iterable', 'iterable'),
array('object', 'object'),
array('Array', 'array'),
array('CALLABLE', 'callable'),
array('Some\Class', new Node\Name('Some\Class')),
array('\Foo', new Node\Name\FullyQualified('Foo')),
array('self', new Node\Name('self')),
array('?array', new Node\NullableType('array')),
array('?Some\Class', new Node\NullableType(new Node\Name('Some\Class'))),
array(new Node\Name('Some\Class'), new Node\Name('Some\Class')),
array(new Node\NullableType('int'), new Node\NullableType('int')),
array(
new Node\NullableType(new Node\Name('Some\Class')),
new Node\NullableType(new Node\Name('Some\Class'))
),
);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Parameter type cannot be void
*/
public function testVoidTypeError() {
$this->createParamBuilder('test')->setTypeHint('void');
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Type must be a string, or an instance of Name or NullableType
*/
public function testInvalidTypeError() {
$this->createParamBuilder('test')->setTypeHint(new \stdClass);
}
public function testByRef() {
$node = $this->createParamBuilder('test')
->makeByRef()
->getNode()
;
$this->assertEquals(
new Node\Param('test', null, null, true),
$node
);
}
public function testVariadic() {
$node = $this->createParamBuilder('test')
->makeVariadic()
->getNode()
;
$this->assertEquals(
new Node\Param('test', null, null, false, true),
$node
);
}
}

View file

@ -0,0 +1,147 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
class PropertyTest extends \PHPUnit_Framework_TestCase
{
public function createPropertyBuilder($name) {
return new Property($name);
}
public function testModifiers() {
$node = $this->createPropertyBuilder('test')
->makePrivate()
->makeStatic()
->getNode()
;
$this->assertEquals(
new Stmt\Property(
Stmt\Class_::MODIFIER_PRIVATE
| Stmt\Class_::MODIFIER_STATIC,
array(
new Stmt\PropertyProperty('test')
)
),
$node
);
$node = $this->createPropertyBuilder('test')
->makeProtected()
->getNode()
;
$this->assertEquals(
new Stmt\Property(
Stmt\Class_::MODIFIER_PROTECTED,
array(
new Stmt\PropertyProperty('test')
)
),
$node
);
$node = $this->createPropertyBuilder('test')
->makePublic()
->getNode()
;
$this->assertEquals(
new Stmt\Property(
Stmt\Class_::MODIFIER_PUBLIC,
array(
new Stmt\PropertyProperty('test')
)
),
$node
);
}
public function testDocComment() {
$node = $this->createPropertyBuilder('test')
->setDocComment('/** Test */')
->getNode();
$this->assertEquals(new Stmt\Property(
Stmt\Class_::MODIFIER_PUBLIC,
array(
new Stmt\PropertyProperty('test')
),
array(
'comments' => array(new Comment\Doc('/** Test */'))
)
), $node);
}
/**
* @dataProvider provideTestDefaultValues
*/
public function testDefaultValues($value, $expectedValueNode) {
$node = $this->createPropertyBuilder('test')
->setDefault($value)
->getNode()
;
$this->assertEquals($expectedValueNode, $node->props[0]->default);
}
public function provideTestDefaultValues() {
return array(
array(
null,
new Expr\ConstFetch(new Name('null'))
),
array(
true,
new Expr\ConstFetch(new Name('true'))
),
array(
false,
new Expr\ConstFetch(new Name('false'))
),
array(
31415,
new Scalar\LNumber(31415)
),
array(
3.1415,
new Scalar\DNumber(3.1415)
),
array(
'Hallo World',
new Scalar\String_('Hallo World')
),
array(
array(1, 2, 3),
new Expr\Array_(array(
new Expr\ArrayItem(new Scalar\LNumber(1)),
new Expr\ArrayItem(new Scalar\LNumber(2)),
new Expr\ArrayItem(new Scalar\LNumber(3)),
))
),
array(
array('foo' => 'bar', 'bar' => 'foo'),
new Expr\Array_(array(
new Expr\ArrayItem(
new Scalar\String_('bar'),
new Scalar\String_('foo')
),
new Expr\ArrayItem(
new Scalar\String_('foo'),
new Scalar\String_('bar')
),
))
),
array(
new Scalar\MagicConst\Dir,
new Scalar\MagicConst\Dir
)
);
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace PhpParser\Builder;
use PhpParser\Comment;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class TraitTest extends \PHPUnit_Framework_TestCase
{
protected function createTraitBuilder($class) {
return new Trait_($class);
}
public function testStmtAddition() {
$method1 = new Stmt\ClassMethod('test1');
$method2 = new Stmt\ClassMethod('test2');
$method3 = new Stmt\ClassMethod('test3');
$prop = new Stmt\Property(Stmt\Class_::MODIFIER_PUBLIC, array(
new Stmt\PropertyProperty('test')
));
$use = new Stmt\TraitUse([new Name('OtherTrait')]);
$trait = $this->createTraitBuilder('TestTrait')
->setDocComment('/** Nice trait */')
->addStmt($method1)
->addStmts([$method2, $method3])
->addStmt($prop)
->addStmt($use)
->getNode();
$this->assertEquals(new Stmt\Trait_('TestTrait', [
'stmts' => [$use, $prop, $method1, $method2, $method3]
], [
'comments' => [
new Comment\Doc('/** Nice trait */')
]
]), $trait);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Unexpected node of type "Stmt_Echo"
*/
public function testInvalidStmtError() {
$this->createTraitBuilder('Test')
->addStmt(new Stmt\Echo_(array()))
;
}
}

View file

@ -0,0 +1,35 @@
<?php
use PhpParser\Builder;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class UseTest extends \PHPUnit_Framework_TestCase
{
protected function createUseBuilder($name, $type = Stmt\Use_::TYPE_NORMAL) {
return new Builder\Use_($name, $type);
}
public function testCreation() {
$node = $this->createUseBuilder('Foo\Bar')->getNode();
$this->assertEquals(new Stmt\Use_(array(
new Stmt\UseUse(new Name('Foo\Bar'), 'Bar')
)), $node);
$node = $this->createUseBuilder(new Name('Foo\Bar'))->as('XYZ')->getNode();
$this->assertEquals(new Stmt\Use_(array(
new Stmt\UseUse(new Name('Foo\Bar'), 'XYZ')
)), $node);
$node = $this->createUseBuilder('foo\bar', Stmt\Use_::TYPE_FUNCTION)->as('foo')->getNode();
$this->assertEquals(new Stmt\Use_(array(
new Stmt\UseUse(new Name('foo\bar'), 'foo')
), Stmt\Use_::TYPE_FUNCTION), $node);
}
public function testNonExistingMethod() {
$this->setExpectedException('LogicException', 'Method "foo" does not exist');
$builder = $this->createUseBuilder('Test');
$builder->foo();
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace PhpParser;
use PhpParser\Node\Expr;
class BuilderFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideTestFactory
*/
public function testFactory($methodName, $className) {
$factory = new BuilderFactory;
$this->assertInstanceOf($className, $factory->$methodName('test'));
}
public function provideTestFactory() {
return array(
array('namespace', 'PhpParser\Builder\Namespace_'),
array('class', 'PhpParser\Builder\Class_'),
array('interface', 'PhpParser\Builder\Interface_'),
array('trait', 'PhpParser\Builder\Trait_'),
array('method', 'PhpParser\Builder\Method'),
array('function', 'PhpParser\Builder\Function_'),
array('property', 'PhpParser\Builder\Property'),
array('param', 'PhpParser\Builder\Param'),
array('use', 'PhpParser\Builder\Use_'),
);
}
public function testNonExistingMethod() {
$this->setExpectedException('LogicException', 'Method "foo" does not exist');
$factory = new BuilderFactory();
$factory->foo();
}
public function testIntegration() {
$factory = new BuilderFactory;
$node = $factory->namespace('Name\Space')
->addStmt($factory->use('Foo\Bar\SomeOtherClass'))
->addStmt($factory->use('Foo\Bar')->as('A'))
->addStmt($factory
->class('SomeClass')
->extend('SomeOtherClass')
->implement('A\Few', '\Interfaces')
->makeAbstract()
->addStmt($factory->method('firstMethod'))
->addStmt($factory->method('someMethod')
->makePublic()
->makeAbstract()
->addParam($factory->param('someParam')->setTypeHint('SomeClass'))
->setDocComment('/**
* This method does something.
*
* @param SomeClass And takes a parameter
*/'))
->addStmt($factory->method('anotherMethod')
->makeProtected()
->addParam($factory->param('someParam')->setDefault('test'))
->addStmt(new Expr\Print_(new Expr\Variable('someParam'))))
->addStmt($factory->property('someProperty')->makeProtected())
->addStmt($factory->property('anotherProperty')
->makePrivate()
->setDefault(array(1, 2, 3))))
->getNode()
;
$expected = <<<'EOC'
<?php
namespace Name\Space;
use Foo\Bar\SomeOtherClass;
use Foo\Bar as A;
abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
{
protected $someProperty;
private $anotherProperty = array(1, 2, 3);
function firstMethod()
{
}
/**
* This method does something.
*
* @param SomeClass And takes a parameter
*/
public abstract function someMethod(SomeClass $someParam);
protected function anotherMethod($someParam = 'test')
{
print $someParam;
}
}
EOC;
$stmts = array($node);
$prettyPrinter = new PrettyPrinter\Standard();
$generated = $prettyPrinter->prettyPrintFile($stmts);
$this->assertEquals(
str_replace("\r\n", "\n", $expected),
str_replace("\r\n", "\n", $generated)
);
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace PhpParser;
require_once __DIR__ . '/CodeTestAbstract.php';
class CodeParsingTest extends CodeTestAbstract
{
/**
* @dataProvider provideTestParse
*/
public function testParse($name, $code, $expected, $modeLine) {
if (null !== $modeLine) {
$modes = array_fill_keys(explode(',', $modeLine), true);
} else {
$modes = [];
}
$lexer = new Lexer\Emulative(array('usedAttributes' => array(
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
)));
$parser5 = new Parser\Php5($lexer);
$parser7 = new Parser\Php7($lexer);
$dumpPositions = isset($modes['positions']);
$output5 = $this->getParseOutput($parser5, $code, $dumpPositions);
$output7 = $this->getParseOutput($parser7, $code, $dumpPositions);
if (isset($modes['php5'])) {
$this->assertSame($expected, $output5, $name);
$this->assertNotSame($expected, $output7, $name);
} else if (isset($modes['php7'])) {
$this->assertNotSame($expected, $output5, $name);
$this->assertSame($expected, $output7, $name);
} else {
$this->assertSame($expected, $output5, $name);
$this->assertSame($expected, $output7, $name);
}
}
private function getParseOutput(Parser $parser, $code, $dumpPositions) {
$errors = new ErrorHandler\Collecting;
$stmts = $parser->parse($code, $errors);
$output = '';
foreach ($errors->getErrors() as $error) {
$output .= $this->formatErrorMessage($error, $code) . "\n";
}
if (null !== $stmts) {
$dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]);
$output .= $dumper->dump($stmts, $code);
}
return canonicalize($output);
}
public function provideTestParse() {
return $this->getTests(__DIR__ . '/../code/parser', 'test');
}
private function formatErrorMessage(Error $e, $code) {
if ($e->hasColumnInfo()) {
return $e->getMessageWithColumnInfo($code);
} else {
return $e->getMessage();
}
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace PhpParser;
abstract class CodeTestAbstract extends \PHPUnit_Framework_TestCase
{
protected function getTests($directory, $fileExtension) {
$directory = realpath($directory);
$it = new \RecursiveDirectoryIterator($directory);
$it = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::LEAVES_ONLY);
$it = new \RegexIterator($it, '(\.' . preg_quote($fileExtension) . '$)');
$tests = array();
foreach ($it as $file) {
$fileName = $file->getPathname();
$fileContents = file_get_contents($fileName);
$fileContents = canonicalize($fileContents);
// evaluate @@{expr}@@ expressions
$fileContents = preg_replace_callback(
'/@@\{(.*?)\}@@/',
function($matches) {
return eval('return ' . $matches[1] . ';');
},
$fileContents
);
// parse sections
$parts = preg_split("/\n-----(?:\n|$)/", $fileContents);
// first part is the name
$name = array_shift($parts) . ' (' . $fileName . ')';
$shortName = ltrim(str_replace($directory, '', $fileName), '/\\');
// multiple sections possible with always two forming a pair
$chunks = array_chunk($parts, 2);
foreach ($chunks as $i => $chunk) {
$dataSetName = $shortName . (count($chunks) > 1 ? '#' . $i : '');
list($expected, $mode) = $this->extractMode($chunk[1]);
$tests[$dataSetName] = array($name, $chunk[0], $expected, $mode);
}
}
return $tests;
}
private function extractMode($expected) {
$firstNewLine = strpos($expected, "\n");
if (false === $firstNewLine) {
$firstNewLine = strlen($expected);
}
$firstLine = substr($expected, 0, $firstNewLine);
if (0 !== strpos($firstLine, '!!')) {
return [$expected, null];
}
$expected = (string) substr($expected, $firstNewLine + 1);
return [$expected, substr($firstLine, 2)];
}
}

View file

@ -0,0 +1,73 @@
<?php
namespace PhpParser;
class CommentTest extends \PHPUnit_Framework_TestCase
{
public function testGetSet() {
$comment = new Comment('/* Some comment */', 1, 10);
$this->assertSame('/* Some comment */', $comment->getText());
$this->assertSame('/* Some comment */', (string) $comment);
$this->assertSame(1, $comment->getLine());
$this->assertSame(10, $comment->getFilePos());
}
/**
* @dataProvider provideTestReformatting
*/
public function testReformatting($commentText, $reformattedText) {
$comment = new Comment($commentText);
$this->assertSame($reformattedText, $comment->getReformattedText());
}
public function provideTestReformatting() {
return array(
array('// Some text' . "\n", '// Some text'),
array('/* Some text */', '/* Some text */'),
array(
'/**
* Some text.
* Some more text.
*/',
'/**
* Some text.
* Some more text.
*/'
),
array(
'/*
Some text.
Some more text.
*/',
'/*
Some text.
Some more text.
*/'
),
array(
'/* Some text.
More text.
Even more text. */',
'/* Some text.
More text.
Even more text. */'
),
array(
'/* Some text.
More text.
Indented text. */',
'/* Some text.
More text.
Indented text. */',
),
// invalid comment -> no reformatting
array(
'hallo
world',
'hallo
world',
),
);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace PhpParser\ErrorHandler;
use PhpParser\Error;
class CollectingTest extends \PHPUnit_Framework_TestCase {
public function testHandleError() {
$errorHandler = new Collecting();
$this->assertFalse($errorHandler->hasErrors());
$this->assertEmpty($errorHandler->getErrors());
$errorHandler->handleError($e1 = new Error('Test 1'));
$errorHandler->handleError($e2 = new Error('Test 2'));
$this->assertTrue($errorHandler->hasErrors());
$this->assertSame([$e1, $e2], $errorHandler->getErrors());
$errorHandler->clearErrors();
$this->assertFalse($errorHandler->hasErrors());
$this->assertEmpty($errorHandler->getErrors());
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace PhpParser\ErrorHandler;
use PhpParser\Error;
class ThrowingTest extends \PHPUnit_Framework_TestCase {
/**
* @expectedException \PhpParser\Error
* @expectedExceptionMessage Test
*/
public function testHandleError() {
$errorHandler = new Throwing();
$errorHandler->handleError(new Error('Test'));
}
}

View file

@ -0,0 +1,106 @@
<?php
namespace PhpParser;
class ErrorTest extends \PHPUnit_Framework_TestCase
{
public function testConstruct() {
$attributes = array(
'startLine' => 10,
'endLine' => 11,
);
$error = new Error('Some error', $attributes);
$this->assertSame('Some error', $error->getRawMessage());
$this->assertSame($attributes, $error->getAttributes());
$this->assertSame(10, $error->getStartLine());
$this->assertSame(11, $error->getEndLine());
$this->assertSame('Some error on line 10', $error->getMessage());
return $error;
}
/**
* @depends testConstruct
*/
public function testSetMessageAndLine(Error $error) {
$error->setRawMessage('Some other error');
$this->assertSame('Some other error', $error->getRawMessage());
$error->setStartLine(15);
$this->assertSame(15, $error->getStartLine());
$this->assertSame('Some other error on line 15', $error->getMessage());
}
public function testUnknownLine() {
$error = new Error('Some error');
$this->assertSame(-1, $error->getStartLine());
$this->assertSame(-1, $error->getEndLine());
$this->assertSame('Some error on unknown line', $error->getMessage());
}
/** @dataProvider provideTestColumnInfo */
public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn) {
$error = new Error('Some error', array(
'startFilePos' => $startPos,
'endFilePos' => $endPos,
));
$this->assertSame(true, $error->hasColumnInfo());
$this->assertSame($startColumn, $error->getStartColumn($code));
$this->assertSame($endColumn, $error->getEndColumn($code));
}
public function provideTestColumnInfo() {
return array(
// Error at "bar"
array("<?php foo bar baz", 10, 12, 11, 13),
array("<?php\nfoo bar baz", 10, 12, 5, 7),
array("<?php foo\nbar baz", 10, 12, 1, 3),
array("<?php foo bar\nbaz", 10, 12, 11, 13),
array("<?php\r\nfoo bar baz", 11, 13, 5, 7),
// Error at "baz"
array("<?php foo bar baz", 14, 16, 15, 17),
array("<?php foo bar\nbaz", 14, 16, 1, 3),
// Error at string literal
array("<?php foo 'bar\nbaz' xyz", 10, 18, 11, 4),
array("<?php\nfoo 'bar\nbaz' xyz", 10, 18, 5, 4),
array("<?php foo\n'\nbarbaz\n'\nxyz", 10, 19, 1, 1),
// Error over full string
array("<?php", 0, 4, 1, 5),
array("<?\nphp", 0, 5, 1, 3),
);
}
public function testNoColumnInfo() {
$error = new Error('Some error', 3);
$this->assertSame(false, $error->hasColumnInfo());
try {
$error->getStartColumn('');
$this->fail('Expected RuntimeException');
} catch (\RuntimeException $e) {
$this->assertSame('Error does not have column information', $e->getMessage());
}
try {
$error->getEndColumn('');
$this->fail('Expected RuntimeException');
} catch (\RuntimeException $e) {
$this->assertSame('Error does not have column information', $e->getMessage());
}
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Invalid position information
*/
public function testInvalidPosInfo() {
$error = new Error('Some error', array(
'startFilePos' => 10,
'endFilePos' => 11,
));
$error->getStartColumn('code');
}
}

View file

@ -0,0 +1,133 @@
<?php
namespace PhpParser\Lexer;
use PhpParser\LexerTest;
use PhpParser\Parser\Tokens;
require_once __DIR__ . '/../LexerTest.php';
class EmulativeTest extends LexerTest
{
protected function getLexer(array $options = array()) {
return new Emulative($options);
}
/**
* @dataProvider provideTestReplaceKeywords
*/
public function testReplaceKeywords($keyword, $expectedToken) {
$lexer = $this->getLexer();
$lexer->startLexing('<?php ' . $keyword);
$this->assertSame($expectedToken, $lexer->getNextToken());
$this->assertSame(0, $lexer->getNextToken());
}
/**
* @dataProvider provideTestReplaceKeywords
*/
public function testNoReplaceKeywordsAfterObjectOperator($keyword) {
$lexer = $this->getLexer();
$lexer->startLexing('<?php ->' . $keyword);
$this->assertSame(Tokens::T_OBJECT_OPERATOR, $lexer->getNextToken());
$this->assertSame(Tokens::T_STRING, $lexer->getNextToken());
$this->assertSame(0, $lexer->getNextToken());
}
public function provideTestReplaceKeywords() {
return array(
// PHP 5.5
array('finally', Tokens::T_FINALLY),
array('yield', Tokens::T_YIELD),
// PHP 5.4
array('callable', Tokens::T_CALLABLE),
array('insteadof', Tokens::T_INSTEADOF),
array('trait', Tokens::T_TRAIT),
array('__TRAIT__', Tokens::T_TRAIT_C),
// PHP 5.3
array('__DIR__', Tokens::T_DIR),
array('goto', Tokens::T_GOTO),
array('namespace', Tokens::T_NAMESPACE),
array('__NAMESPACE__', Tokens::T_NS_C),
);
}
/**
* @dataProvider provideTestLexNewFeatures
*/
public function testLexNewFeatures($code, array $expectedTokens) {
$lexer = $this->getLexer();
$lexer->startLexing('<?php ' . $code);
foreach ($expectedTokens as $expectedToken) {
list($expectedTokenType, $expectedTokenText) = $expectedToken;
$this->assertSame($expectedTokenType, $lexer->getNextToken($text));
$this->assertSame($expectedTokenText, $text);
}
$this->assertSame(0, $lexer->getNextToken());
}
/**
* @dataProvider provideTestLexNewFeatures
*/
public function testLeaveStuffAloneInStrings($code) {
$stringifiedToken = '"' . addcslashes($code, '"\\') . '"';
$lexer = $this->getLexer();
$lexer->startLexing('<?php ' . $stringifiedToken);
$this->assertSame(Tokens::T_CONSTANT_ENCAPSED_STRING, $lexer->getNextToken($text));
$this->assertSame($stringifiedToken, $text);
$this->assertSame(0, $lexer->getNextToken());
}
public function provideTestLexNewFeatures() {
return array(
array('yield from', array(
array(Tokens::T_YIELD_FROM, 'yield from'),
)),
array("yield\r\nfrom", array(
array(Tokens::T_YIELD_FROM, "yield\r\nfrom"),
)),
array('...', array(
array(Tokens::T_ELLIPSIS, '...'),
)),
array('**', array(
array(Tokens::T_POW, '**'),
)),
array('**=', array(
array(Tokens::T_POW_EQUAL, '**='),
)),
array('??', array(
array(Tokens::T_COALESCE, '??'),
)),
array('<=>', array(
array(Tokens::T_SPACESHIP, '<=>'),
)),
array('0b1010110', array(
array(Tokens::T_LNUMBER, '0b1010110'),
)),
array('0b1011010101001010110101010010101011010101010101101011001110111100', array(
array(Tokens::T_DNUMBER, '0b1011010101001010110101010010101011010101010101101011001110111100'),
)),
array('\\', array(
array(Tokens::T_NS_SEPARATOR, '\\'),
)),
array("<<<'NOWDOC'\nNOWDOC;\n", array(
array(Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"),
array(Tokens::T_END_HEREDOC, 'NOWDOC'),
array(ord(';'), ';'),
)),
array("<<<'NOWDOC'\nFoobar\nNOWDOC;\n", array(
array(Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"),
array(Tokens::T_ENCAPSED_AND_WHITESPACE, "Foobar\n"),
array(Tokens::T_END_HEREDOC, 'NOWDOC'),
array(ord(';'), ';'),
)),
);
}
}

View file

@ -0,0 +1,265 @@
<?php
namespace PhpParser;
use PhpParser\Parser\Tokens;
class LexerTest extends \PHPUnit_Framework_TestCase
{
/* To allow overwriting in parent class */
protected function getLexer(array $options = array()) {
return new Lexer($options);
}
/**
* @dataProvider provideTestError
*/
public function testError($code, $messages) {
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM does not throw warnings from token_get_all()');
}
$errorHandler = new ErrorHandler\Collecting();
$lexer = $this->getLexer(['usedAttributes' => [
'comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'
]]);
$lexer->startLexing($code, $errorHandler);
$errors = $errorHandler->getErrors();
$this->assertSame(count($messages), count($errors));
for ($i = 0; $i < count($messages); $i++) {
$this->assertSame($messages[$i], $errors[$i]->getMessageWithColumnInfo($code));
}
}
public function provideTestError() {
return array(
array("<?php /*", array("Unterminated comment from 1:7 to 1:9")),
array("<?php \1", array("Unexpected character \"\1\" (ASCII 1) from 1:7 to 1:7")),
array("<?php \0", array("Unexpected null byte from 1:7 to 1:7")),
// Error with potentially emulated token
array("<?php ?? \0", array("Unexpected null byte from 1:10 to 1:10")),
array("<?php\n\0\1 foo /* bar", array(
"Unexpected null byte from 2:1 to 2:1",
"Unexpected character \"\1\" (ASCII 1) from 2:2 to 2:2",
"Unterminated comment from 2:8 to 2:14"
)),
);
}
/**
* @dataProvider provideTestLex
*/
public function testLex($code, $options, $tokens) {
$lexer = $this->getLexer($options);
$lexer->startLexing($code);
while ($id = $lexer->getNextToken($value, $startAttributes, $endAttributes)) {
$token = array_shift($tokens);
$this->assertSame($token[0], $id);
$this->assertSame($token[1], $value);
$this->assertEquals($token[2], $startAttributes);
$this->assertEquals($token[3], $endAttributes);
}
}
public function provideTestLex() {
return array(
// tests conversion of closing PHP tag and drop of whitespace and opening tags
array(
'<?php tokens ?>plaintext',
array(),
array(
array(
Tokens::T_STRING, 'tokens',
array('startLine' => 1), array('endLine' => 1)
),
array(
ord(';'), '?>',
array('startLine' => 1), array('endLine' => 1)
),
array(
Tokens::T_INLINE_HTML, 'plaintext',
array('startLine' => 1, 'hasLeadingNewline' => false),
array('endLine' => 1)
),
)
),
// tests line numbers
array(
'<?php' . "\n" . '$ token /** doc' . "\n" . 'comment */ $',
array(),
array(
array(
ord('$'), '$',
array('startLine' => 2), array('endLine' => 2)
),
array(
Tokens::T_STRING, 'token',
array('startLine' => 2), array('endLine' => 2)
),
array(
ord('$'), '$',
array(
'startLine' => 3,
'comments' => array(
new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14),
)
),
array('endLine' => 3)
),
)
),
// tests comment extraction
array(
'<?php /* comment */ // comment' . "\n" . '/** docComment 1 *//** docComment 2 */ token',
array(),
array(
array(
Tokens::T_STRING, 'token',
array(
'startLine' => 2,
'comments' => array(
new Comment('/* comment */', 1, 6),
new Comment('// comment' . "\n", 1, 20),
new Comment\Doc('/** docComment 1 */', 2, 31),
new Comment\Doc('/** docComment 2 */', 2, 50),
),
),
array('endLine' => 2)
),
)
),
// tests differing start and end line
array(
'<?php "foo' . "\n" . 'bar"',
array(),
array(
array(
Tokens::T_CONSTANT_ENCAPSED_STRING, '"foo' . "\n" . 'bar"',
array('startLine' => 1), array('endLine' => 2)
),
)
),
// tests exact file offsets
array(
'<?php "a";' . "\n" . '// foo' . "\n" . '"b";',
array('usedAttributes' => array('startFilePos', 'endFilePos')),
array(
array(
Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
array('startFilePos' => 6), array('endFilePos' => 8)
),
array(
ord(';'), ';',
array('startFilePos' => 9), array('endFilePos' => 9)
),
array(
Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
array('startFilePos' => 18), array('endFilePos' => 20)
),
array(
ord(';'), ';',
array('startFilePos' => 21), array('endFilePos' => 21)
),
)
),
// tests token offsets
array(
'<?php "a";' . "\n" . '// foo' . "\n" . '"b";',
array('usedAttributes' => array('startTokenPos', 'endTokenPos')),
array(
array(
Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
array('startTokenPos' => 1), array('endTokenPos' => 1)
),
array(
ord(';'), ';',
array('startTokenPos' => 2), array('endTokenPos' => 2)
),
array(
Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
array('startTokenPos' => 5), array('endTokenPos' => 5)
),
array(
ord(';'), ';',
array('startTokenPos' => 6), array('endTokenPos' => 6)
),
)
),
// tests all attributes being disabled
array(
'<?php /* foo */ $bar;',
array('usedAttributes' => array()),
array(
array(
Tokens::T_VARIABLE, '$bar',
array(), array()
),
array(
ord(';'), ';',
array(), array()
)
)
),
// tests no tokens
array(
'',
array(),
array()
),
);
}
/**
* @dataProvider provideTestHaltCompiler
*/
public function testHandleHaltCompiler($code, $remaining) {
$lexer = $this->getLexer();
$lexer->startLexing($code);
while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken());
$this->assertSame($remaining, $lexer->handleHaltCompiler());
$this->assertSame(0, $lexer->getNextToken());
}
public function provideTestHaltCompiler() {
return array(
array('<?php ... __halt_compiler();Remaining Text', 'Remaining Text'),
array('<?php ... __halt_compiler ( ) ;Remaining Text', 'Remaining Text'),
array('<?php ... __halt_compiler() ?>Remaining Text', 'Remaining Text'),
//array('<?php ... __halt_compiler();' . "\0", "\0"),
//array('<?php ... __halt_compiler /* */ ( ) ;Remaining Text', 'Remaining Text'),
);
}
/**
* @expectedException \PhpParser\Error
* @expectedExceptionMessage __HALT_COMPILER must be followed by "();"
*/
public function testHandleHaltCompilerError() {
$lexer = $this->getLexer();
$lexer->startLexing('<?php ... __halt_compiler invalid ();');
while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken());
$lexer->handleHaltCompiler();
}
public function testGetTokens() {
$code = '<?php "a";' . "\n" . '// foo' . "\n" . '"b";';
$expectedTokens = array(
array(T_OPEN_TAG, '<?php ', 1),
array(T_CONSTANT_ENCAPSED_STRING, '"a"', 1),
';',
array(T_WHITESPACE, "\n", 1),
array(T_COMMENT, '// foo' . "\n", 2),
array(T_CONSTANT_ENCAPSED_STRING, '"b"', 3),
';',
);
$lexer = $this->getLexer();
$lexer->startLexing($code);
$this->assertSame($expectedTokens, $lexer->getTokens());
}
}

View file

@ -0,0 +1,134 @@
<?php
namespace PhpParser\Node;
class NameTest extends \PHPUnit_Framework_TestCase
{
public function testConstruct() {
$name = new Name(array('foo', 'bar'));
$this->assertSame(array('foo', 'bar'), $name->parts);
$name = new Name('foo\bar');
$this->assertSame(array('foo', 'bar'), $name->parts);
$name = new Name($name);
$this->assertSame(array('foo', 'bar'), $name->parts);
}
public function testGet() {
$name = new Name('foo');
$this->assertSame('foo', $name->getFirst());
$this->assertSame('foo', $name->getLast());
$name = new Name('foo\bar');
$this->assertSame('foo', $name->getFirst());
$this->assertSame('bar', $name->getLast());
}
public function testToString() {
$name = new Name('foo\bar');
$this->assertSame('foo\bar', (string) $name);
$this->assertSame('foo\bar', $name->toString());
}
public function testSlice() {
$name = new Name('foo\bar\baz');
$this->assertEquals(new Name('foo\bar\baz'), $name->slice(0));
$this->assertEquals(new Name('bar\baz'), $name->slice(1));
$this->assertNull($name->slice(3));
$this->assertEquals(new Name('foo\bar\baz'), $name->slice(-3));
$this->assertEquals(new Name('bar\baz'), $name->slice(-2));
$this->assertEquals(new Name('foo\bar'), $name->slice(0, -1));
$this->assertNull($name->slice(0, -3));
$this->assertEquals(new Name('bar'), $name->slice(1, -1));
$this->assertNull($name->slice(1, -2));
$this->assertEquals(new Name('bar'), $name->slice(-2, 1));
$this->assertEquals(new Name('bar'), $name->slice(-2, -1));
$this->assertNull($name->slice(-2, -2));
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Offset 4 is out of bounds
*/
public function testSliceOffsetTooLarge() {
(new Name('foo\bar\baz'))->slice(4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Offset -4 is out of bounds
*/
public function testSliceOffsetTooSmall() {
(new Name('foo\bar\baz'))->slice(-4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Length 4 is out of bounds
*/
public function testSliceLengthTooLarge() {
(new Name('foo\bar\baz'))->slice(0, 4);
}
/**
* @expectedException \OutOfBoundsException
* @expectedExceptionMessage Length -4 is out of bounds
*/
public function testSliceLengthTooSmall() {
(new Name('foo\bar\baz'))->slice(0, -4);
}
public function testConcat() {
$this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz'));
$this->assertEquals(
new Name\FullyQualified('foo\bar'),
Name\FullyQualified::concat(['foo'], new Name('bar'))
);
$attributes = ['foo' => 'bar'];
$this->assertEquals(
new Name\Relative('foo\bar\baz', $attributes),
Name\Relative::concat(new Name\FullyQualified('foo\bar'), 'baz', $attributes)
);
$this->assertEquals(new Name('foo'), Name::concat(null, 'foo'));
$this->assertEquals(new Name('foo'), Name::concat('foo', null));
$this->assertNull(Name::concat(null, null));
}
public function testIs() {
$name = new Name('foo');
$this->assertTrue ($name->isUnqualified());
$this->assertFalse($name->isQualified());
$this->assertFalse($name->isFullyQualified());
$this->assertFalse($name->isRelative());
$name = new Name('foo\bar');
$this->assertFalse($name->isUnqualified());
$this->assertTrue ($name->isQualified());
$this->assertFalse($name->isFullyQualified());
$this->assertFalse($name->isRelative());
$name = new Name\FullyQualified('foo');
$this->assertFalse($name->isUnqualified());
$this->assertFalse($name->isQualified());
$this->assertTrue ($name->isFullyQualified());
$this->assertFalse($name->isRelative());
$name = new Name\Relative('foo');
$this->assertFalse($name->isUnqualified());
$this->assertFalse($name->isQualified());
$this->assertFalse($name->isFullyQualified());
$this->assertTrue ($name->isRelative());
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Expected string, array of parts or Name instance
*/
public function testInvalidArg() {
Name::concat('foo', new \stdClass);
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace PhpParser\Node\Scalar;
class MagicConstTest extends \PHPUnit_Framework_TestCase {
/**
* @dataProvider provideTestGetName
*/
public function testGetName(MagicConst $magicConst, $name) {
$this->assertSame($name, $magicConst->getName());
}
public function provideTestGetName() {
return array(
array(new MagicConst\Class_, '__CLASS__'),
array(new MagicConst\Dir, '__DIR__'),
array(new MagicConst\File, '__FILE__'),
array(new MagicConst\Function_, '__FUNCTION__'),
array(new MagicConst\Line, '__LINE__'),
array(new MagicConst\Method, '__METHOD__'),
array(new MagicConst\Namespace_, '__NAMESPACE__'),
array(new MagicConst\Trait_, '__TRAIT__'),
);
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace PhpParser\Node\Scalar;
class StringTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideTestParseEscapeSequences
*/
public function testParseEscapeSequences($expected, $string, $quote) {
$this->assertSame(
$expected,
String_::parseEscapeSequences($string, $quote)
);
}
/**
* @dataProvider provideTestParse
*/
public function testCreate($expected, $string) {
$this->assertSame(
$expected,
String_::parse($string)
);
}
public function provideTestParseEscapeSequences() {
return array(
array('"', '\\"', '"'),
array('\\"', '\\"', '`'),
array('\\"\\`', '\\"\\`', null),
array("\\\$\n\r\t\f\v", '\\\\\$\n\r\t\f\v', null),
array("\x1B", '\e', null),
array(chr(255), '\xFF', null),
array(chr(255), '\377', null),
array(chr(0), '\400', null),
array("\0", '\0', null),
array('\xFF', '\\\\xFF', null),
);
}
public function provideTestParse() {
$tests = array(
array('A', '\'A\''),
array('A', 'b\'A\''),
array('A', '"A"'),
array('A', 'b"A"'),
array('\\', '\'\\\\\''),
array('\'', '\'\\\'\''),
);
foreach ($this->provideTestParseEscapeSequences() as $i => $test) {
// skip second and third tests, they aren't for double quotes
if ($i != 1 && $i != 2) {
$tests[] = array($test[0], '"' . $test[1] . '"');
}
}
return $tests;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace PhpParser\Node\Stmt;
class ClassConstTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideModifiers
*/
public function testModifiers($modifier) {
$node = new ClassConst(
array(), // invalid
constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier))
);
$this->assertTrue($node->{'is' . $modifier}());
}
public function testNoModifiers() {
$node = new ClassConst(array(), 0);
$this->assertTrue($node->isPublic());
$this->assertFalse($node->isProtected());
$this->assertFalse($node->isPrivate());
$this->assertFalse($node->isStatic());
}
public function provideModifiers() {
return array(
array('public'),
array('protected'),
array('private'),
);
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace PhpParser\Node\Stmt;
class ClassMethodTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideModifiers
*/
public function testModifiers($modifier) {
$node = new ClassMethod('foo', array(
'type' => constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier))
));
$this->assertTrue($node->{'is' . $modifier}());
}
public function testNoModifiers() {
$node = new ClassMethod('foo', array('type' => 0));
$this->assertTrue($node->isPublic());
$this->assertFalse($node->isProtected());
$this->assertFalse($node->isPrivate());
$this->assertFalse($node->isAbstract());
$this->assertFalse($node->isFinal());
$this->assertFalse($node->isStatic());
}
public function provideModifiers() {
return array(
array('public'),
array('protected'),
array('private'),
array('abstract'),
array('final'),
array('static'),
);
}
/**
* Checks that implicit public modifier detection for method is working
*
* @dataProvider implicitPublicModifiers
*
* @param integer $modifier Node type modifier
*/
public function testImplicitPublic($modifier)
{
$node = new ClassMethod('foo', array(
'type' => constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier))
));
$this->assertTrue($node->isPublic(), 'Node should be implicitly public');
}
public function implicitPublicModifiers() {
return array(
array('abstract'),
array('final'),
array('static'),
);
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace PhpParser\Node\Stmt;
class ClassTest extends \PHPUnit_Framework_TestCase
{
public function testIsAbstract() {
$class = new Class_('Foo', array('type' => Class_::MODIFIER_ABSTRACT));
$this->assertTrue($class->isAbstract());
$class = new Class_('Foo');
$this->assertFalse($class->isAbstract());
}
public function testIsFinal() {
$class = new Class_('Foo', array('type' => Class_::MODIFIER_FINAL));
$this->assertTrue($class->isFinal());
$class = new Class_('Foo');
$this->assertFalse($class->isFinal());
}
public function testGetMethods() {
$methods = array(
new ClassMethod('foo'),
new ClassMethod('bar'),
new ClassMethod('fooBar'),
);
$class = new Class_('Foo', array(
'stmts' => array(
new TraitUse(array()),
$methods[0],
new ClassConst(array()),
$methods[1],
new Property(0, array()),
$methods[2],
)
));
$this->assertSame($methods, $class->getMethods());
}
public function testGetMethod() {
$methodConstruct = new ClassMethod('__CONSTRUCT');
$methodTest = new ClassMethod('test');
$class = new Class_('Foo', array(
'stmts' => array(
new ClassConst(array()),
$methodConstruct,
new Property(0, array()),
$methodTest,
)
));
$this->assertSame($methodConstruct, $class->getMethod('__construct'));
$this->assertSame($methodTest, $class->getMethod('test'));
$this->assertNull($class->getMethod('nonExisting'));
}
public function testDeprecatedTypeNode() {
$class = new Class_('Foo', array('type' => Class_::MODIFIER_ABSTRACT));
$this->assertTrue($class->isAbstract());
$this->assertSame(Class_::MODIFIER_ABSTRACT, $class->flags);
$this->assertSame(Class_::MODIFIER_ABSTRACT, $class->type);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class InterfaceTest extends \PHPUnit_Framework_TestCase
{
public function testGetMethods() {
$methods = array(
new ClassMethod('foo'),
new ClassMethod('bar'),
);
$interface = new Class_('Foo', array(
'stmts' => array(
new Node\Stmt\ClassConst(array(new Node\Const_('C1', new Node\Scalar\String_('C1')))),
$methods[0],
new Node\Stmt\ClassConst(array(new Node\Const_('C2', new Node\Scalar\String_('C2')))),
$methods[1],
new Node\Stmt\ClassConst(array(new Node\Const_('C3', new Node\Scalar\String_('C3')))),
)
));
$this->assertSame($methods, $interface->getMethods());
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace PhpParser\Node\Stmt;
class PropertyTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideModifiers
*/
public function testModifiers($modifier) {
$node = new Property(
constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier)),
array() // invalid
);
$this->assertTrue($node->{'is' . $modifier}());
}
public function testNoModifiers() {
$node = new Property(0, array());
$this->assertTrue($node->isPublic());
$this->assertFalse($node->isProtected());
$this->assertFalse($node->isPrivate());
$this->assertFalse($node->isStatic());
}
public function testStaticImplicitlyPublic() {
$node = new Property(Class_::MODIFIER_STATIC, array());
$this->assertTrue($node->isPublic());
$this->assertFalse($node->isProtected());
$this->assertFalse($node->isPrivate());
$this->assertTrue($node->isStatic());
}
public function provideModifiers() {
return array(
array('public'),
array('protected'),
array('private'),
array('static'),
);
}
}

View file

@ -0,0 +1,274 @@
<?php
namespace PhpParser;
class DummyNode extends NodeAbstract {
public $subNode1;
public $subNode2;
public function __construct($subNode1, $subNode2, $attributes) {
parent::__construct($attributes);
$this->subNode1 = $subNode1;
$this->subNode2 = $subNode2;
}
public function getSubNodeNames() {
return array('subNode1', 'subNode2');
}
// This method is only overwritten because the node is located in an unusual namespace
public function getType() {
return 'Dummy';
}
}
class NodeAbstractTest extends \PHPUnit_Framework_TestCase
{
public function provideNodes() {
$attributes = array(
'startLine' => 10,
'comments' => array(
new Comment('// Comment' . "\n"),
new Comment\Doc('/** doc comment */'),
),
);
$node = new DummyNode('value1', 'value2', $attributes);
$node->notSubNode = 'value3';
return array(
array($attributes, $node),
);
}
/**
* @dataProvider provideNodes
*/
public function testConstruct(array $attributes, Node $node) {
$this->assertSame('Dummy', $node->getType());
$this->assertSame(array('subNode1', 'subNode2'), $node->getSubNodeNames());
$this->assertSame(10, $node->getLine());
$this->assertSame('/** doc comment */', $node->getDocComment()->getText());
$this->assertSame('value1', $node->subNode1);
$this->assertSame('value2', $node->subNode2);
$this->assertTrue(isset($node->subNode1));
$this->assertTrue(isset($node->subNode2));
$this->assertFalse(isset($node->subNode3));
$this->assertSame($attributes, $node->getAttributes());
return $node;
}
/**
* @dataProvider provideNodes
*/
public function testGetDocComment(array $attributes, Node $node) {
$this->assertSame('/** doc comment */', $node->getDocComment()->getText());
array_pop($node->getAttribute('comments')); // remove doc comment
$this->assertNull($node->getDocComment());
array_pop($node->getAttribute('comments')); // remove comment
$this->assertNull($node->getDocComment());
}
public function testSetDocComment() {
$node = new DummyNode(null, null, []);
// Add doc comment to node without comments
$docComment = new Comment\Doc('/** doc */');
$node->setDocComment($docComment);
$this->assertSame($docComment, $node->getDocComment());
// Replace it
$docComment = new Comment\Doc('/** doc 2 */');
$node->setDocComment($docComment);
$this->assertSame($docComment, $node->getDocComment());
// Add docmment to node with other comments
$c1 = new Comment('/* foo */');
$c2 = new Comment('/* bar */');
$docComment = new Comment\Doc('/** baz */');
$node->setAttribute('comments', [$c1, $c2]);
$node->setDocComment($docComment);
$this->assertSame([$c1, $c2, $docComment], $node->getAttribute('comments'));
}
/**
* @dataProvider provideNodes
*/
public function testChange(array $attributes, Node $node) {
// change of line
$node->setLine(15);
$this->assertSame(15, $node->getLine());
// direct modification
$node->subNode = 'newValue';
$this->assertSame('newValue', $node->subNode);
// indirect modification
$subNode =& $node->subNode;
$subNode = 'newNewValue';
$this->assertSame('newNewValue', $node->subNode);
// removal
unset($node->subNode);
$this->assertFalse(isset($node->subNode));
}
/**
* @dataProvider provideNodes
*/
public function testIteration(array $attributes, Node $node) {
// Iteration is simple object iteration over properties,
// not over subnodes
$i = 0;
foreach ($node as $key => $value) {
if ($i === 0) {
$this->assertSame('subNode1', $key);
$this->assertSame('value1', $value);
} else if ($i === 1) {
$this->assertSame('subNode2', $key);
$this->assertSame('value2', $value);
} else if ($i === 2) {
$this->assertSame('notSubNode', $key);
$this->assertSame('value3', $value);
} else {
throw new \Exception;
}
$i++;
}
$this->assertSame(3, $i);
}
public function testAttributes() {
/** @var $node Node */
$node = $this->getMockForAbstractClass('PhpParser\NodeAbstract');
$this->assertEmpty($node->getAttributes());
$node->setAttribute('key', 'value');
$this->assertTrue($node->hasAttribute('key'));
$this->assertSame('value', $node->getAttribute('key'));
$this->assertFalse($node->hasAttribute('doesNotExist'));
$this->assertNull($node->getAttribute('doesNotExist'));
$this->assertSame('default', $node->getAttribute('doesNotExist', 'default'));
$node->setAttribute('null', null);
$this->assertTrue($node->hasAttribute('null'));
$this->assertNull($node->getAttribute('null'));
$this->assertNull($node->getAttribute('null', 'default'));
$this->assertSame(
array(
'key' => 'value',
'null' => null,
),
$node->getAttributes()
);
}
public function testJsonSerialization() {
$code = <<<'PHP'
<?php
// comment
/** doc comment */
function functionName(&$a = 0, $b = 1.0) {
echo 'Foo';
}
PHP;
$expected = <<<'JSON'
[
{
"nodeType": "Stmt_Function",
"byRef": false,
"name": "functionName",
"params": [
{
"nodeType": "Param",
"type": null,
"byRef": true,
"variadic": false,
"name": "a",
"default": {
"nodeType": "Scalar_LNumber",
"value": 0,
"attributes": {
"startLine": 4,
"endLine": 4,
"kind": 10
}
},
"attributes": {
"startLine": 4,
"endLine": 4
}
},
{
"nodeType": "Param",
"type": null,
"byRef": false,
"variadic": false,
"name": "b",
"default": {
"nodeType": "Scalar_DNumber",
"value": 1,
"attributes": {
"startLine": 4,
"endLine": 4
}
},
"attributes": {
"startLine": 4,
"endLine": 4
}
}
],
"returnType": null,
"stmts": [
{
"nodeType": "Stmt_Echo",
"exprs": [
{
"nodeType": "Scalar_String",
"value": "Foo",
"attributes": {
"startLine": 5,
"endLine": 5,
"kind": 1
}
}
],
"attributes": {
"startLine": 5,
"endLine": 5
}
}
],
"attributes": {
"startLine": 4,
"comments": [
{
"nodeType": "Comment",
"text": "\/\/ comment\n",
"line": 2,
"filePos": 6
},
{
"nodeType": "Comment_Doc",
"text": "\/** doc comment *\/",
"line": 3,
"filePos": 17
}
],
"endLine": 6
}
}
]
JSON;
$parser = new Parser\Php7(new Lexer());
$stmts = $parser->parse(canonicalize($code));
$json = json_encode($stmts, JSON_PRETTY_PRINT);
$this->assertEquals(canonicalize($expected), canonicalize($json));
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace PhpParser;
class NodeDumperTest extends \PHPUnit_Framework_TestCase
{
private function canonicalize($string) {
return str_replace("\r\n", "\n", $string);
}
/**
* @dataProvider provideTestDump
*/
public function testDump($node, $dump) {
$dumper = new NodeDumper;
$this->assertSame($this->canonicalize($dump), $this->canonicalize($dumper->dump($node)));
}
public function provideTestDump() {
return array(
array(
array(),
'array(
)'
),
array(
array('Foo', 'Bar', 'Key' => 'FooBar'),
'array(
0: Foo
1: Bar
Key: FooBar
)'
),
array(
new Node\Name(array('Hallo', 'World')),
'Name(
parts: array(
0: Hallo
1: World
)
)'
),
array(
new Node\Expr\Array_(array(
new Node\Expr\ArrayItem(new Node\Scalar\String_('Foo'))
)),
'Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: Foo
)
byRef: false
)
)
)'
),
);
}
public function testDumpWithPositions() {
$parser = (new ParserFactory)->create(
ParserFactory::ONLY_PHP7,
new Lexer(['usedAttributes' => ['startLine', 'endLine', 'startFilePos', 'endFilePos']])
);
$dumper = new NodeDumper(['dumpPositions' => true]);
$code = "<?php\n\$a = 1;\necho \$a;";
$expected = <<<'OUT'
array(
0: Expr_Assign[2:1 - 2:6](
var: Expr_Variable[2:1 - 2:2](
name: a
)
expr: Scalar_LNumber[2:6 - 2:6](
value: 1
)
)
1: Stmt_Echo[3:1 - 3:8](
exprs: array(
0: Expr_Variable[3:6 - 3:7](
name: a
)
)
)
)
OUT;
$stmts = $parser->parse($code);
$dump = $dumper->dump($stmts, $code);
$this->assertSame($this->canonicalize($expected), $this->canonicalize($dump));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Can only dump nodes and arrays.
*/
public function testError() {
$dumper = new NodeDumper;
$dumper->dump(new \stdClass);
}
}

View file

@ -0,0 +1,267 @@
<?php
namespace PhpParser;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\String_;
class NodeTraverserTest extends \PHPUnit_Framework_TestCase
{
public function testNonModifying() {
$str1Node = new String_('Foo');
$str2Node = new String_('Bar');
$echoNode = new Node\Stmt\Echo_(array($str1Node, $str2Node));
$stmts = array($echoNode);
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(0))->method('beforeTraverse')->with($stmts);
$visitor->expects($this->at(1))->method('enterNode')->with($echoNode);
$visitor->expects($this->at(2))->method('enterNode')->with($str1Node);
$visitor->expects($this->at(3))->method('leaveNode')->with($str1Node);
$visitor->expects($this->at(4))->method('enterNode')->with($str2Node);
$visitor->expects($this->at(5))->method('leaveNode')->with($str2Node);
$visitor->expects($this->at(6))->method('leaveNode')->with($echoNode);
$visitor->expects($this->at(7))->method('afterTraverse')->with($stmts);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
}
public function testModifying() {
$str1Node = new String_('Foo');
$str2Node = new String_('Bar');
$printNode = new Expr\Print_($str1Node);
// first visitor changes the node, second verifies the change
$visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
// replace empty statements with string1 node
$visitor1->expects($this->at(0))->method('beforeTraverse')->with(array())
->will($this->returnValue(array($str1Node)));
$visitor2->expects($this->at(0))->method('beforeTraverse')->with(array($str1Node));
// replace string1 node with print node
$visitor1->expects($this->at(1))->method('enterNode')->with($str1Node)
->will($this->returnValue($printNode));
$visitor2->expects($this->at(1))->method('enterNode')->with($printNode);
// replace string1 node with string2 node
$visitor1->expects($this->at(2))->method('enterNode')->with($str1Node)
->will($this->returnValue($str2Node));
$visitor2->expects($this->at(2))->method('enterNode')->with($str2Node);
// replace string2 node with string1 node again
$visitor1->expects($this->at(3))->method('leaveNode')->with($str2Node)
->will($this->returnValue($str1Node));
$visitor2->expects($this->at(3))->method('leaveNode')->with($str1Node);
// replace print node with string1 node again
$visitor1->expects($this->at(4))->method('leaveNode')->with($printNode)
->will($this->returnValue($str1Node));
$visitor2->expects($this->at(4))->method('leaveNode')->with($str1Node);
// replace string1 node with empty statements again
$visitor1->expects($this->at(5))->method('afterTraverse')->with(array($str1Node))
->will($this->returnValue(array()));
$visitor2->expects($this->at(5))->method('afterTraverse')->with(array());
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor1);
$traverser->addVisitor($visitor2);
// as all operations are reversed we end where we start
$this->assertEquals(array(), $traverser->traverse(array()));
}
public function testRemove() {
$str1Node = new String_('Foo');
$str2Node = new String_('Bar');
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
// remove the string1 node, leave the string2 node
$visitor->expects($this->at(2))->method('leaveNode')->with($str1Node)
->will($this->returnValue(false));
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals(array($str2Node), $traverser->traverse(array($str1Node, $str2Node)));
}
public function testMerge() {
$strStart = new String_('Start');
$strMiddle = new String_('End');
$strEnd = new String_('Middle');
$strR1 = new String_('Replacement 1');
$strR2 = new String_('Replacement 2');
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
// replace strMiddle with strR1 and strR2 by merge
$visitor->expects($this->at(4))->method('leaveNode')->with($strMiddle)
->will($this->returnValue(array($strR1, $strR2)));
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals(
array($strStart, $strR1, $strR2, $strEnd),
$traverser->traverse(array($strStart, $strMiddle, $strEnd))
);
}
public function testDeepArray() {
$strNode = new String_('Foo');
$stmts = array(array(array($strNode)));
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(1))->method('enterNode')->with($strNode);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
}
public function testDontTraverseChildren() {
$strNode = new String_('str');
$printNode = new Expr\Print_($strNode);
$varNode = new Expr\Variable('foo');
$mulNode = new Expr\BinaryOp\Mul($varNode, $varNode);
$negNode = new Expr\UnaryMinus($mulNode);
$stmts = array($printNode, $negNode);
$visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor1->expects($this->at(1))->method('enterNode')->with($printNode)
->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN));
$visitor2->expects($this->at(1))->method('enterNode')->with($printNode);
$visitor1->expects($this->at(2))->method('leaveNode')->with($printNode);
$visitor2->expects($this->at(2))->method('leaveNode')->with($printNode);
$visitor1->expects($this->at(3))->method('enterNode')->with($negNode);
$visitor2->expects($this->at(3))->method('enterNode')->with($negNode);
$visitor1->expects($this->at(4))->method('enterNode')->with($mulNode);
$visitor2->expects($this->at(4))->method('enterNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN));
$visitor1->expects($this->at(5))->method('leaveNode')->with($mulNode);
$visitor2->expects($this->at(5))->method('leaveNode')->with($mulNode);
$visitor1->expects($this->at(6))->method('leaveNode')->with($negNode);
$visitor2->expects($this->at(6))->method('leaveNode')->with($negNode);
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor1);
$traverser->addVisitor($visitor2);
$this->assertEquals($stmts, $traverser->traverse($stmts));
}
public function testStopTraversal() {
$varNode1 = new Expr\Variable('a');
$varNode2 = new Expr\Variable('b');
$varNode3 = new Expr\Variable('c');
$mulNode = new Expr\BinaryOp\Mul($varNode1, $varNode2);
$printNode = new Expr\Print_($varNode3);
$stmts = [$mulNode, $printNode];
// From enterNode() with array parent
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(1))->method('enterNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(2))->method('afterTraverse');
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
// From enterNode with Node parent
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(2))->method('enterNode')->with($varNode1)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(3))->method('afterTraverse');
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
// From leaveNode with Node parent
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(3))->method('leaveNode')->with($varNode1)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(4))->method('afterTraverse');
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
// From leaveNode with array parent
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(6))->method('leaveNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(7))->method('afterTraverse');
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals($stmts, $traverser->traverse($stmts));
// Check that pending array modifications are still carried out
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->expects($this->at(6))->method('leaveNode')->with($mulNode)
->will($this->returnValue(NodeTraverser::REMOVE_NODE));
$visitor->expects($this->at(7))->method('enterNode')->with($printNode)
->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL));
$visitor->expects($this->at(8))->method('afterTraverse');
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor);
$this->assertEquals([$printNode], $traverser->traverse($stmts));
}
public function testRemovingVisitor() {
$visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor3 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$traverser = new NodeTraverser;
$traverser->addVisitor($visitor1);
$traverser->addVisitor($visitor2);
$traverser->addVisitor($visitor3);
$preExpected = array($visitor1, $visitor2, $visitor3);
$this->assertAttributeSame($preExpected, 'visitors', $traverser, 'The appropriate visitors have not been added');
$traverser->removeVisitor($visitor2);
$postExpected = array(0 => $visitor1, 2 => $visitor3);
$this->assertAttributeSame($postExpected, 'visitors', $traverser, 'The appropriate visitors are not present after removal');
}
public function testNoCloneNodes() {
$stmts = array(new Node\Stmt\Echo_(array(new String_('Foo'), new String_('Bar'))));
$traverser = new NodeTraverser;
$this->assertSame($stmts, $traverser->traverse($stmts));
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage leaveNode() may only return an array if the parent structure is an array
*/
public function testReplaceByArrayOnlyAllowedIfParentIsArray() {
$stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42)));
$visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock();
$visitor->method('leaveNode')->willReturn(array(new Node\Scalar\DNumber(42.0)));
$traverser = new NodeTraverser();
$traverser->addVisitor($visitor);
$traverser->traverse($stmts);
}
}

View file

@ -0,0 +1,468 @@
<?php
namespace PhpParser\NodeVisitor;
use PhpParser;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class NameResolverTest extends \PHPUnit_Framework_TestCase
{
private function canonicalize($string) {
return str_replace("\r\n", "\n", $string);
}
/**
* @covers PhpParser\NodeVisitor\NameResolver
*/
public function testResolveNames() {
$code = <<<'EOC'
<?php
namespace Foo {
use Hallo as Hi;
new Bar();
new Hi();
new Hi\Bar();
new \Bar();
new namespace\Bar();
bar();
hi();
Hi\bar();
foo\bar();
\bar();
namespace\bar();
}
namespace {
use Hallo as Hi;
new Bar();
new Hi();
new Hi\Bar();
new \Bar();
new namespace\Bar();
bar();
hi();
Hi\bar();
foo\bar();
\bar();
namespace\bar();
}
namespace Bar {
use function foo\bar as baz;
use const foo\BAR as BAZ;
use foo as bar;
bar();
baz();
bar\foo();
baz\foo();
BAR();
BAZ();
BAR\FOO();
BAZ\FOO();
bar;
baz;
bar\foo;
baz\foo;
BAR;
BAZ;
BAR\FOO;
BAZ\FOO;
}
namespace Baz {
use A\T\{B\C, D\E};
use function X\T\{b\c, d\e};
use const Y\T\{B\C, D\E};
use Z\T\{G, function f, const K};
new C;
new E;
new C\D;
new E\F;
new G;
c();
e();
f();
C;
E;
K;
}
EOC;
$expectedCode = <<<'EOC'
namespace Foo {
use Hallo as Hi;
new \Foo\Bar();
new \Hallo();
new \Hallo\Bar();
new \Bar();
new \Foo\Bar();
bar();
hi();
\Hallo\bar();
\Foo\foo\bar();
\bar();
\Foo\bar();
}
namespace {
use Hallo as Hi;
new \Bar();
new \Hallo();
new \Hallo\Bar();
new \Bar();
new \Bar();
\bar();
\hi();
\Hallo\bar();
\foo\bar();
\bar();
\bar();
}
namespace Bar {
use function foo\bar as baz;
use const foo\BAR as BAZ;
use foo as bar;
bar();
\foo\bar();
\foo\foo();
\Bar\baz\foo();
BAR();
\foo\bar();
\foo\FOO();
\Bar\BAZ\FOO();
bar;
baz;
\foo\foo;
\Bar\baz\foo;
BAR;
\foo\BAR;
\foo\FOO;
\Bar\BAZ\FOO;
}
namespace Baz {
use A\T\{B\C, D\E};
use function X\T\{b\c, d\e};
use const Y\T\{B\C, D\E};
use Z\T\{G, function f, const K};
new \A\T\B\C();
new \A\T\D\E();
new \A\T\B\C\D();
new \A\T\D\E\F();
new \Z\T\G();
\X\T\b\c();
\X\T\d\e();
\Z\T\f();
\Y\T\B\C;
\Y\T\D\E;
\Z\T\K;
}
EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $parser->parse($code);
$stmts = $traverser->traverse($stmts);
$this->assertSame(
$this->canonicalize($expectedCode),
$prettyPrinter->prettyPrint($stmts)
);
}
/**
* @covers PhpParser\NodeVisitor\NameResolver
*/
public function testResolveLocations() {
$code = <<<'EOC'
<?php
namespace NS;
class A extends B implements C, D {
use E, F, G {
f as private g;
E::h as i;
E::j insteadof F, G;
}
}
interface A extends C, D {
public function a(A $a) : A;
}
function fn(A $a) : A {}
function fn2(array $a) : array {}
function(A $a) : A {};
function fn3(?A $a) : ?A {}
function fn4(?array $a) : ?array {}
A::b();
A::$b;
A::B;
new A;
$a instanceof A;
namespace\a();
namespace\A;
try {
$someThing;
} catch (A $a) {
$someThingElse;
}
EOC;
$expectedCode = <<<'EOC'
namespace NS;
class A extends \NS\B implements \NS\C, \NS\D
{
use \NS\E, \NS\F, \NS\G {
f as private g;
\NS\E::h as i;
\NS\E::j insteadof \NS\F, \NS\G;
}
}
interface A extends \NS\C, \NS\D
{
public function a(\NS\A $a) : \NS\A;
}
function fn(\NS\A $a) : \NS\A
{
}
function fn2(array $a) : array
{
}
function (\NS\A $a) : \NS\A {
};
function fn3(?\NS\A $a) : ?\NS\A
{
}
function fn4(?array $a) : ?array
{
}
\NS\A::b();
\NS\A::$b;
\NS\A::B;
new \NS\A();
$a instanceof \NS\A;
\NS\a();
\NS\A;
try {
$someThing;
} catch (\NS\A $a) {
$someThingElse;
}
EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $parser->parse($code);
$stmts = $traverser->traverse($stmts);
$this->assertSame(
$this->canonicalize($expectedCode),
$prettyPrinter->prettyPrint($stmts)
);
}
public function testNoResolveSpecialName() {
$stmts = array(new Node\Expr\New_(new Name('self')));
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$this->assertEquals($stmts, $traverser->traverse($stmts));
}
public function testAddDeclarationNamespacedName() {
$nsStmts = array(
new Stmt\Class_('A'),
new Stmt\Interface_('B'),
new Stmt\Function_('C'),
new Stmt\Const_(array(
new Node\Const_('D', new Node\Scalar\LNumber(42))
)),
new Stmt\Trait_('E'),
new Expr\New_(new Stmt\Class_(null)),
);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]);
$this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName);
$this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName);
$this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName);
$this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
$this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName);
$this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
$stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]);
$this->assertSame('A', (string) $stmts[0]->stmts[0]->namespacedName);
$this->assertSame('B', (string) $stmts[0]->stmts[1]->namespacedName);
$this->assertSame('C', (string) $stmts[0]->stmts[2]->namespacedName);
$this->assertSame('D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
$this->assertSame('E', (string) $stmts[0]->stmts[4]->namespacedName);
$this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
}
public function testAddRuntimeResolvedNamespacedName() {
$stmts = array(
new Stmt\Namespace_(new Name('NS'), array(
new Expr\FuncCall(new Name('foo')),
new Expr\ConstFetch(new Name('FOO')),
)),
new Stmt\Namespace_(null, array(
new Expr\FuncCall(new Name('foo')),
new Expr\ConstFetch(new Name('FOO')),
)),
);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $traverser->traverse($stmts);
$this->assertSame('NS\\foo', (string) $stmts[0]->stmts[0]->name->getAttribute('namespacedName'));
$this->assertSame('NS\\FOO', (string) $stmts[0]->stmts[1]->name->getAttribute('namespacedName'));
$this->assertFalse($stmts[1]->stmts[0]->name->hasAttribute('namespacedName'));
$this->assertFalse($stmts[1]->stmts[1]->name->hasAttribute('namespacedName'));
}
/**
* @dataProvider provideTestError
*/
public function testError(Node $stmt, $errorMsg) {
$this->setExpectedException('PhpParser\Error', $errorMsg);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$traverser->traverse(array($stmt));
}
public function provideTestError() {
return array(
array(
new Stmt\Use_(array(
new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
), Stmt\Use_::TYPE_NORMAL),
'Cannot use C\D as B because the name is already in use on line 2'
),
array(
new Stmt\Use_(array(
new Stmt\UseUse(new Name('a\b'), 'b', 0, array('startLine' => 1)),
new Stmt\UseUse(new Name('c\d'), 'B', 0, array('startLine' => 2)),
), Stmt\Use_::TYPE_FUNCTION),
'Cannot use function c\d as B because the name is already in use on line 2'
),
array(
new Stmt\Use_(array(
new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
), Stmt\Use_::TYPE_CONSTANT),
'Cannot use const C\D as B because the name is already in use on line 2'
),
array(
new Expr\New_(new Name\FullyQualified('self', array('startLine' => 3))),
"'\\self' is an invalid class name on line 3"
),
array(
new Expr\New_(new Name\Relative('self', array('startLine' => 3))),
"'\\self' is an invalid class name on line 3"
),
array(
new Expr\New_(new Name\FullyQualified('PARENT', array('startLine' => 3))),
"'\\PARENT' is an invalid class name on line 3"
),
array(
new Expr\New_(new Name\Relative('STATIC', array('startLine' => 3))),
"'\\STATIC' is an invalid class name on line 3"
),
);
}
public function testClassNameIsCaseInsensitive()
{
$source = <<<'EOC'
<?php
namespace Foo;
use Bar\Baz;
$test = new baz();
EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$stmts = $parser->parse($source);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $traverser->traverse($stmts);
$stmt = $stmts[0];
$this->assertSame(array('Bar', 'Baz'), $stmt->stmts[1]->expr->class->parts);
}
public function testSpecialClassNamesAreCaseInsensitive() {
$source = <<<'EOC'
<?php
namespace Foo;
class Bar
{
public static function method()
{
SELF::method();
PARENT::method();
STATIC::method();
}
}
EOC;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
$stmts = $parser->parse($source);
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver);
$stmts = $traverser->traverse($stmts);
$classStmt = $stmts[0];
$methodStmt = $classStmt->stmts[0]->stmts[0];
$this->assertSame('SELF', (string)$methodStmt->stmts[0]->class);
$this->assertSame('PARENT', (string)$methodStmt->stmts[1]->class);
$this->assertSame('STATIC', (string)$methodStmt->stmts[2]->class);
}
public function testAddOriginalNames() {
$traverser = new PhpParser\NodeTraverser;
$traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true]));
$n1 = new Name('Bar');
$n2 = new Name('bar');
$origStmts = [
new Stmt\Namespace_(new Name('Foo'), [
new Expr\ClassConstFetch($n1, 'FOO'),
new Expr\FuncCall($n2),
])
];
$stmts = $traverser->traverse($origStmts);
$this->assertSame($n1, $stmts[0]->stmts[0]->class->getAttribute('originalName'));
$this->assertSame($n2, $stmts[0]->stmts[1]->name->getAttribute('originalName'));
}
}

View file

@ -0,0 +1,94 @@
<?php
namespace PhpParser\Parser;
use PhpParser\Error;
use PhpParser\Lexer;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt;
use PhpParser\ParserTest;
require_once __DIR__ . '/../ParserTest.php';
class MultipleTest extends ParserTest {
// This provider is for the generic parser tests, just pick an arbitrary order here
protected function getParser(Lexer $lexer) {
return new Multiple([new Php5($lexer), new Php7($lexer)]);
}
private function getPrefer7() {
$lexer = new Lexer(['usedAttributes' => []]);
return new Multiple([new Php7($lexer), new Php5($lexer)]);
}
private function getPrefer5() {
$lexer = new Lexer(['usedAttributes' => []]);
return new Multiple([new Php5($lexer), new Php7($lexer)]);
}
/** @dataProvider provideTestParse */
public function testParse($code, Multiple $parser, $expected) {
$this->assertEquals($expected, $parser->parse($code));
}
public function provideTestParse() {
return [
[
// PHP 7 only code
'<?php class Test { function function() {} }',
$this->getPrefer5(),
[
new Stmt\Class_('Test', ['stmts' => [
new Stmt\ClassMethod('function')
]]),
]
],
[
// PHP 5 only code
'<?php global $$a->b;',
$this->getPrefer7(),
[
new Stmt\Global_([
new Expr\Variable(new Expr\PropertyFetch(new Expr\Variable('a'), 'b'))
])
]
],
[
// Different meaning (PHP 5)
'<?php $$a[0];',
$this->getPrefer5(),
[
new Expr\Variable(
new Expr\ArrayDimFetch(new Expr\Variable('a'), LNumber::fromString('0'))
)
]
],
[
// Different meaning (PHP 7)
'<?php $$a[0];',
$this->getPrefer7(),
[
new Expr\ArrayDimFetch(
new Expr\Variable(new Expr\Variable('a')), LNumber::fromString('0')
)
]
],
];
}
public function testThrownError() {
$this->setExpectedException('PhpParser\Error', 'FAIL A');
$parserA = $this->getMockBuilder('PhpParser\Parser')->getMock();
$parserA->expects($this->at(0))
->method('parse')->will($this->throwException(new Error('FAIL A')));
$parserB = $this->getMockBuilder('PhpParser\Parser')->getMock();
$parserB->expects($this->at(0))
->method('parse')->will($this->throwException(new Error('FAIL B')));
$parser = new Multiple([$parserA, $parserB]);
$parser->parse('dummy');
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace PhpParser\Parser;
use PhpParser\Lexer;
use PhpParser\ParserTest;
require_once __DIR__ . '/../ParserTest.php';
class Php5Test extends ParserTest {
protected function getParser(Lexer $lexer) {
return new Php5($lexer);
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace PhpParser\Parser;
use PhpParser\Lexer;
use PhpParser\ParserTest;
require_once __DIR__ . '/../ParserTest.php';
class Php7Test extends ParserTest {
protected function getParser(Lexer $lexer) {
return new Php7($lexer);
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace PhpParser;
/* This test is very weak, because PHPUnit's assertEquals assertion is way too slow dealing with the
* large objects involved here. So we just do some basic instanceof tests instead. */
class ParserFactoryTest extends \PHPUnit_Framework_TestCase {
/** @dataProvider provideTestCreate */
public function testCreate($kind, $lexer, $expected) {
$this->assertInstanceOf($expected, (new ParserFactory)->create($kind, $lexer));
}
public function provideTestCreate() {
$lexer = new Lexer();
return [
[
ParserFactory::PREFER_PHP7, $lexer,
'PhpParser\Parser\Multiple'
],
[
ParserFactory::PREFER_PHP5, null,
'PhpParser\Parser\Multiple'
],
[
ParserFactory::ONLY_PHP7, null,
'PhpParser\Parser\Php7'
],
[
ParserFactory::ONLY_PHP5, $lexer,
'PhpParser\Parser\Php5'
]
];
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace PhpParser;
use PhpParser\Comment;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\String_;
abstract class ParserTest extends \PHPUnit_Framework_TestCase
{
/** @returns Parser */
abstract protected function getParser(Lexer $lexer);
/**
* @expectedException \PhpParser\Error
* @expectedExceptionMessage Syntax error, unexpected EOF on line 1
*/
public function testParserThrowsSyntaxError() {
$parser = $this->getParser(new Lexer());
$parser->parse('<?php foo');
}
/**
* @expectedException \PhpParser\Error
* @expectedExceptionMessage Cannot use foo as self because 'self' is a special class name on line 1
*/
public function testParserThrowsSpecialError() {
$parser = $this->getParser(new Lexer());
$parser->parse('<?php use foo as self;');
}
/**
* @expectedException \PhpParser\Error
* @expectedExceptionMessage Unterminated comment on line 1
*/
public function testParserThrowsLexerError() {
$parser = $this->getParser(new Lexer());
$parser->parse('<?php /*');
}
public function testAttributeAssignment() {
$lexer = new Lexer(array(
'usedAttributes' => array(
'comments', 'startLine', 'endLine',
'startTokenPos', 'endTokenPos',
)
));
$code = <<<'EOC'
<?php
/** Doc comment */
function test($a) {
// Line
// Comments
echo $a;
}
EOC;
$code = canonicalize($code);
$parser = $this->getParser($lexer);
$stmts = $parser->parse($code);
/** @var \PhpParser\Node\Stmt\Function_ $fn */
$fn = $stmts[0];
$this->assertInstanceOf('PhpParser\Node\Stmt\Function_', $fn);
$this->assertEquals(array(
'comments' => array(
new Comment\Doc('/** Doc comment */', 2, 6),
),
'startLine' => 3,
'endLine' => 7,
'startTokenPos' => 3,
'endTokenPos' => 21,
), $fn->getAttributes());
$param = $fn->params[0];
$this->assertInstanceOf('PhpParser\Node\Param', $param);
$this->assertEquals(array(
'startLine' => 3,
'endLine' => 3,
'startTokenPos' => 7,
'endTokenPos' => 7,
), $param->getAttributes());
/** @var \PhpParser\Node\Stmt\Echo_ $echo */
$echo = $fn->stmts[0];
$this->assertInstanceOf('PhpParser\Node\Stmt\Echo_', $echo);
$this->assertEquals(array(
'comments' => array(
new Comment("// Line\n", 4, 49),
new Comment("// Comments\n", 5, 61),
),
'startLine' => 6,
'endLine' => 6,
'startTokenPos' => 16,
'endTokenPos' => 19,
), $echo->getAttributes());
/** @var \PhpParser\Node\Expr\Variable $var */
$var = $echo->exprs[0];
$this->assertInstanceOf('PhpParser\Node\Expr\Variable', $var);
$this->assertEquals(array(
'startLine' => 6,
'endLine' => 6,
'startTokenPos' => 18,
'endTokenPos' => 18,
), $var->getAttributes());
}
/**
* @expectedException \RangeException
* @expectedExceptionMessage The lexer returned an invalid token (id=999, value=foobar)
*/
public function testInvalidToken() {
$lexer = new InvalidTokenLexer;
$parser = $this->getParser($lexer);
$parser->parse('dummy');
}
/**
* @dataProvider provideTestExtraAttributes
*/
public function testExtraAttributes($code, $expectedAttributes) {
$parser = $this->getParser(new Lexer);
$stmts = $parser->parse("<?php $code;");
$attributes = $stmts[0]->getAttributes();
foreach ($expectedAttributes as $name => $value) {
$this->assertSame($value, $attributes[$name]);
}
}
public function provideTestExtraAttributes() {
return array(
array('0', ['kind' => Scalar\LNumber::KIND_DEC]),
array('9', ['kind' => Scalar\LNumber::KIND_DEC]),
array('07', ['kind' => Scalar\LNumber::KIND_OCT]),
array('0xf', ['kind' => Scalar\LNumber::KIND_HEX]),
array('0XF', ['kind' => Scalar\LNumber::KIND_HEX]),
array('0b1', ['kind' => Scalar\LNumber::KIND_BIN]),
array('0B1', ['kind' => Scalar\LNumber::KIND_BIN]),
array('[]', ['kind' => Expr\Array_::KIND_SHORT]),
array('array()', ['kind' => Expr\Array_::KIND_LONG]),
array("'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
array("b'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
array("B'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
array('"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array('b"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array('B"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array('"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array('b"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array('B"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
array("<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
array("<<<STR\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("<<<\"STR\"\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("b<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
array("B<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
array("<<< \t 'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
// HHVM doesn't support this due to a lexer bug
// (https://github.com/facebook/hhvm/issues/6970)
// array("<<<'\xff'\n\xff\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => "\xff"]),
array("<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("b<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("B<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("<<< \t \"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
array("die", ['kind' => Expr\Exit_::KIND_DIE]),
array("die('done')", ['kind' => Expr\Exit_::KIND_DIE]),
array("exit", ['kind' => Expr\Exit_::KIND_EXIT]),
array("exit(1)", ['kind' => Expr\Exit_::KIND_EXIT]),
array("?>Foo", ['hasLeadingNewline' => false]),
array("?>\nFoo", ['hasLeadingNewline' => true]),
array("namespace Foo;", ['kind' => Node\Stmt\Namespace_::KIND_SEMICOLON]),
array("namespace Foo {}", ['kind' => Node\Stmt\Namespace_::KIND_BRACED]),
array("namespace {}", ['kind' => Node\Stmt\Namespace_::KIND_BRACED]),
);
}
}
class InvalidTokenLexer extends Lexer {
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
$value = 'foobar';
return 999;
}
}

View file

@ -0,0 +1,207 @@
<?php
namespace PhpParser;
use PhpParser\Comment;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\Encapsed;
use PhpParser\Node\Scalar\EncapsedStringPart;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinter\Standard;
require_once __DIR__ . '/CodeTestAbstract.php';
class PrettyPrinterTest extends CodeTestAbstract
{
protected function doTestPrettyPrintMethod($method, $name, $code, $expected, $modeLine) {
$lexer = new Lexer\Emulative;
$parser5 = new Parser\Php5($lexer);
$parser7 = new Parser\Php7($lexer);
list($version, $options) = $this->parseModeLine($modeLine);
$prettyPrinter = new Standard($options);
try {
$output5 = canonicalize($prettyPrinter->$method($parser5->parse($code)));
} catch (Error $e) {
$output5 = null;
if ('php7' !== $version) {
throw $e;
}
}
try {
$output7 = canonicalize($prettyPrinter->$method($parser7->parse($code)));
} catch (Error $e) {
$output7 = null;
if ('php5' !== $version) {
throw $e;
}
}
if ('php5' === $version) {
$this->assertSame($expected, $output5, $name);
$this->assertNotSame($expected, $output7, $name);
} else if ('php7' === $version) {
$this->assertSame($expected, $output7, $name);
$this->assertNotSame($expected, $output5, $name);
} else {
$this->assertSame($expected, $output5, $name);
$this->assertSame($expected, $output7, $name);
}
}
/**
* @dataProvider provideTestPrettyPrint
* @covers PhpParser\PrettyPrinter\Standard<extended>
*/
public function testPrettyPrint($name, $code, $expected, $mode) {
$this->doTestPrettyPrintMethod('prettyPrint', $name, $code, $expected, $mode);
}
/**
* @dataProvider provideTestPrettyPrintFile
* @covers PhpParser\PrettyPrinter\Standard<extended>
*/
public function testPrettyPrintFile($name, $code, $expected, $mode) {
$this->doTestPrettyPrintMethod('prettyPrintFile', $name, $code, $expected, $mode);
}
public function provideTestPrettyPrint() {
return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'test');
}
public function provideTestPrettyPrintFile() {
return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'file-test');
}
public function testPrettyPrintExpr() {
$prettyPrinter = new Standard;
$expr = new Expr\BinaryOp\Mul(
new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')),
new Expr\Variable('c')
);
$this->assertEquals('($a + $b) * $c', $prettyPrinter->prettyPrintExpr($expr));
$expr = new Expr\Closure(array(
'stmts' => array(new Stmt\Return_(new String_("a\nb")))
));
$this->assertEquals("function () {\n return 'a\nb';\n}", $prettyPrinter->prettyPrintExpr($expr));
}
public function testCommentBeforeInlineHTML() {
$prettyPrinter = new PrettyPrinter\Standard;
$comment = new Comment\Doc("/**\n * This is a comment\n */");
$stmts = [new Stmt\InlineHTML('Hello World!', ['comments' => [$comment]])];
$expected = "<?php\n\n/**\n * This is a comment\n */\n?>\nHello World!";
$this->assertSame($expected, $prettyPrinter->prettyPrintFile($stmts));
}
private function parseModeLine($modeLine) {
$parts = explode(' ', $modeLine, 2);
$version = isset($parts[0]) ? $parts[0] : 'both';
$options = isset($parts[1]) ? json_decode($parts[1], true) : [];
return [$version, $options];
}
public function testArraySyntaxDefault() {
$prettyPrinter = new Standard(['shortArraySyntax' => true]);
$expr = new Expr\Array_([
new Expr\ArrayItem(new String_('val'), new String_('key'))
]);
$expected = "['key' => 'val']";
$this->assertSame($expected, $prettyPrinter->prettyPrintExpr($expr));
}
/**
* @dataProvider provideTestKindAttributes
*/
public function testKindAttributes($node, $expected) {
$prttyPrinter = new PrettyPrinter\Standard;
$result = $prttyPrinter->prettyPrintExpr($node);
$this->assertSame($expected, $result);
}
public function provideTestKindAttributes() {
$nowdoc = ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR'];
$heredoc = ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR'];
return [
// Defaults to single quoted
[new String_('foo'), "'foo'"],
// Explicit single/double quoted
[new String_('foo', ['kind' => String_::KIND_SINGLE_QUOTED]), "'foo'"],
[new String_('foo', ['kind' => String_::KIND_DOUBLE_QUOTED]), '"foo"'],
// Fallback from doc string if no label
[new String_('foo', ['kind' => String_::KIND_NOWDOC]), "'foo'"],
[new String_('foo', ['kind' => String_::KIND_HEREDOC]), '"foo"'],
// Fallback if string contains label
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'A']), "'A\nB\nC'"],
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'B']), "'A\nB\nC'"],
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'C']), "'A\nB\nC'"],
[new String_("STR;", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']), "'STR;'"],
// Doc string if label not contained (or not in ending position)
[new String_("foo", $nowdoc), "<<<'STR'\nfoo\nSTR\n"],
[new String_("foo", $heredoc), "<<<STR\nfoo\nSTR\n"],
[new String_("STRx", $nowdoc), "<<<'STR'\nSTRx\nSTR\n"],
[new String_("xSTR", $nowdoc), "<<<'STR'\nxSTR\nSTR\n"],
// Empty doc string variations (encapsed variant does not occur naturally)
[new String_("", $nowdoc), "<<<'STR'\nSTR\n"],
[new String_("", $heredoc), "<<<STR\nSTR\n"],
[new Encapsed([new EncapsedStringPart('')], $heredoc), "<<<STR\nSTR\n"],
// Encapsed doc string variations
[new Encapsed([new EncapsedStringPart('foo')], $heredoc), "<<<STR\nfoo\nSTR\n"],
[new Encapsed([new EncapsedStringPart('foo'), new Expr\Variable('y')], $heredoc), "<<<STR\nfoo{\$y}\nSTR\n"],
[new Encapsed([new EncapsedStringPart("\nSTR"), new Expr\Variable('y')], $heredoc), "<<<STR\n\nSTR{\$y}\nSTR\n"],
[new Encapsed([new EncapsedStringPart("\nSTR"), new Expr\Variable('y')], $heredoc), "<<<STR\n\nSTR{\$y}\nSTR\n"],
[new Encapsed([new Expr\Variable('y'), new EncapsedStringPart("STR\n")], $heredoc), "<<<STR\n{\$y}STR\n\nSTR\n"],
// Encapsed doc string fallback
[new Encapsed([new Expr\Variable('y'), new EncapsedStringPart("\nSTR")], $heredoc), '"{$y}\\nSTR"'],
[new Encapsed([new EncapsedStringPart("STR\n"), new Expr\Variable('y')], $heredoc), '"STR\\n{$y}"'],
[new Encapsed([new EncapsedStringPart("STR")], $heredoc), '"STR"'],
];
}
/** @dataProvider provideTestUnnaturalLiterals */
public function testUnnaturalLiterals($node, $expected) {
$prttyPrinter = new PrettyPrinter\Standard;
$result = $prttyPrinter->prettyPrintExpr($node);
$this->assertSame($expected, $result);
}
public function provideTestUnnaturalLiterals() {
return [
[new LNumber(-1), '-1'],
[new LNumber(-PHP_INT_MAX - 1), '(-' . PHP_INT_MAX . '-1)'],
[new LNumber(-1, ['kind' => LNumber::KIND_BIN]), '-0b1'],
[new LNumber(-1, ['kind' => LNumber::KIND_OCT]), '-01'],
[new LNumber(-1, ['kind' => LNumber::KIND_HEX]), '-0x1'],
[new DNumber(\INF), '\INF'],
[new DNumber(-\INF), '-\INF'],
[new DNumber(-\NAN), '\NAN'],
];
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Cannot pretty-print AST with Error nodes
*/
public function testPrettyPrintWithError() {
$stmts = [new Expr\PropertyFetch(new Expr\Variable('a'), new Expr\Error())];
$prettyPrinter = new PrettyPrinter\Standard;
$prettyPrinter->prettyPrint($stmts);
}
/**
* @expectedException \LogicException
* @expectedExceptionMessage Cannot pretty-print AST with Error nodes
*/
public function testPrettyPrintWithErrorInClassConstFetch() {
$stmts = [new Expr\ClassConstFetch(new Name('Foo'), new Expr\Error())];
$prettyPrinter = new PrettyPrinter\Standard;
$prettyPrinter->prettyPrint($stmts);
}
}

View file

@ -0,0 +1,172 @@
<?php
namespace PhpParser\Serializer;
use PhpParser;
class XMLTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers PhpParser\Serializer\XML<extended>
*/
public function testSerialize() {
$code = <<<CODE
<?php
// comment
/** doc comment */
function functionName(&\$a = 0, \$b = 1.0) {
echo 'Foo';
}
CODE;
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:node="http://nikic.github.com/PHPParser/XML/node" xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode" xmlns:attribute="http://nikic.github.com/PHPParser/XML/attribute" xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar">
<scalar:array>
<node:Stmt_Function>
<attribute:startLine>
<scalar:int>4</scalar:int>
</attribute:startLine>
<attribute:comments>
<scalar:array>
<comment isDocComment="false" line="2">// comment
</comment>
<comment isDocComment="true" line="3">/** doc comment */</comment>
</scalar:array>
</attribute:comments>
<attribute:endLine>
<scalar:int>6</scalar:int>
</attribute:endLine>
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
<subNode:name>
<scalar:string>functionName</scalar:string>
</subNode:name>
<subNode:params>
<scalar:array>
<node:Param>
<attribute:startLine>
<scalar:int>4</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:true/>
</subNode:byRef>
<subNode:variadic>
<scalar:false/>
</subNode:variadic>
<subNode:name>
<scalar:string>a</scalar:string>
</subNode:name>
<subNode:default>
<node:Scalar_LNumber>
<attribute:startLine>
<scalar:int>4</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<attribute:kind>
<scalar:int>10</scalar:int>
</attribute:kind>
<subNode:value>
<scalar:int>0</scalar:int>
</subNode:value>
</node:Scalar_LNumber>
</subNode:default>
</node:Param>
<node:Param>
<attribute:startLine>
<scalar:int>4</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<subNode:type>
<scalar:null/>
</subNode:type>
<subNode:byRef>
<scalar:false/>
</subNode:byRef>
<subNode:variadic>
<scalar:false/>
</subNode:variadic>
<subNode:name>
<scalar:string>b</scalar:string>
</subNode:name>
<subNode:default>
<node:Scalar_DNumber>
<attribute:startLine>
<scalar:int>4</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>4</scalar:int>
</attribute:endLine>
<subNode:value>
<scalar:float>1</scalar:float>
</subNode:value>
</node:Scalar_DNumber>
</subNode:default>
</node:Param>
</scalar:array>
</subNode:params>
<subNode:returnType>
<scalar:null/>
</subNode:returnType>
<subNode:stmts>
<scalar:array>
<node:Stmt_Echo>
<attribute:startLine>
<scalar:int>5</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>5</scalar:int>
</attribute:endLine>
<subNode:exprs>
<scalar:array>
<node:Scalar_String>
<attribute:startLine>
<scalar:int>5</scalar:int>
</attribute:startLine>
<attribute:endLine>
<scalar:int>5</scalar:int>
</attribute:endLine>
<attribute:kind>
<scalar:int>1</scalar:int>
</attribute:kind>
<subNode:value>
<scalar:string>Foo</scalar:string>
</subNode:value>
</node:Scalar_String>
</scalar:array>
</subNode:exprs>
</node:Stmt_Echo>
</scalar:array>
</subNode:stmts>
</node:Stmt_Function>
</scalar:array>
</AST>
XML;
$parser = new PhpParser\Parser\Php7(new PhpParser\Lexer);
$serializer = new XML;
$code = str_replace("\r\n", "\n", $code);
$stmts = $parser->parse($code);
$this->assertXmlStringEqualsXmlString($xml, $serializer->serialize($stmts));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Unexpected node type
*/
public function testError() {
$serializer = new XML;
$serializer->serialize(array(new \stdClass));
}
}

View file

@ -0,0 +1,150 @@
<?php
namespace PhpParser\Unserializer;
use PhpParser\Comment;
use PhpParser\Node\Scalar;
class XMLTest extends \PHPUnit_Framework_TestCase
{
public function testNode() {
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:node="http://nikic.github.com/PHPParser/XML/node" xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode" xmlns:attribute="http://nikic.github.com/PHPParser/XML/attribute" xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar">
<node:Scalar_String line="1" docComment="/** doc comment */">
<attribute:startLine>
<scalar:int>1</scalar:int>
</attribute:startLine>
<attribute:comments>
<scalar:array>
<comment isDocComment="false" line="2">// comment
</comment>
<comment isDocComment="true" line="3">/** doc comment */</comment>
</scalar:array>
</attribute:comments>
<subNode:value>
<scalar:string>Test</scalar:string>
</subNode:value>
</node:Scalar_String>
</AST>
XML;
$unserializer = new XML;
$this->assertEquals(
new Scalar\String_('Test', array(
'startLine' => 1,
'comments' => array(
new Comment('// comment' . "\n", 2),
new Comment\Doc('/** doc comment */', 3),
),
)),
$unserializer->unserialize($xml)
);
}
public function testEmptyNode() {
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:node="http://nikic.github.com/PHPParser/XML/node">
<node:Scalar_MagicConst_Class />
</AST>
XML;
$unserializer = new XML;
$this->assertEquals(
new Scalar\MagicConst\Class_,
$unserializer->unserialize($xml)
);
}
public function testScalars() {
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar">
<scalar:array>
<scalar:array></scalar:array>
<scalar:array/>
<scalar:string>test</scalar:string>
<scalar:string></scalar:string>
<scalar:string/>
<scalar:int>1</scalar:int>
<scalar:float>1</scalar:float>
<scalar:float>1.5</scalar:float>
<scalar:true/>
<scalar:false/>
<scalar:null/>
</scalar:array>
</AST>
XML;
$result = array(
array(), array(),
'test', '', '',
1,
1, 1.5,
true, false, null
);
$unserializer = new XML;
$this->assertEquals($result, $unserializer->unserialize($xml));
}
/**
* @expectedException \DomainException
* @expectedExceptionMessage AST root element not found
*/
public function testWrongRootElementError() {
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<notAST/>
XML;
$unserializer = new XML;
$unserializer->unserialize($xml);
}
/**
* @dataProvider provideTestErrors
*/
public function testErrors($xml, $errorMsg) {
$this->setExpectedException('DomainException', $errorMsg);
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<AST xmlns:scalar="http://nikic.github.com/PHPParser/XML/scalar"
xmlns:node="http://nikic.github.com/PHPParser/XML/node"
xmlns:subNode="http://nikic.github.com/PHPParser/XML/subNode"
xmlns:foo="http://nikic.github.com/PHPParser/XML/foo">
$xml
</AST>
XML;
$unserializer = new XML;
$unserializer->unserialize($xml);
}
public function provideTestErrors() {
return array(
array('<scalar:true>test</scalar:true>', '"true" scalar must be empty'),
array('<scalar:false>test</scalar:false>', '"false" scalar must be empty'),
array('<scalar:null>test</scalar:null>', '"null" scalar must be empty'),
array('<scalar:foo>bar</scalar:foo>', 'Unknown scalar type "foo"'),
array('<scalar:int>x</scalar:int>', '"x" is not a valid int'),
array('<scalar:float>x</scalar:float>', '"x" is not a valid float'),
array('', 'Expected node or scalar'),
array('<foo:bar>test</foo:bar>', 'Unexpected node of type "foo:bar"'),
array(
'<node:Scalar_String><foo:bar>test</foo:bar></node:Scalar_String>',
'Expected sub node or attribute, got node of type "foo:bar"'
),
array(
'<node:Scalar_String><subNode:value/></node:Scalar_String>',
'Expected node or scalar'
),
array(
'<node:Foo><subNode:value/></node:Foo>',
'Unknown node type "Foo"'
),
);
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace PhpParser;
require __DIR__ . '/../vendor/autoload.php';
function canonicalize($str) {
// normalize EOL style
$str = str_replace("\r\n", "\n", $str);
// trim newlines at end
$str = rtrim($str, "\n");
// remove trailing whitespace on all lines
$lines = explode("\n", $str);
$lines = array_map(function($line) {
return rtrim($line, " \t");
}, $lines);
return implode("\n", $lines);
}

View file

@ -0,0 +1,31 @@
Comments on blocks
-----
<?php
// foo
{
// bar
{
// baz
$a;
}
}
// empty
{}
-----
array(
0: Expr_Variable(
name: a
comments: array(
0: // foo
1: // bar
2: // baz
)
)
1: Stmt_Nop(
comments: array(
0: // empty
)
)
)

View file

@ -0,0 +1,100 @@
Comments
-----
<?php
/** doc 1 */
/* foobar 1 */
// foo 1
// bar 1
$var;
if ($cond) {
/** doc 2 */
/* foobar 2 */
// foo 2
// bar 2
}
/** doc 3 */
/* foobar 3 */
// foo 3
// bar 3
-----
array(
0: Expr_Variable(
name: var
comments: array(
0: /** doc 1 */
1: /* foobar 1 */
2: // foo 1
3: // bar 1
)
)
1: Stmt_If(
cond: Expr_Variable(
name: cond
)
stmts: array(
0: Stmt_Nop(
comments: array(
0: /** doc 2 */
1: /* foobar 2 */
2: // foo 2
3: // bar 2
)
)
)
elseifs: array(
)
else: null
)
2: Stmt_Nop(
comments: array(
0: /** doc 3 */
1: /* foobar 3 */
2: // foo 3
3: // bar 3
)
)
)
-----
<?php
/** doc */
/* foobar */
// foo
// bar
?>
-----
array(
0: Stmt_Nop(
comments: array(
0: /** doc */
1: /* foobar */
2: // foo
3: // bar
)
)
)
-----
<?php
// comment
if (42) {}
-----
array(
0: Stmt_If(
cond: Scalar_LNumber(
value: 42
)
stmts: array(
)
elseifs: array(
)
else: null
comments: array(
0: // comment
)
)
)

View file

@ -0,0 +1,32 @@
Error positions
-----
<?php foo
-----
Syntax error, unexpected EOF from 1:10 to 1:10
array(
0: Expr_ConstFetch(
name: Name(
parts: array(
0: foo
)
)
)
)
-----
<?php foo /* bar */
-----
Syntax error, unexpected EOF from 1:20 to 1:20
array(
0: Expr_ConstFetch(
name: Name(
parts: array(
0: foo
)
)
)
1: Stmt_Nop(
comments: array(
0: /* bar */
)
)
)

View file

@ -0,0 +1,124 @@
Lexer errors
-----
<?php
$a = 42;
/*
$b = 24;
-----
Unterminated comment from 4:1 to 5:9
array(
0: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Scalar_LNumber(
value: 42
)
)
1: Stmt_Nop(
comments: array(
0: /*
$b = 24;
)
)
)
-----
<?php
$a = 42;
@@{ "\1" }@@
$b = 24;
-----
Unexpected character "@@{ "\1" }@@" (ASCII 1) from 4:1 to 4:1
array(
0: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Scalar_LNumber(
value: 42
)
)
1: Expr_Assign(
var: Expr_Variable(
name: b
)
expr: Scalar_LNumber(
value: 24
)
)
)
-----
<?php
$a = 42;
@@{ "\0" }@@
$b = 24;
-----
Unexpected null byte from 4:1 to 4:1
array(
0: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Scalar_LNumber(
value: 42
)
)
1: Expr_Assign(
var: Expr_Variable(
name: b
)
expr: Scalar_LNumber(
value: 24
)
)
)
-----
<?php
$a = 1;
@@{ "\1" }@@
$b = 2;
@@{ "\2" }@@
$c = 3;
-----
Unexpected character "@@{ "\1" }@@" (ASCII 1) from 4:1 to 4:1
Unexpected character "@@{ "\2" }@@" (ASCII 2) from 6:1 to 6:1
array(
0: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Scalar_LNumber(
value: 1
)
)
1: Expr_Assign(
var: Expr_Variable(
name: b
)
expr: Scalar_LNumber(
value: 2
)
)
2: Expr_Assign(
var: Expr_Variable(
name: c
)
expr: Scalar_LNumber(
value: 3
)
)
)
-----
<?php
if ($b) {
$a = 1;
/* unterminated
}
-----
Unterminated comment from 5:5 to 6:2
Syntax error, unexpected EOF from 6:2 to 6:2

View file

@ -0,0 +1,866 @@
Error recovery
-----
<?php
foo()
bar()
baz()
-----
Syntax error, unexpected T_STRING from 4:1 to 4:3
Syntax error, unexpected T_STRING from 5:1 to 5:3
Syntax error, unexpected EOF from 5:6 to 5:6
array(
0: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
)
)
1: Expr_FuncCall(
name: Name(
parts: array(
0: bar
)
)
args: array(
)
)
2: Expr_FuncCall(
name: Name(
parts: array(
0: baz
)
)
args: array(
)
)
)
-----
<?php
foo()
bar();
baz();
-----
Syntax error, unexpected T_STRING from 4:1 to 4:3
array(
0: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
)
)
1: Expr_FuncCall(
name: Name(
parts: array(
0: bar
)
)
args: array(
)
)
2: Expr_FuncCall(
name: Name(
parts: array(
0: baz
)
)
args: array(
)
)
)
-----
<?php
foo();
bar()
baz();
-----
Syntax error, unexpected T_STRING from 5:1 to 5:3
array(
0: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
)
)
1: Expr_FuncCall(
name: Name(
parts: array(
0: bar
)
)
args: array(
)
)
2: Expr_FuncCall(
name: Name(
parts: array(
0: baz
)
)
args: array(
)
)
)
-----
<?php
abc;
1 + ;
-----
Syntax error, unexpected ';' from 3:5 to 3:5
array(
0: Expr_ConstFetch(
name: Name(
parts: array(
0: abc
)
)
)
1: Scalar_LNumber(
value: 1
)
)
-----
<?php
function test() {
1 +
}
-----
Syntax error, unexpected '}' from 4:1 to 4:1
array(
0: Stmt_Function(
byRef: false
name: test
params: array(
)
returnType: null
stmts: array(
0: Scalar_LNumber(
value: 1
)
)
)
)
-----
<?php
$i = 0;
while
$j = 1;
$k = 2;
-----
Syntax error, unexpected T_VARIABLE, expecting '(' from 6:1 to 6:2
array(
0: Expr_Assign(
var: Expr_Variable(
name: i
)
expr: Scalar_LNumber(
value: 0
)
)
1: Expr_Assign(
var: Expr_Variable(
name: j
)
expr: Scalar_LNumber(
value: 1
)
)
2: Expr_Assign(
var: Expr_Variable(
name: k
)
expr: Scalar_LNumber(
value: 2
)
)
)
-----
<?php
$i = 0;
while () {
$j = 1;
}
$k = 2;
// The output here drops the loop - would require Error node to handle this
-----
Syntax error, unexpected ')' from 4:8 to 4:8
array(
0: Expr_Assign(
var: Expr_Variable(
name: i
)
expr: Scalar_LNumber(
value: 0
)
)
1: Expr_Assign(
var: Expr_Variable(
name: j
)
expr: Scalar_LNumber(
value: 1
)
)
2: Expr_Assign(
var: Expr_Variable(
name: k
)
expr: Scalar_LNumber(
value: 2
)
)
3: Stmt_Nop(
comments: array(
0: // The output here drops the loop - would require Error node to handle this
)
)
)
-----
<?php
// Can't recover this yet, as the '}' for the inner_statement_list
// is always required.
$i = 0;
while (true) {
$i = 1;
$i = 2;
-----
Syntax error, unexpected EOF from 8:12 to 8:12
-----
<?php
$foo->
;
-----
!!positions
Syntax error, unexpected ';', expecting T_STRING or T_VARIABLE or '{' or '$' from 3:1 to 3:1
array(
0: Expr_PropertyFetch[2:1 - 2:6](
var: Expr_Variable[2:1 - 2:4](
name: foo
)
name: Expr_Error[3:1 - 2:6](
)
)
)
-----
<?php
function foo() {
$bar->
}
-----
!!positions
Syntax error, unexpected '}', expecting T_STRING or T_VARIABLE or '{' or '$' from 4:1 to 4:1
array(
0: Stmt_Function[2:1 - 4:1](
byRef: false
name: foo
params: array(
)
returnType: null
stmts: array(
0: Expr_PropertyFetch[3:5 - 3:10](
var: Expr_Variable[3:5 - 3:8](
name: bar
)
name: Expr_Error[4:1 - 3:10](
)
)
)
)
)
-----
<?php
new T
-----
Syntax error, unexpected EOF from 2:6 to 2:6
array(
0: Expr_New(
class: Name(
parts: array(
0: T
)
)
args: array(
)
)
)
-----
<?php
new
-----
!!php7,positions
Syntax error, unexpected EOF from 2:4 to 2:4
array(
0: Expr_New[2:1 - 2:3](
class: Expr_Error[2:4 - 2:3](
)
args: array(
)
)
)
-----
<?php
$foo instanceof
-----
!!php7
Syntax error, unexpected EOF from 2:16 to 2:16
array(
0: Expr_Instanceof(
expr: Expr_Variable(
name: foo
)
class: Expr_Error(
)
)
)
-----
<?php
$
-----
!!php7
Syntax error, unexpected EOF, expecting T_VARIABLE or '{' or '$' from 2:2 to 2:2
array(
0: Expr_Variable(
name: Expr_Error(
)
)
)
-----
<?php
Foo::$
-----
!!php7
Syntax error, unexpected EOF, expecting T_VARIABLE or '{' or '$' from 2:7 to 2:7
array(
0: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: Foo
)
)
name: Expr_Error(
)
)
)
-----
<?php
Foo::
-----
!!php7
Syntax error, unexpected EOF from 2:6 to 2:6
array(
0: Expr_ClassConstFetch(
class: Name(
parts: array(
0: Foo
)
)
name: Expr_Error(
)
)
)
-----
<?php
namespace Foo
use A
use function a
use A\{B}
const A = 1
break
break 2
continue
continue 2
return
return 2
echo $a
unset($a)
throw $x
goto label
-----
!!php7
Syntax error, unexpected T_USE, expecting ';' or '{' from 3:1 to 3:3
Syntax error, unexpected T_USE, expecting ';' from 5:1 to 5:3
Syntax error, unexpected T_CONST, expecting ';' from 6:1 to 6:5
Syntax error, unexpected T_BREAK, expecting ';' from 7:1 to 7:5
Syntax error, unexpected T_THROW, expecting ';' from 15:1 to 15:5
array(
0: Stmt_Namespace(
name: Name(
parts: array(
0: Foo
)
)
stmts: array(
0: Stmt_Use(
type: TYPE_NORMAL (1)
uses: array(
0: Stmt_UseUse(
type: TYPE_UNKNOWN (0)
name: Name(
parts: array(
0: A
)
)
alias: A
)
)
)
1: Stmt_Use(
type: TYPE_FUNCTION (2)
uses: array(
0: Stmt_UseUse(
type: TYPE_UNKNOWN (0)
name: Name(
parts: array(
0: a
)
)
alias: a
)
)
)
2: Stmt_GroupUse(
type: TYPE_UNKNOWN (0)
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
type: TYPE_NORMAL (1)
name: Name(
parts: array(
0: B
)
)
alias: B
)
)
)
3: Stmt_Const(
consts: array(
0: Const(
name: A
value: Scalar_LNumber(
value: 1
)
)
)
)
4: Stmt_Break(
num: null
)
5: Stmt_Break(
num: Scalar_LNumber(
value: 2
)
)
6: Stmt_Continue(
num: null
)
7: Stmt_Continue(
num: Scalar_LNumber(
value: 2
)
)
8: Stmt_Return(
expr: null
)
9: Stmt_Return(
expr: Scalar_LNumber(
value: 2
)
)
10: Stmt_Echo(
exprs: array(
0: Expr_Variable(
name: a
)
)
)
11: Stmt_Unset(
vars: array(
0: Expr_Variable(
name: a
)
)
)
12: Stmt_Throw(
expr: Expr_Variable(
name: x
)
)
13: Stmt_Goto(
name: label
)
)
)
)
-----
<?php
use A\{B, };
use function A\{b, };
use A, ;
const A = 42, ;
class X implements Y, {
use A, ;
use A, {
A::b insteadof C, ;
}
const A = 42, ;
public $x, ;
}
interface I extends J, {}
unset($x, );
isset($x, );
declare(a=42, );
function foo($a, ) {}
foo($a, );
global $a, ;
static $a, ;
echo $a, ;
for ($a, ; $b, ; $c, );
function ($a, ) use ($b, ) {};
-----
!!php7
A trailing comma is not allowed here from 5:6 to 5:6
A trailing comma is not allowed here from 6:13 to 6:13
A trailing comma is not allowed here from 8:21 to 8:21
A trailing comma is not allowed here from 9:10 to 9:10
A trailing comma is not allowed here from 10:10 to 10:10
A trailing comma is not allowed here from 11:25 to 11:25
A trailing comma is not allowed here from 13:17 to 13:17
A trailing comma is not allowed here from 14:14 to 14:14
A trailing comma is not allowed here from 16:22 to 16:22
A trailing comma is not allowed here from 18:9 to 18:9
A trailing comma is not allowed here from 19:9 to 19:9
A trailing comma is not allowed here from 21:13 to 21:13
A trailing comma is not allowed here from 23:16 to 23:16
A trailing comma is not allowed here from 24:7 to 24:7
A trailing comma is not allowed here from 25:10 to 25:10
A trailing comma is not allowed here from 26:10 to 26:10
A trailing comma is not allowed here from 27:8 to 27:8
A trailing comma is not allowed here from 29:8 to 29:8
A trailing comma is not allowed here from 29:14 to 29:14
A trailing comma is not allowed here from 29:20 to 29:20
A trailing comma is not allowed here from 30:13 to 30:13
A trailing comma is not allowed here from 30:24 to 30:24
array(
0: Stmt_GroupUse(
type: TYPE_UNKNOWN (0)
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
type: TYPE_NORMAL (1)
name: Name(
parts: array(
0: B
)
)
alias: B
)
)
)
1: Stmt_GroupUse(
type: TYPE_FUNCTION (2)
prefix: Name(
parts: array(
0: A
)
)
uses: array(
0: Stmt_UseUse(
type: TYPE_UNKNOWN (0)
name: Name(
parts: array(
0: b
)
)
alias: b
)
)
)
2: Stmt_Use(
type: TYPE_NORMAL (1)
uses: array(
0: Stmt_UseUse(
type: TYPE_UNKNOWN (0)
name: Name(
parts: array(
0: A
)
)
alias: A
)
)
)
3: Stmt_Const(
consts: array(
0: Const(
name: A
value: Scalar_LNumber(
value: 42
)
)
)
)
4: Stmt_Class(
flags: 0
name: X
extends: null
implements: array(
0: Name(
parts: array(
0: Y
)
)
)
stmts: array(
0: Stmt_TraitUse(
traits: array(
0: Name(
parts: array(
0: A
)
)
)
adaptations: array(
)
)
1: Stmt_TraitUse(
traits: array(
0: Name(
parts: array(
0: A
)
)
)
adaptations: array(
0: Stmt_TraitUseAdaptation_Precedence(
trait: Name(
parts: array(
0: A
)
)
method: b
insteadof: array(
0: Name(
parts: array(
0: C
)
)
)
)
)
)
2: Stmt_ClassConst(
flags: 0
consts: array(
0: Const(
name: A
value: Scalar_LNumber(
value: 42
)
)
)
)
3: Stmt_Property(
flags: MODIFIER_PUBLIC (1)
props: array(
0: Stmt_PropertyProperty(
name: x
default: null
)
)
)
)
)
5: Stmt_Interface(
name: I
extends: array(
0: Name(
parts: array(
0: J
)
)
)
stmts: array(
)
)
6: Stmt_Unset(
vars: array(
0: Expr_Variable(
name: x
)
)
)
7: Expr_Isset(
vars: array(
0: Expr_Variable(
name: x
)
)
)
8: Stmt_Declare(
declares: array(
0: Stmt_DeclareDeclare(
key: a
value: Scalar_LNumber(
value: 42
)
)
)
stmts: null
)
9: Stmt_Function(
byRef: false
name: foo
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
returnType: null
stmts: array(
)
)
10: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: false
unpack: false
)
)
)
11: Stmt_Global(
vars: array(
0: Expr_Variable(
name: a
)
)
)
12: Stmt_Static(
vars: array(
0: Stmt_StaticVar(
name: a
default: null
)
)
)
13: Stmt_Echo(
exprs: array(
0: Expr_Variable(
name: a
)
)
)
14: Stmt_For(
init: array(
0: Expr_Variable(
name: a
)
)
cond: array(
0: Expr_Variable(
name: b
)
)
loop: array(
0: Expr_Variable(
name: c
)
)
stmts: array(
)
)
15: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
0: Expr_ClosureUse(
var: b
byRef: false
)
)
returnType: null
stmts: array(
)
)
)
-----
<?php
foo(Bar::);
-----
!!php7,positions
Syntax error, unexpected ')' from 3:10 to 3:10
array(
0: Expr_FuncCall[3:1 - 3:10](
name: Name[3:1 - 3:3](
parts: array(
0: foo
)
)
args: array(
0: Arg[3:5 - 3:9](
value: Expr_ClassConstFetch[3:5 - 3:9](
class: Name[3:5 - 3:7](
parts: array(
0: Bar
)
)
name: Expr_Error[3:10 - 3:9](
)
)
byRef: false
unpack: false
)
)
)
)

View file

@ -0,0 +1,142 @@
Array definitions
-----
<?php
array();
array('a');
array('a', );
array('a', 'b');
array('a', &$b, 'c' => 'd', 'e' => &$f);
// short array syntax
[];
[1, 2, 3];
['a' => 'b'];
-----
array(
0: Expr_Array(
items: array(
)
)
1: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: a
)
byRef: false
)
)
)
2: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: a
)
byRef: false
)
)
)
3: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: a
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_String(
value: b
)
byRef: false
)
)
)
4: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: a
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: b
)
byRef: true
)
2: Expr_ArrayItem(
key: Scalar_String(
value: c
)
value: Scalar_String(
value: d
)
byRef: false
)
3: Expr_ArrayItem(
key: Scalar_String(
value: e
)
value: Expr_Variable(
name: f
)
byRef: true
)
)
)
5: Expr_Array(
items: array(
)
comments: array(
0: // short array syntax
)
)
6: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
7: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Scalar_String(
value: b
)
byRef: false
)
)
)
)

View file

@ -0,0 +1,144 @@
Array destructuring
-----
<?php
[$a, $b] = [$c, $d];
[, $a, , , $b, ,] = $foo;
[, [[$a]], $b] = $bar;
['a' => $b, 'b' => $a] = $baz;
-----
!!php7
array(
0: Expr_Assign(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: b
)
byRef: false
)
)
)
expr: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: c
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: d
)
byRef: false
)
)
)
)
1: Expr_Assign(
var: Expr_Array(
items: array(
0: null
1: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
2: null
3: null
4: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: b
)
byRef: false
)
5: null
)
)
expr: Expr_Variable(
name: foo
)
)
2: Expr_Assign(
var: Expr_Array(
items: array(
0: null
1: Expr_ArrayItem(
key: null
value: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
)
)
byRef: false
)
)
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: b
)
byRef: false
)
)
)
expr: Expr_Variable(
name: bar
)
)
3: Expr_Assign(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Expr_Variable(
name: b
)
byRef: false
)
1: Expr_ArrayItem(
key: Scalar_String(
value: b
)
value: Expr_Variable(
name: a
)
byRef: false
)
)
)
expr: Expr_Variable(
name: baz
)
)
)

View file

@ -0,0 +1,301 @@
Assignments
-----
<?php
// simple assign
$a = $b;
// combined assign
$a &= $b;
$a |= $b;
$a ^= $b;
$a .= $b;
$a /= $b;
$a -= $b;
$a %= $b;
$a *= $b;
$a += $b;
$a <<= $b;
$a >>= $b;
$a **= $b;
// chained assign
$a = $b *= $c **= $d;
// by ref assign
$a =& $b;
// list() assign
list($a) = $b;
list($a, , $b) = $c;
list($a, list(, $c), $d) = $e;
// inc/dec
++$a;
$a++;
--$a;
$a--;
-----
array(
0: Expr_Assign(
var: Expr_Variable(
name: a
comments: array(
0: // simple assign
)
)
expr: Expr_Variable(
name: b
)
comments: array(
0: // simple assign
)
)
1: Expr_AssignOp_BitwiseAnd(
var: Expr_Variable(
name: a
comments: array(
0: // combined assign
)
)
expr: Expr_Variable(
name: b
)
comments: array(
0: // combined assign
)
)
2: Expr_AssignOp_BitwiseOr(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
3: Expr_AssignOp_BitwiseXor(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
4: Expr_AssignOp_Concat(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
5: Expr_AssignOp_Div(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
6: Expr_AssignOp_Minus(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
7: Expr_AssignOp_Mod(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
8: Expr_AssignOp_Mul(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
9: Expr_AssignOp_Plus(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
10: Expr_AssignOp_ShiftLeft(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
11: Expr_AssignOp_ShiftRight(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
12: Expr_AssignOp_Pow(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
13: Expr_Assign(
var: Expr_Variable(
name: a
comments: array(
0: // chained assign
)
)
expr: Expr_AssignOp_Mul(
var: Expr_Variable(
name: b
)
expr: Expr_AssignOp_Pow(
var: Expr_Variable(
name: c
)
expr: Expr_Variable(
name: d
)
)
)
comments: array(
0: // chained assign
)
)
14: Expr_AssignRef(
var: Expr_Variable(
name: a
comments: array(
0: // by ref assign
)
)
expr: Expr_Variable(
name: b
)
comments: array(
0: // by ref assign
)
)
15: Expr_Assign(
var: Expr_List(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
)
comments: array(
0: // list() assign
)
)
expr: Expr_Variable(
name: b
)
comments: array(
0: // list() assign
)
)
16: Expr_Assign(
var: Expr_List(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
1: null
2: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: b
)
byRef: false
)
)
)
expr: Expr_Variable(
name: c
)
)
17: Expr_Assign(
var: Expr_List(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: a
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Expr_List(
items: array(
0: null
1: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: c
)
byRef: false
)
)
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: d
)
byRef: false
)
)
)
expr: Expr_Variable(
name: e
)
)
18: Expr_PreInc(
var: Expr_Variable(
name: a
)
comments: array(
0: // inc/dec
)
)
19: Expr_PostInc(
var: Expr_Variable(
name: a
)
)
20: Expr_PreDec(
var: Expr_Variable(
name: a
)
)
21: Expr_PostDec(
var: Expr_Variable(
name: a
)
)
)

View file

@ -0,0 +1,39 @@
Assigning new by reference (PHP 5 only)
-----
<?php
$a =& new B;
-----
!!php5
array(
0: Expr_AssignRef(
var: Expr_Variable(
name: a
)
expr: Expr_New(
class: Name(
parts: array(
0: B
)
)
args: array(
)
)
)
)
-----
<?php
$a =& new B;
-----
!!php7
Syntax error, unexpected T_NEW from 2:7 to 2:9
array(
0: Expr_New(
class: Name(
parts: array(
0: B
)
)
args: array(
)
)
)

View file

@ -0,0 +1,72 @@
Casts
-----
<?php
(array) $a;
(bool) $a;
(boolean) $a;
(real) $a;
(double) $a;
(float) $a;
(int) $a;
(integer) $a;
(object) $a;
(string) $a;
(unset) $a;
-----
array(
0: Expr_Cast_Array(
expr: Expr_Variable(
name: a
)
)
1: Expr_Cast_Bool(
expr: Expr_Variable(
name: a
)
)
2: Expr_Cast_Bool(
expr: Expr_Variable(
name: a
)
)
3: Expr_Cast_Double(
expr: Expr_Variable(
name: a
)
)
4: Expr_Cast_Double(
expr: Expr_Variable(
name: a
)
)
5: Expr_Cast_Double(
expr: Expr_Variable(
name: a
)
)
6: Expr_Cast_Int(
expr: Expr_Variable(
name: a
)
)
7: Expr_Cast_Int(
expr: Expr_Variable(
name: a
)
)
8: Expr_Cast_Object(
expr: Expr_Variable(
name: a
)
)
9: Expr_Cast_String(
expr: Expr_Variable(
name: a
)
)
10: Expr_Cast_Unset(
expr: Expr_Variable(
name: a
)
)
)

View file

@ -0,0 +1,13 @@
Clone
-----
<?php
clone $a;
-----
array(
0: Expr_Clone(
expr: Expr_Variable(
name: a
)
)
)

View file

@ -0,0 +1,142 @@
Closures
-----
<?php
function($a) { $a; };
function($a) use($b) {};
function() use($a, &$b) {};
function &($a) {};
static function() {};
function($a) : array {};
function() use($a) : \Foo\Bar {};
-----
array(
0: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
)
returnType: null
stmts: array(
0: Expr_Variable(
name: a
)
)
)
1: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
0: Expr_ClosureUse(
var: b
byRef: false
)
)
returnType: null
stmts: array(
)
)
2: Expr_Closure(
static: false
byRef: false
params: array(
)
uses: array(
0: Expr_ClosureUse(
var: a
byRef: false
)
1: Expr_ClosureUse(
var: b
byRef: true
)
)
returnType: null
stmts: array(
)
)
3: Expr_Closure(
static: false
byRef: true
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
)
returnType: null
stmts: array(
)
)
4: Expr_Closure(
static: true
byRef: false
params: array(
)
uses: array(
)
returnType: null
stmts: array(
)
)
5: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: a
default: null
)
)
uses: array(
)
returnType: array
stmts: array(
)
)
6: Expr_Closure(
static: false
byRef: false
params: array(
)
uses: array(
0: Expr_ClosureUse(
var: a
byRef: false
)
)
returnType: Name_FullyQualified(
parts: array(
0: Foo
1: Bar
)
)
stmts: array(
)
)
)

View file

@ -0,0 +1,107 @@
Comparison operators
-----
<?php
$a < $b;
$a <= $b;
$a > $b;
$a >= $b;
$a == $b;
$a === $b;
$a != $b;
$a !== $b;
$a <=> $b;
$a instanceof B;
$a instanceof $b;
-----
array(
0: Expr_BinaryOp_Smaller(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
1: Expr_BinaryOp_SmallerOrEqual(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
2: Expr_BinaryOp_Greater(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
3: Expr_BinaryOp_GreaterOrEqual(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
4: Expr_BinaryOp_Equal(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
5: Expr_BinaryOp_Identical(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
6: Expr_BinaryOp_NotEqual(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
7: Expr_BinaryOp_NotIdentical(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
8: Expr_BinaryOp_Spaceship(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
9: Expr_Instanceof(
expr: Expr_Variable(
name: a
)
class: Name(
parts: array(
0: B
)
)
)
10: Expr_Instanceof(
expr: Expr_Variable(
name: a
)
class: Expr_Variable(
name: b
)
)
)

View file

@ -0,0 +1,621 @@
Expressions in static scalar context
-----
<?php
const T_1 = 1 << 1;
const T_2 = 1 / 2;
const T_3 = 1.5 + 1.5;
const T_4 = "foo" . "bar";
const T_5 = (1.5 + 1.5) * 2;
const T_6 = "foo" . 2 . 3 . 4.0;
const T_7 = __LINE__;
const T_8 = <<<ENDOFSTRING
This is a test string
ENDOFSTRING;
const T_9 = ~-1;
const T_10 = (-1?:1) + (0?2:3);
const T_11 = 1 && 0;
const T_12 = 1 and 1;
const T_13 = 0 || 0;
const T_14 = 1 or 0;
const T_15 = 1 xor 1;
const T_16 = 1 xor 0;
const T_17 = 1 < 0;
const T_18 = 0 <= 0;
const T_19 = 1 > 0;
const T_20 = 1 >= 0;
const T_21 = 1 === 1;
const T_22 = 1 !== 1;
const T_23 = 0 != "0";
const T_24 = 1 == "1";
const T_25 = 1 + 2 * 3;
const T_26 = "1" + 2 + "3";
const T_27 = 2 ** 3;
const T_28 = [1, 2, 3][1];
const T_29 = 12 - 13;
const T_30 = 12 ^ 13;
const T_31 = 12 & 13;
const T_32 = 12 | 13;
const T_33 = 12 % 3;
const T_34 = 100 >> 4;
const T_35 = !false;
-----
array(
0: Stmt_Const(
consts: array(
0: Const(
name: T_1
value: Expr_BinaryOp_ShiftLeft(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 1
)
)
)
)
)
1: Stmt_Const(
consts: array(
0: Const(
name: T_2
value: Expr_BinaryOp_Div(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 2
)
)
)
)
)
2: Stmt_Const(
consts: array(
0: Const(
name: T_3
value: Expr_BinaryOp_Plus(
left: Scalar_DNumber(
value: 1.5
)
right: Scalar_DNumber(
value: 1.5
)
)
)
)
)
3: Stmt_Const(
consts: array(
0: Const(
name: T_4
value: Expr_BinaryOp_Concat(
left: Scalar_String(
value: foo
)
right: Scalar_String(
value: bar
)
)
)
)
)
4: Stmt_Const(
consts: array(
0: Const(
name: T_5
value: Expr_BinaryOp_Mul(
left: Expr_BinaryOp_Plus(
left: Scalar_DNumber(
value: 1.5
)
right: Scalar_DNumber(
value: 1.5
)
)
right: Scalar_LNumber(
value: 2
)
)
)
)
)
5: Stmt_Const(
consts: array(
0: Const(
name: T_6
value: Expr_BinaryOp_Concat(
left: Expr_BinaryOp_Concat(
left: Expr_BinaryOp_Concat(
left: Scalar_String(
value: foo
)
right: Scalar_LNumber(
value: 2
)
)
right: Scalar_LNumber(
value: 3
)
)
right: Scalar_DNumber(
value: 4
)
)
)
)
)
6: Stmt_Const(
consts: array(
0: Const(
name: T_7
value: Scalar_MagicConst_Line(
)
)
)
)
7: Stmt_Const(
consts: array(
0: Const(
name: T_8
value: Scalar_String(
value: This is a test string
)
)
)
)
8: Stmt_Const(
consts: array(
0: Const(
name: T_9
value: Expr_BitwiseNot(
expr: Expr_UnaryMinus(
expr: Scalar_LNumber(
value: 1
)
)
)
)
)
)
9: Stmt_Const(
consts: array(
0: Const(
name: T_10
value: Expr_BinaryOp_Plus(
left: Expr_Ternary(
cond: Expr_UnaryMinus(
expr: Scalar_LNumber(
value: 1
)
)
if: null
else: Scalar_LNumber(
value: 1
)
)
right: Expr_Ternary(
cond: Scalar_LNumber(
value: 0
)
if: Scalar_LNumber(
value: 2
)
else: Scalar_LNumber(
value: 3
)
)
)
)
)
)
10: Stmt_Const(
consts: array(
0: Const(
name: T_11
value: Expr_BinaryOp_BooleanAnd(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
11: Stmt_Const(
consts: array(
0: Const(
name: T_12
value: Expr_BinaryOp_LogicalAnd(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 1
)
)
)
)
)
12: Stmt_Const(
consts: array(
0: Const(
name: T_13
value: Expr_BinaryOp_BooleanOr(
left: Scalar_LNumber(
value: 0
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
13: Stmt_Const(
consts: array(
0: Const(
name: T_14
value: Expr_BinaryOp_LogicalOr(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
14: Stmt_Const(
consts: array(
0: Const(
name: T_15
value: Expr_BinaryOp_LogicalXor(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 1
)
)
)
)
)
15: Stmt_Const(
consts: array(
0: Const(
name: T_16
value: Expr_BinaryOp_LogicalXor(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
16: Stmt_Const(
consts: array(
0: Const(
name: T_17
value: Expr_BinaryOp_Smaller(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
17: Stmt_Const(
consts: array(
0: Const(
name: T_18
value: Expr_BinaryOp_SmallerOrEqual(
left: Scalar_LNumber(
value: 0
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
18: Stmt_Const(
consts: array(
0: Const(
name: T_19
value: Expr_BinaryOp_Greater(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
19: Stmt_Const(
consts: array(
0: Const(
name: T_20
value: Expr_BinaryOp_GreaterOrEqual(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 0
)
)
)
)
)
20: Stmt_Const(
consts: array(
0: Const(
name: T_21
value: Expr_BinaryOp_Identical(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 1
)
)
)
)
)
21: Stmt_Const(
consts: array(
0: Const(
name: T_22
value: Expr_BinaryOp_NotIdentical(
left: Scalar_LNumber(
value: 1
)
right: Scalar_LNumber(
value: 1
)
)
)
)
)
22: Stmt_Const(
consts: array(
0: Const(
name: T_23
value: Expr_BinaryOp_NotEqual(
left: Scalar_LNumber(
value: 0
)
right: Scalar_String(
value: 0
)
)
)
)
)
23: Stmt_Const(
consts: array(
0: Const(
name: T_24
value: Expr_BinaryOp_Equal(
left: Scalar_LNumber(
value: 1
)
right: Scalar_String(
value: 1
)
)
)
)
)
24: Stmt_Const(
consts: array(
0: Const(
name: T_25
value: Expr_BinaryOp_Plus(
left: Scalar_LNumber(
value: 1
)
right: Expr_BinaryOp_Mul(
left: Scalar_LNumber(
value: 2
)
right: Scalar_LNumber(
value: 3
)
)
)
)
)
)
25: Stmt_Const(
consts: array(
0: Const(
name: T_26
value: Expr_BinaryOp_Plus(
left: Expr_BinaryOp_Plus(
left: Scalar_String(
value: 1
)
right: Scalar_LNumber(
value: 2
)
)
right: Scalar_String(
value: 3
)
)
)
)
)
26: Stmt_Const(
consts: array(
0: Const(
name: T_27
value: Expr_BinaryOp_Pow(
left: Scalar_LNumber(
value: 2
)
right: Scalar_LNumber(
value: 3
)
)
)
)
)
27: Stmt_Const(
consts: array(
0: Const(
name: T_28
value: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 1
)
)
)
)
)
28: Stmt_Const(
consts: array(
0: Const(
name: T_29
value: Expr_BinaryOp_Minus(
left: Scalar_LNumber(
value: 12
)
right: Scalar_LNumber(
value: 13
)
)
)
)
)
29: Stmt_Const(
consts: array(
0: Const(
name: T_30
value: Expr_BinaryOp_BitwiseXor(
left: Scalar_LNumber(
value: 12
)
right: Scalar_LNumber(
value: 13
)
)
)
)
)
30: Stmt_Const(
consts: array(
0: Const(
name: T_31
value: Expr_BinaryOp_BitwiseAnd(
left: Scalar_LNumber(
value: 12
)
right: Scalar_LNumber(
value: 13
)
)
)
)
)
31: Stmt_Const(
consts: array(
0: Const(
name: T_32
value: Expr_BinaryOp_BitwiseOr(
left: Scalar_LNumber(
value: 12
)
right: Scalar_LNumber(
value: 13
)
)
)
)
)
32: Stmt_Const(
consts: array(
0: Const(
name: T_33
value: Expr_BinaryOp_Mod(
left: Scalar_LNumber(
value: 12
)
right: Scalar_LNumber(
value: 3
)
)
)
)
)
33: Stmt_Const(
consts: array(
0: Const(
name: T_34
value: Expr_BinaryOp_ShiftRight(
left: Scalar_LNumber(
value: 100
)
right: Scalar_LNumber(
value: 4
)
)
)
)
)
34: Stmt_Const(
consts: array(
0: Const(
name: T_35
value: Expr_BooleanNot(
expr: Expr_ConstFetch(
name: Name(
parts: array(
0: false
)
)
)
)
)
)
)
)

View file

@ -0,0 +1,12 @@
Error suppression
-----
<?php
@$a;
-----
array(
0: Expr_ErrorSuppress(
expr: Expr_Variable(
name: a
)
)
)

View file

@ -0,0 +1,34 @@
Exit
-----
<?php
exit;
exit();
exit('Die!');
die;
die();
die('Exit!');
-----
array(
0: Expr_Exit(
expr: null
)
1: Expr_Exit(
expr: null
)
2: Expr_Exit(
expr: Scalar_String(
value: Die!
)
)
3: Expr_Exit(
expr: null
)
4: Expr_Exit(
expr: null
)
5: Expr_Exit(
expr: Scalar_String(
value: Exit!
)
)
)

View file

@ -0,0 +1,99 @@
Arguments
-----
<?php
f();
f($a);
f($a, $b);
f(&$a);
f($a, ...$b);
-----
array(
0: Expr_FuncCall(
name: Name(
parts: array(
0: f
)
)
args: array(
)
)
1: Expr_FuncCall(
name: Name(
parts: array(
0: f
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: false
unpack: false
)
)
)
2: Expr_FuncCall(
name: Name(
parts: array(
0: f
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: false
unpack: false
)
1: Arg(
value: Expr_Variable(
name: b
)
byRef: false
unpack: false
)
)
)
3: Expr_FuncCall(
name: Name(
parts: array(
0: f
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: true
unpack: false
)
)
)
4: Expr_FuncCall(
name: Name(
parts: array(
0: f
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: false
unpack: false
)
1: Arg(
value: Expr_Variable(
name: b
)
byRef: false
unpack: true
)
)
)
)

View file

@ -0,0 +1,33 @@
Constant fetches
-----
<?php
A;
A::B;
A::class;
-----
array(
0: Expr_ConstFetch(
name: Name(
parts: array(
0: A
)
)
)
1: Expr_ClassConstFetch(
class: Name(
parts: array(
0: A
)
)
name: B
)
2: Expr_ClassConstFetch(
class: Name(
parts: array(
0: A
)
)
name: class
)
)

View file

@ -0,0 +1,231 @@
Array/string dereferencing
-----
<?php
"abc"[2];
"abc"[2][0][0];
[1, 2, 3][2];
[1, 2, 3][2][0][0];
array(1, 2, 3)[2];
array(1, 2, 3)[2][0][0];
FOO[0];
Foo::BAR[1];
$foo::BAR[2][1][0];
-----
array(
0: Expr_ArrayDimFetch(
var: Scalar_String(
value: abc
)
dim: Scalar_LNumber(
value: 2
)
)
1: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Scalar_String(
value: abc
)
dim: Scalar_LNumber(
value: 2
)
)
dim: Scalar_LNumber(
value: 0
)
)
dim: Scalar_LNumber(
value: 0
)
)
2: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 2
)
)
3: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 2
)
)
dim: Scalar_LNumber(
value: 0
)
)
dim: Scalar_LNumber(
value: 0
)
)
4: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 2
)
)
5: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 2
)
)
dim: Scalar_LNumber(
value: 0
)
)
dim: Scalar_LNumber(
value: 0
)
)
6: Expr_ArrayDimFetch(
var: Expr_ConstFetch(
name: Name(
parts: array(
0: FOO
)
)
)
dim: Scalar_LNumber(
value: 0
)
)
7: Expr_ArrayDimFetch(
var: Expr_ClassConstFetch(
class: Name(
parts: array(
0: Foo
)
)
name: BAR
)
dim: Scalar_LNumber(
value: 1
)
)
8: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ClassConstFetch(
class: Expr_Variable(
name: foo
)
name: BAR
)
dim: Scalar_LNumber(
value: 2
)
)
dim: Scalar_LNumber(
value: 1
)
)
dim: Scalar_LNumber(
value: 0
)
)
)

View file

@ -0,0 +1,132 @@
Function calls
-----
<?php
// function name variations
a();
$a();
${'a'}();
$$a();
$$$a();
$a['b']();
$a{'b'}();
$a->b['c']();
// array dereferencing
a()['b'];
-----
array(
0: Expr_FuncCall(
name: Name(
parts: array(
0: a
)
comments: array(
0: // function name variations
)
)
args: array(
)
comments: array(
0: // function name variations
)
)
1: Expr_FuncCall(
name: Expr_Variable(
name: a
)
args: array(
)
)
2: Expr_FuncCall(
name: Expr_Variable(
name: Scalar_String(
value: a
)
)
args: array(
)
)
3: Expr_FuncCall(
name: Expr_Variable(
name: Expr_Variable(
name: a
)
)
args: array(
)
)
4: Expr_FuncCall(
name: Expr_Variable(
name: Expr_Variable(
name: Expr_Variable(
name: a
)
)
)
args: array(
)
)
5: Expr_FuncCall(
name: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
args: array(
)
)
6: Expr_FuncCall(
name: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
args: array(
)
)
7: Expr_FuncCall(
name: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
dim: Scalar_String(
value: c
)
)
args: array(
)
)
8: Expr_ArrayDimFetch(
var: Expr_FuncCall(
name: Name(
parts: array(
0: a
)
comments: array(
0: // array dereferencing
)
)
args: array(
)
comments: array(
0: // array dereferencing
)
)
dim: Scalar_String(
value: b
)
comments: array(
0: // array dereferencing
)
)
)

View file

@ -0,0 +1,70 @@
New expression dereferencing
-----
<?php
(new A)->b;
(new A)->b();
(new A)['b'];
(new A)['b']['c'];
-----
array(
0: Expr_PropertyFetch(
var: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
name: b
)
1: Expr_MethodCall(
var: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
name: b
args: array(
)
)
2: Expr_ArrayDimFetch(
var: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
dim: Scalar_String(
value: b
)
)
3: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
dim: Scalar_String(
value: b
)
)
dim: Scalar_String(
value: c
)
)
)

View file

@ -0,0 +1,145 @@
Object access
-----
<?php
// property fetch variations
$a->b;
$a->b['c'];
$a->b{'c'};
// method call variations
$a->b();
$a->{'b'}();
$a->$b();
$a->$b['c']();
// array dereferencing
$a->b()['c'];
$a->b(){'c'}; // invalid PHP: drop Support?
-----
!!php5
array(
0: Expr_PropertyFetch(
var: Expr_Variable(
name: a
comments: array(
0: // property fetch variations
)
)
name: b
comments: array(
0: // property fetch variations
)
)
1: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
dim: Scalar_String(
value: c
)
)
2: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
dim: Scalar_String(
value: c
)
)
3: Expr_MethodCall(
var: Expr_Variable(
name: a
comments: array(
0: // method call variations
)
)
name: b
args: array(
)
comments: array(
0: // method call variations
)
)
4: Expr_MethodCall(
var: Expr_Variable(
name: a
)
name: Scalar_String(
value: b
)
args: array(
)
)
5: Expr_MethodCall(
var: Expr_Variable(
name: a
)
name: Expr_Variable(
name: b
)
args: array(
)
)
6: Expr_MethodCall(
var: Expr_Variable(
name: a
)
name: Expr_ArrayDimFetch(
var: Expr_Variable(
name: b
)
dim: Scalar_String(
value: c
)
)
args: array(
)
)
7: Expr_ArrayDimFetch(
var: Expr_MethodCall(
var: Expr_Variable(
name: a
comments: array(
0: // array dereferencing
)
)
name: b
args: array(
)
comments: array(
0: // array dereferencing
)
)
dim: Scalar_String(
value: c
)
comments: array(
0: // array dereferencing
)
)
8: Expr_ArrayDimFetch(
var: Expr_MethodCall(
var: Expr_Variable(
name: a
)
name: b
args: array(
)
)
dim: Scalar_String(
value: c
)
)
9: Stmt_Nop(
comments: array(
0: // invalid PHP: drop Support?
)
)
)

View file

@ -0,0 +1,62 @@
Simple array access
-----
<?php
$a['b'];
$a['b']['c'];
$a[] = $b;
$a{'b'};
${$a}['b'];
-----
array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
1: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
dim: Scalar_String(
value: c
)
)
2: Expr_Assign(
var: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: null
)
expr: Expr_Variable(
name: b
)
)
3: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
4: Expr_ArrayDimFetch(
var: Expr_Variable(
name: Expr_Variable(
name: a
)
)
dim: Scalar_String(
value: b
)
)
)

View file

@ -0,0 +1,173 @@
Static calls
-----
<?php
// method name variations
A::b();
A::{'b'}();
A::$b();
A::$b['c']();
A::$b['c']['d']();
// array dereferencing
A::b()['c'];
// class name variations
static::b();
$a::b();
${'a'}::b();
$a['b']::c();
-----
!!php5
array(
0: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
comments: array(
0: // method name variations
)
)
name: b
args: array(
)
comments: array(
0: // method name variations
)
)
1: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
)
name: Scalar_String(
value: b
)
args: array(
)
)
2: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
)
name: Expr_Variable(
name: b
)
args: array(
)
)
3: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
)
name: Expr_ArrayDimFetch(
var: Expr_Variable(
name: b
)
dim: Scalar_String(
value: c
)
)
args: array(
)
)
4: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
)
name: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_Variable(
name: b
)
dim: Scalar_String(
value: c
)
)
dim: Scalar_String(
value: d
)
)
args: array(
)
)
5: Expr_ArrayDimFetch(
var: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
comments: array(
0: // array dereferencing
)
)
name: b
args: array(
)
comments: array(
0: // array dereferencing
)
)
dim: Scalar_String(
value: c
)
comments: array(
0: // array dereferencing
)
)
6: Expr_StaticCall(
class: Name(
parts: array(
0: static
)
comments: array(
0: // class name variations
)
)
name: b
args: array(
)
comments: array(
0: // class name variations
)
)
7: Expr_StaticCall(
class: Expr_Variable(
name: a
)
name: b
args: array(
)
)
8: Expr_StaticCall(
class: Expr_Variable(
name: Scalar_String(
value: a
)
)
name: b
args: array(
)
)
9: Expr_StaticCall(
class: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
name: c
args: array(
)
)
)

View file

@ -0,0 +1,91 @@
Static property fetches
-----
<?php
// property name variations
A::$b;
A::$$b;
A::${'b'};
// array access
A::$b['c'];
A::$b{'c'};
// class name variations can be found in staticCall.test
-----
array(
0: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
comments: array(
0: // property name variations
)
)
name: b
comments: array(
0: // property name variations
)
)
1: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: Expr_Variable(
name: b
)
)
2: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: Scalar_String(
value: b
)
)
3: Expr_ArrayDimFetch(
var: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
comments: array(
0: // array access
)
)
name: b
comments: array(
0: // array access
)
)
dim: Scalar_String(
value: c
)
comments: array(
0: // array access
)
)
4: Expr_ArrayDimFetch(
var: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: b
)
dim: Scalar_String(
value: c
)
)
5: Stmt_Nop(
comments: array(
0: // class name variations can be found in staticCall.test
)
)
)

View file

@ -0,0 +1,40 @@
Include and eval
-----
<?php
include 'A.php';
include_once 'A.php';
require 'A.php';
require_once 'A.php';
eval('A');
-----
array(
0: Expr_Include(
expr: Scalar_String(
value: A.php
)
type: TYPE_INCLUDE (1)
)
1: Expr_Include(
expr: Scalar_String(
value: A.php
)
type: TYPE_INCLUDE_ONCE (2)
)
2: Expr_Include(
expr: Scalar_String(
value: A.php
)
type: TYPE_REQUIRE (3)
)
3: Expr_Include(
expr: Scalar_String(
value: A.php
)
type: TYPE_REQURE_ONCE (4)
)
4: Expr_Eval(
expr: Scalar_String(
value: A
)
)
)

View file

@ -0,0 +1,75 @@
isset() and empty()
-----
<?php
isset($a);
isset($a, $b, $c);
empty($a);
empty(foo());
empty(array(1, 2, 3));
-----
array(
0: Expr_Isset(
vars: array(
0: Expr_Variable(
name: a
)
)
)
1: Expr_Isset(
vars: array(
0: Expr_Variable(
name: a
)
1: Expr_Variable(
name: b
)
2: Expr_Variable(
name: c
)
)
)
2: Expr_Empty(
expr: Expr_Variable(
name: a
)
)
3: Expr_Empty(
expr: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
)
)
)
4: Expr_Empty(
expr: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 2
)
byRef: false
)
2: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 3
)
byRef: false
)
)
)
)
)

View file

@ -0,0 +1,75 @@
List destructing with keys
-----
<?php
list('a' => $b) = ['a' => 'b'];
list('a' => list($b => $c), 'd' => $e) = $x;
-----
!!php7
array(
0: Expr_Assign(
var: Expr_List(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Expr_Variable(
name: b
)
byRef: false
)
)
)
expr: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Scalar_String(
value: b
)
byRef: false
)
)
)
)
1: Expr_Assign(
var: Expr_List(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Expr_List(
items: array(
0: Expr_ArrayItem(
key: Expr_Variable(
name: b
)
value: Expr_Variable(
name: c
)
byRef: false
)
)
)
byRef: false
)
1: Expr_ArrayItem(
key: Scalar_String(
value: d
)
value: Expr_Variable(
name: e
)
byRef: false
)
)
)
expr: Expr_Variable(
name: x
)
)
)

View file

@ -0,0 +1,159 @@
Logical operators
-----
<?php
// boolean ops
$a && $b;
$a || $b;
!$a;
!!$a;
// logical ops
$a and $b;
$a or $b;
$a xor $b;
// precedence
$a && $b || $c && $d;
$a && ($b || $c) && $d;
$a = $b || $c;
$a = $b or $c;
-----
array(
0: Expr_BinaryOp_BooleanAnd(
left: Expr_Variable(
name: a
comments: array(
0: // boolean ops
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // boolean ops
)
)
1: Expr_BinaryOp_BooleanOr(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
2: Expr_BooleanNot(
expr: Expr_Variable(
name: a
)
)
3: Expr_BooleanNot(
expr: Expr_BooleanNot(
expr: Expr_Variable(
name: a
)
)
)
4: Expr_BinaryOp_LogicalAnd(
left: Expr_Variable(
name: a
comments: array(
0: // logical ops
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // logical ops
)
)
5: Expr_BinaryOp_LogicalOr(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
6: Expr_BinaryOp_LogicalXor(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
7: Expr_BinaryOp_BooleanOr(
left: Expr_BinaryOp_BooleanAnd(
left: Expr_Variable(
name: a
comments: array(
0: // precedence
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // precedence
)
)
right: Expr_BinaryOp_BooleanAnd(
left: Expr_Variable(
name: c
)
right: Expr_Variable(
name: d
)
)
comments: array(
0: // precedence
)
)
8: Expr_BinaryOp_BooleanAnd(
left: Expr_BinaryOp_BooleanAnd(
left: Expr_Variable(
name: a
)
right: Expr_BinaryOp_BooleanOr(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
)
right: Expr_Variable(
name: d
)
)
9: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Expr_BinaryOp_BooleanOr(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
)
10: Expr_BinaryOp_LogicalOr(
left: Expr_Assign(
var: Expr_Variable(
name: a
)
expr: Expr_Variable(
name: b
)
)
right: Expr_Variable(
name: c
)
)
)

View file

@ -0,0 +1,256 @@
Mathematical operators
-----
<?php
// unary ops
~$a;
+$a;
-$a;
// binary ops
$a & $b;
$a | $b;
$a ^ $b;
$a . $b;
$a / $b;
$a - $b;
$a % $b;
$a * $b;
$a + $b;
$a << $b;
$a >> $b;
$a ** $b;
// associativity
$a * $b * $c;
$a * ($b * $c);
// precedence
$a + $b * $c;
($a + $b) * $c;
// pow is special
$a ** $b ** $c;
($a ** $b) ** $c;
-----
array(
0: Expr_BitwiseNot(
expr: Expr_Variable(
name: a
)
comments: array(
0: // unary ops
)
)
1: Expr_UnaryPlus(
expr: Expr_Variable(
name: a
)
)
2: Expr_UnaryMinus(
expr: Expr_Variable(
name: a
)
)
3: Expr_BinaryOp_BitwiseAnd(
left: Expr_Variable(
name: a
comments: array(
0: // binary ops
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // binary ops
)
)
4: Expr_BinaryOp_BitwiseOr(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
5: Expr_BinaryOp_BitwiseXor(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
6: Expr_BinaryOp_Concat(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
7: Expr_BinaryOp_Div(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
8: Expr_BinaryOp_Minus(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
9: Expr_BinaryOp_Mod(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
10: Expr_BinaryOp_Mul(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
11: Expr_BinaryOp_Plus(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
12: Expr_BinaryOp_ShiftLeft(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
13: Expr_BinaryOp_ShiftRight(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
14: Expr_BinaryOp_Pow(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
15: Expr_BinaryOp_Mul(
left: Expr_BinaryOp_Mul(
left: Expr_Variable(
name: a
comments: array(
0: // associativity
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // associativity
)
)
right: Expr_Variable(
name: c
)
comments: array(
0: // associativity
)
)
16: Expr_BinaryOp_Mul(
left: Expr_Variable(
name: a
)
right: Expr_BinaryOp_Mul(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
)
17: Expr_BinaryOp_Plus(
left: Expr_Variable(
name: a
comments: array(
0: // precedence
)
)
right: Expr_BinaryOp_Mul(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
comments: array(
0: // precedence
)
)
18: Expr_BinaryOp_Mul(
left: Expr_BinaryOp_Plus(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
right: Expr_Variable(
name: c
)
)
19: Expr_BinaryOp_Pow(
left: Expr_Variable(
name: a
comments: array(
0: // pow is special
)
)
right: Expr_BinaryOp_Pow(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
comments: array(
0: // pow is special
)
)
20: Expr_BinaryOp_Pow(
left: Expr_BinaryOp_Pow(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
right: Expr_Variable(
name: c
)
)
)

View file

@ -0,0 +1,146 @@
New
-----
<?php
new A;
new A($b);
// class name variations
new $a();
new $a['b']();
new A::$b();
// DNCR object access
new $a->b();
new $a->b->c();
new $a->b['c']();
new $a->b{'c'}();
// test regression introduces by new dereferencing syntax
(new A);
-----
array(
0: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
1: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: b
)
byRef: false
unpack: false
)
)
)
2: Expr_New(
class: Expr_Variable(
name: a
)
args: array(
)
comments: array(
0: // class name variations
)
)
3: Expr_New(
class: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
args: array(
)
)
4: Expr_New(
class: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: b
)
args: array(
)
)
5: Expr_New(
class: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
args: array(
)
comments: array(
0: // DNCR object access
)
)
6: Expr_New(
class: Expr_PropertyFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
name: c
)
args: array(
)
)
7: Expr_New(
class: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
dim: Scalar_String(
value: c
)
)
args: array(
)
)
8: Expr_New(
class: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Variable(
name: a
)
name: b
)
dim: Scalar_String(
value: c
)
)
args: array(
)
)
9: Expr_New(
class: Name(
parts: array(
0: A
)
)
args: array(
)
)
)

View file

@ -0,0 +1,23 @@
New without a class
-----
<?php
new;
-----
!!php5
Syntax error, unexpected ';' from 2:4 to 2:4
array(
)
-----
<?php
new;
-----
!!php7
Syntax error, unexpected ';' from 2:4 to 2:4
array(
0: Expr_New(
class: Expr_Error(
)
args: array(
)
)
)

View file

@ -0,0 +1,12 @@
Print
-----
<?php
print $a;
-----
array(
0: Expr_Print(
expr: Expr_Variable(
name: a
)
)
)

View file

@ -0,0 +1,46 @@
Shell execution
-----
<?php
``;
`test`;
`test $A`;
`test \``;
`test \"`;
-----
array(
0: Expr_ShellExec(
parts: array(
)
)
1: Expr_ShellExec(
parts: array(
0: Scalar_EncapsedStringPart(
value: test
)
)
)
2: Expr_ShellExec(
parts: array(
0: Scalar_EncapsedStringPart(
value: test
)
1: Expr_Variable(
name: A
)
)
)
3: Expr_ShellExec(
parts: array(
0: Scalar_EncapsedStringPart(
value: test `
)
)
)
4: Expr_ShellExec(
parts: array(
0: Scalar_EncapsedStringPart(
value: test \"
)
)
)
)

View file

@ -0,0 +1,149 @@
Ternary operator
-----
<?php
// ternary
$a ? $b : $c;
$a ?: $c;
// precedence
$a ? $b : $c ? $d : $e;
$a ? $b : ($c ? $d : $e);
// null coalesce
$a ?? $b;
$a ?? $b ?? $c;
$a ?? $b ? $c : $d;
$a && $b ?? $c;
-----
array(
0: Expr_Ternary(
cond: Expr_Variable(
name: a
comments: array(
0: // ternary
)
)
if: Expr_Variable(
name: b
)
else: Expr_Variable(
name: c
)
comments: array(
0: // ternary
)
)
1: Expr_Ternary(
cond: Expr_Variable(
name: a
)
if: null
else: Expr_Variable(
name: c
)
)
2: Expr_Ternary(
cond: Expr_Ternary(
cond: Expr_Variable(
name: a
comments: array(
0: // precedence
)
)
if: Expr_Variable(
name: b
)
else: Expr_Variable(
name: c
)
comments: array(
0: // precedence
)
)
if: Expr_Variable(
name: d
)
else: Expr_Variable(
name: e
)
comments: array(
0: // precedence
)
)
3: Expr_Ternary(
cond: Expr_Variable(
name: a
)
if: Expr_Variable(
name: b
)
else: Expr_Ternary(
cond: Expr_Variable(
name: c
)
if: Expr_Variable(
name: d
)
else: Expr_Variable(
name: e
)
)
)
4: Expr_BinaryOp_Coalesce(
left: Expr_Variable(
name: a
comments: array(
0: // null coalesce
)
)
right: Expr_Variable(
name: b
)
comments: array(
0: // null coalesce
)
)
5: Expr_BinaryOp_Coalesce(
left: Expr_Variable(
name: a
)
right: Expr_BinaryOp_Coalesce(
left: Expr_Variable(
name: b
)
right: Expr_Variable(
name: c
)
)
)
6: Expr_Ternary(
cond: Expr_BinaryOp_Coalesce(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
if: Expr_Variable(
name: c
)
else: Expr_Variable(
name: d
)
)
7: Expr_BinaryOp_Coalesce(
left: Expr_BinaryOp_BooleanAnd(
left: Expr_Variable(
name: a
)
right: Expr_Variable(
name: b
)
)
right: Expr_Variable(
name: c
)
)
)

View file

@ -0,0 +1,25 @@
Non-simple variables are forbidden in PHP 7
-----
<?php
global $$foo->bar;
-----
!!php7
Syntax error, unexpected T_OBJECT_OPERATOR, expecting ';' from 2:13 to 2:14
array(
0: Stmt_Global(
vars: array(
0: Expr_Variable(
name: Expr_Variable(
name: foo
)
)
)
)
1: Expr_ConstFetch(
name: Name(
parts: array(
0: bar
)
)
)
)

View file

@ -0,0 +1,481 @@
UVS indirect calls
-----
<?php
id('var_dump')(1);
id('id')('var_dump')(2);
id()()('var_dump')(4);
id(['udef', 'id'])[1]()('var_dump')(5);
(function($x) { return $x; })('id')('var_dump')(8);
($f = function($x = null) use (&$f) {
return $x ?: $f;
})()()()('var_dump')(9);
[$obj, 'id']()('id')($id)('var_dump')(10);
'id'()('id')('var_dump')(12);
('i' . 'd')()('var_dump')(13);
'\id'('var_dump')(14);
-----
!!php7
array(
0: Expr_FuncCall(
name: Expr_FuncCall(
name: Name(
parts: array(
0: id
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 1
)
byRef: false
unpack: false
)
)
)
1: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Name(
parts: array(
0: id
)
)
args: array(
0: Arg(
value: Scalar_String(
value: id
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 2
)
byRef: false
unpack: false
)
)
)
2: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Name(
parts: array(
0: id
)
)
args: array(
)
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 4
)
byRef: false
unpack: false
)
)
)
3: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_ArrayDimFetch(
var: Expr_FuncCall(
name: Name(
parts: array(
0: id
)
)
args: array(
0: Arg(
value: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_String(
value: udef
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_String(
value: id
)
byRef: false
)
)
)
byRef: false
unpack: false
)
)
)
dim: Scalar_LNumber(
value: 1
)
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 5
)
byRef: false
unpack: false
)
)
)
4: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: x
default: null
)
)
uses: array(
)
returnType: null
stmts: array(
0: Stmt_Return(
expr: Expr_Variable(
name: x
)
)
)
)
args: array(
0: Arg(
value: Scalar_String(
value: id
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 8
)
byRef: false
unpack: false
)
)
)
5: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_Assign(
var: Expr_Variable(
name: f
)
expr: Expr_Closure(
static: false
byRef: false
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: x
default: Expr_ConstFetch(
name: Name(
parts: array(
0: null
)
)
)
)
)
uses: array(
0: Expr_ClosureUse(
var: f
byRef: true
)
)
returnType: null
stmts: array(
0: Stmt_Return(
expr: Expr_Ternary(
cond: Expr_Variable(
name: x
)
if: null
else: Expr_Variable(
name: f
)
)
)
)
)
)
args: array(
)
)
args: array(
)
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 9
)
byRef: false
unpack: false
)
)
)
6: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Expr_Variable(
name: obj
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_String(
value: id
)
byRef: false
)
)
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: id
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: id
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 10
)
byRef: false
unpack: false
)
)
)
7: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Scalar_String(
value: id
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: id
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 12
)
byRef: false
unpack: false
)
)
)
8: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_FuncCall(
name: Expr_BinaryOp_Concat(
left: Scalar_String(
value: i
)
right: Scalar_String(
value: d
)
)
args: array(
)
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 13
)
byRef: false
unpack: false
)
)
)
9: Expr_FuncCall(
name: Expr_FuncCall(
name: Scalar_String(
value: \id
)
args: array(
0: Arg(
value: Scalar_String(
value: var_dump
)
byRef: false
unpack: false
)
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 14
)
byRef: false
unpack: false
)
)
)
)

View file

@ -0,0 +1,74 @@
UVS isset() on temporaries
-----
<?php
isset(([0, 1] + [])[0]);
isset(['a' => 'b']->a);
isset("str"->a);
-----
!!php7
array(
0: Expr_Isset(
vars: array(
0: Expr_ArrayDimFetch(
var: Expr_BinaryOp_Plus(
left: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 0
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
)
)
right: Expr_Array(
items: array(
)
)
)
dim: Scalar_LNumber(
value: 0
)
)
)
)
1: Expr_Isset(
vars: array(
0: Expr_PropertyFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: Scalar_String(
value: a
)
value: Scalar_String(
value: b
)
byRef: false
)
)
)
name: a
)
)
)
2: Expr_Isset(
vars: array(
0: Expr_PropertyFetch(
var: Scalar_String(
value: str
)
name: a
)
)
)
)

View file

@ -0,0 +1,109 @@
Uniform variable syntax in PHP 7 (misc)
-----
<?php
A::A[0];
A::A[0][1][2];
"string"->length();
(clone $obj)->b[0](1);
[0, 1][0] = 1;
-----
!!php7
array(
0: Expr_ArrayDimFetch(
var: Expr_ClassConstFetch(
class: Name(
parts: array(
0: A
)
)
name: A
)
dim: Scalar_LNumber(
value: 0
)
)
1: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ArrayDimFetch(
var: Expr_ClassConstFetch(
class: Name(
parts: array(
0: A
)
)
name: A
)
dim: Scalar_LNumber(
value: 0
)
)
dim: Scalar_LNumber(
value: 1
)
)
dim: Scalar_LNumber(
value: 2
)
)
2: Expr_MethodCall(
var: Scalar_String(
value: string
)
name: length
args: array(
)
)
3: Expr_FuncCall(
name: Expr_ArrayDimFetch(
var: Expr_PropertyFetch(
var: Expr_Clone(
expr: Expr_Variable(
name: obj
)
)
name: b
)
dim: Scalar_LNumber(
value: 0
)
)
args: array(
0: Arg(
value: Scalar_LNumber(
value: 1
)
byRef: false
unpack: false
)
)
)
4: Expr_Assign(
var: Expr_ArrayDimFetch(
var: Expr_Array(
items: array(
0: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 0
)
byRef: false
)
1: Expr_ArrayItem(
key: null
value: Scalar_LNumber(
value: 1
)
byRef: false
)
)
)
dim: Scalar_LNumber(
value: 0
)
)
expr: Scalar_LNumber(
value: 1
)
)
)

View file

@ -0,0 +1,95 @@
UVS new expressions
-----
<?php
new $className;
new $array['className'];
new $array{'className'};
new $obj->className;
new Test::$className;
new $test::$className;
new $weird[0]->foo::$className;
-----
!!php7
array(
0: Expr_New(
class: Expr_Variable(
name: className
)
args: array(
)
)
1: Expr_New(
class: Expr_ArrayDimFetch(
var: Expr_Variable(
name: array
)
dim: Scalar_String(
value: className
)
)
args: array(
)
)
2: Expr_New(
class: Expr_ArrayDimFetch(
var: Expr_Variable(
name: array
)
dim: Scalar_String(
value: className
)
)
args: array(
)
)
3: Expr_New(
class: Expr_PropertyFetch(
var: Expr_Variable(
name: obj
)
name: className
)
args: array(
)
)
4: Expr_New(
class: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: Test
)
)
name: className
)
args: array(
)
)
5: Expr_New(
class: Expr_StaticPropertyFetch(
class: Expr_Variable(
name: test
)
name: className
)
args: array(
)
)
6: Expr_New(
class: Expr_StaticPropertyFetch(
class: Expr_PropertyFetch(
var: Expr_ArrayDimFetch(
var: Expr_Variable(
name: weird
)
dim: Scalar_LNumber(
value: 0
)
)
name: foo
)
name: className
)
args: array(
)
)
)

View file

@ -0,0 +1,93 @@
UVS static access
-----
<?php
A::$b;
$A::$b;
'A'::$b;
('A' . '')::$b;
'A'[0]::$b;
A::$$b;
A::$$c[1];
A::$A::$b;
-----
!!php7
array(
0: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: b
)
1: Expr_StaticPropertyFetch(
class: Expr_Variable(
name: A
)
name: b
)
2: Expr_StaticPropertyFetch(
class: Scalar_String(
value: A
)
name: b
)
3: Expr_StaticPropertyFetch(
class: Expr_BinaryOp_Concat(
left: Scalar_String(
value: A
)
right: Scalar_String(
value:
)
)
name: b
)
4: Expr_StaticPropertyFetch(
class: Expr_ArrayDimFetch(
var: Scalar_String(
value: A
)
dim: Scalar_LNumber(
value: 0
)
)
name: b
)
5: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: Expr_Variable(
name: b
)
)
6: Expr_ArrayDimFetch(
var: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: Expr_Variable(
name: c
)
)
dim: Scalar_LNumber(
value: 1
)
)
7: Expr_StaticPropertyFetch(
class: Expr_StaticPropertyFetch(
class: Name(
parts: array(
0: A
)
)
name: A
)
name: b
)
)

View file

@ -0,0 +1,55 @@
Variable syntaxes
-----
<?php
$a;
${'a'};
${foo()};
$$a;
$$$a;
$$a['b'];
-----
!!php5
array(
0: Expr_Variable(
name: a
)
1: Expr_Variable(
name: Scalar_String(
value: a
)
)
2: Expr_Variable(
name: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
)
)
)
3: Expr_Variable(
name: Expr_Variable(
name: a
)
)
4: Expr_Variable(
name: Expr_Variable(
name: Expr_Variable(
name: a
)
)
)
5: Expr_Variable(
name: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: b
)
)
)
)

View file

@ -0,0 +1,60 @@
Constant string syntaxes
-----
<?php
'';
"";
b'';
b"";
'Hi';
b'Hi';
B'Hi';
"Hi";
b"Hi";
B"Hi";
'!\'!\\!\a!';
"!\"!\\!\$!\n!\r!\t!\f!\v!\e!\a";
"!\xFF!\377!\400!\0!";
-----
array(
0: Scalar_String(
value:
)
1: Scalar_String(
value:
)
2: Scalar_String(
value:
)
3: Scalar_String(
value:
)
4: Scalar_String(
value: Hi
)
5: Scalar_String(
value: Hi
)
6: Scalar_String(
value: Hi
)
7: Scalar_String(
value: Hi
)
8: Scalar_String(
value: Hi
)
9: Scalar_String(
value: Hi
)
10: Scalar_String(
value: !'!\!\a!
)
11: Scalar_String(
value: !"!\!$!
!@@{ "\r" }@@!@@{ "\t" }@@!@@{ "\f" }@@!@@{ "\v" }@@!@@{ chr(27) /* "\e" */ }@@!\a
)
12: Scalar_String(
value: !@@{ chr(255) }@@!@@{ chr(255) }@@!@@{ chr(0) }@@!@@{ chr(0) }@@!
)
)

View file

@ -0,0 +1,90 @@
Nowdoc and heredoc strings
-----
<?php
// empty strings
<<<'EOS'
EOS;
<<<EOS
EOS;
// constant encapsed strings
<<<'EOS'
Test '" $a \n
EOS;
<<<EOS
Test '" \$a \n
EOS;
// encapsed strings
<<<EOS
Test $a
EOS;
<<<EOS
Test $a and $b->c test
EOS;
b<<<EOS
Binary
EOS;
-----
array(
0: Scalar_String(
value:
comments: array(
0: // empty strings
)
)
1: Scalar_String(
value:
)
2: Scalar_String(
value: Test '" $a \n
comments: array(
0: // constant encapsed strings
)
)
3: Scalar_String(
value: Test '" $a
)
4: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: Test
)
1: Expr_Variable(
name: a
)
)
comments: array(
0: // encapsed strings
)
)
5: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: Test
)
1: Expr_Variable(
name: a
)
2: Scalar_EncapsedStringPart(
value: and
)
3: Expr_PropertyFetch(
var: Expr_Variable(
name: b
)
name: c
)
4: Scalar_EncapsedStringPart(
value: test
)
)
)
6: Scalar_String(
value: Binary
)
)

View file

@ -0,0 +1,61 @@
Trailing newlines in doc strings
-----
<?php
<<<'EOF'@@{ "\n\n" }@@EOF;
<<<'EOF'@@{ "\n\n\n" }@@EOF;
<<<'EOF'@@{ "\nFoo\n\n" }@@EOF;
<<<EOF@@{ "\n\$var\n\n" }@@EOF;
<<<'EOF'@@{ "\r\n\r\n" }@@EOF;
<<<'EOF'@@{ "\r\n\r\n\r\n" }@@EOF;
<<<'EOF'@@{ "\r\nFoo\r\n\r\n" }@@EOF;
<<<EOF@@{ "\r\n\$var\r\n\r\n" }@@EOF;
-----
array(
0: Scalar_String(
value:
)
1: Scalar_String(
value:
)
2: Scalar_String(
value: Foo
)
3: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: var
)
1: Scalar_EncapsedStringPart(
value:
)
)
)
4: Scalar_String(
value:
)
5: Scalar_String(
value:
)
6: Scalar_String(
value: Foo
)
7: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: var
)
1: Scalar_EncapsedStringPart(
value:
)
)
)
)

View file

@ -0,0 +1,72 @@
Encapsed string negative var offsets
-----
<?php
"$a[-0]";
"$a[-1]";
"$a[-0x0]";
"$a[-00]";
"$a[@@{ -PHP_INT_MAX - 1 }@@]";
-----
!!php7
array(
0: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: -0
)
)
)
)
1: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_LNumber(
value: -1
)
)
)
)
2: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: -0x0
)
)
)
)
3: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_String(
value: -00
)
)
)
)
4: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: a
)
dim: Scalar_LNumber(
value: @@{ -PHP_INT_MAX - 1 }@@
)
)
)
)
)

View file

@ -0,0 +1,294 @@
Encapsed strings
-----
<?php
"$A";
"$A->B";
"$A[B]";
"$A[0]";
"$A[1234]";
"$A[9223372036854775808]";
"$A[000]";
"$A[0x0]";
"$A[0b0]";
"$A[$B]";
"{$A}";
"{$A['B']}";
"${A}";
"${A['B']}";
"${$A}";
"\{$A}";
"\{ $A }";
"\\{$A}";
"\\{ $A }";
"{$$A}[B]";
"$$A[B]";
"A $B C";
b"$A";
B"$A";
-----
array(
0: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: A
)
)
)
1: Scalar_Encapsed(
parts: array(
0: Expr_PropertyFetch(
var: Expr_Variable(
name: A
)
name: B
)
)
)
2: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: B
)
)
)
)
3: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_LNumber(
value: 0
)
)
)
)
4: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_LNumber(
value: 1234
)
)
)
)
5: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: 9223372036854775808
)
)
)
)
6: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: 000
)
)
)
)
7: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: 0x0
)
)
)
)
8: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: 0b0
)
)
)
)
9: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Expr_Variable(
name: B
)
)
)
)
10: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: A
)
)
)
11: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: B
)
)
)
)
12: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: A
)
)
)
13: Scalar_Encapsed(
parts: array(
0: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: B
)
)
)
)
14: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: Expr_Variable(
name: A
)
)
)
)
15: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: \{
)
1: Expr_Variable(
name: A
)
2: Scalar_EncapsedStringPart(
value: }
)
)
)
16: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: \{
)
1: Expr_Variable(
name: A
)
2: Scalar_EncapsedStringPart(
value: }
)
)
)
17: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: \
)
1: Expr_Variable(
name: A
)
)
)
18: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: \{
)
1: Expr_Variable(
name: A
)
2: Scalar_EncapsedStringPart(
value: }
)
)
)
19: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: Expr_Variable(
name: A
)
)
1: Scalar_EncapsedStringPart(
value: [B]
)
)
)
20: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: $
)
1: Expr_ArrayDimFetch(
var: Expr_Variable(
name: A
)
dim: Scalar_String(
value: B
)
)
)
)
21: Scalar_Encapsed(
parts: array(
0: Scalar_EncapsedStringPart(
value: A
)
1: Expr_Variable(
name: B
)
2: Scalar_EncapsedStringPart(
value: C
)
)
)
22: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: A
)
)
)
23: Scalar_Encapsed(
parts: array(
0: Expr_Variable(
name: A
)
)
)
)

View file

@ -0,0 +1,74 @@
Different float syntaxes
-----
<?php
0.0;
0.;
.0;
0e0;
0E0;
0e+0;
0e-0;
30.20e10;
300.200e100;
1e10000;
// various integer -> float overflows
// (all are actually the same number, just in different representations)
18446744073709551615;
0xFFFFFFFFFFFFFFFF;
01777777777777777777777;
0177777777777777777777787;
0b1111111111111111111111111111111111111111111111111111111111111111;
-----
array(
0: Scalar_DNumber(
value: 0
)
1: Scalar_DNumber(
value: 0
)
2: Scalar_DNumber(
value: 0
)
3: Scalar_DNumber(
value: 0
)
4: Scalar_DNumber(
value: 0
)
5: Scalar_DNumber(
value: 0
)
6: Scalar_DNumber(
value: 0
)
7: Scalar_DNumber(
value: 302000000000
)
8: Scalar_DNumber(
value: 3.002E+102
)
9: Scalar_DNumber(
value: INF
)
10: Scalar_DNumber(
value: 1.844674407371E+19
comments: array(
0: // various integer -> float overflows
1: // (all are actually the same number, just in different representations)
)
)
11: Scalar_DNumber(
value: 1.844674407371E+19
)
12: Scalar_DNumber(
value: 1.844674407371E+19
)
13: Scalar_DNumber(
value: 1.844674407371E+19
)
14: Scalar_DNumber(
value: 1.844674407371E+19
)
)

View file

@ -0,0 +1,43 @@
Different integer syntaxes
-----
<?php
0;
1;
@@{ PHP_INT_MAX }@@;
@@{ PHP_INT_MAX + 1 }@@;
0xFFF;
0xfff;
0XfFf;
0777;
0b111000111000;
-----
array(
0: Scalar_LNumber(
value: 0
)
1: Scalar_LNumber(
value: 1
)
2: Scalar_LNumber(
value: @@{ PHP_INT_MAX }@@
)
3: Scalar_DNumber(
value: @@{ PHP_INT_MAX + 1 }@@
)
4: Scalar_LNumber(
value: 4095
)
5: Scalar_LNumber(
value: 4095
)
6: Scalar_LNumber(
value: 4095
)
7: Scalar_LNumber(
value: 511
)
8: Scalar_LNumber(
value: 3640
)
)

View file

@ -0,0 +1,22 @@
Invalid octal literals
-----
<?php
0787;
-----
!!php7
Invalid numeric literal from 2:1 to 2:4
array(
0: Scalar_LNumber(
value: 0
)
)
-----
<?php
0787;
-----
!!php5
array(
0: Scalar_LNumber(
value: 7
)
)

View file

@ -0,0 +1,31 @@
Magic constants
-----
<?php
__CLASS__;
__DIR__;
__FILE__;
__FUNCTION__;
__LINE__;
__METHOD__;
__NAMESPACE__;
__TRAIT__;
-----
array(
0: Scalar_MagicConst_Class(
)
1: Scalar_MagicConst_Dir(
)
2: Scalar_MagicConst_File(
)
3: Scalar_MagicConst_Function(
)
4: Scalar_MagicConst_Line(
)
5: Scalar_MagicConst_Method(
)
6: Scalar_MagicConst_Namespace(
)
7: Scalar_MagicConst_Trait(
)
)

View file

@ -0,0 +1,20 @@
Unicode escape sequence
-----
<?php
"\u{0}";
"\u{114}";
"\u{1F602}";
-----
!!php7
array(
0: Scalar_String(
value: @@{"\0"}@@
)
1: Scalar_String(
value: Ĕ
)
2: Scalar_String(
value: @@{"\xF0\x9F\x98\x82"}@@
)
)

View file

@ -0,0 +1,382 @@
Valid usages of reserved keywords as identifiers
-----
<?php
class Test {
function array() {}
function public() {}
static function list() {}
static function protected() {}
public $class;
public $private;
const TRAIT = 3, FINAL = 4;
const __CLASS__ = 1, __TRAIT__ = 2, __FUNCTION__ = 3, __METHOD__ = 4, __LINE__ = 5,
__FILE__ = 6, __DIR__ = 7, __NAMESPACE__ = 8;
// __halt_compiler does not work
}
$t = new Test;
$t->array();
$t->public();
Test::list();
Test::protected();
$t->class;
$t->private;
Test::TRAIT;
Test::FINAL;
class Foo {
use TraitA, TraitB {
TraitA::catch insteadof namespace\TraitB;
TraitA::list as foreach;
TraitB::throw as protected public;
TraitB::self as protected;
exit as die;
\TraitC::exit as bye;
namespace\TraitC::exit as byebye;
TraitA::
//
/** doc comment */
#
catch /* comment */
// comment
# comment
insteadof TraitB;
}
}
-----
array(
0: Stmt_Class(
flags: 0
name: Test
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassMethod(
flags: 0
byRef: false
name: array
params: array(
)
returnType: null
stmts: array(
)
)
1: Stmt_ClassMethod(
flags: 0
byRef: false
name: public
params: array(
)
returnType: null
stmts: array(
)
)
2: Stmt_ClassMethod(
flags: MODIFIER_STATIC (8)
byRef: false
name: list
params: array(
)
returnType: null
stmts: array(
)
)
3: Stmt_ClassMethod(
flags: MODIFIER_STATIC (8)
byRef: false
name: protected
params: array(
)
returnType: null
stmts: array(
)
)
4: Stmt_Property(
flags: MODIFIER_PUBLIC (1)
props: array(
0: Stmt_PropertyProperty(
name: class
default: null
)
)
)
5: Stmt_Property(
flags: MODIFIER_PUBLIC (1)
props: array(
0: Stmt_PropertyProperty(
name: private
default: null
)
)
)
6: Stmt_ClassConst(
flags: 0
consts: array(
0: Const(
name: TRAIT
value: Scalar_LNumber(
value: 3
)
)
1: Const(
name: FINAL
value: Scalar_LNumber(
value: 4
)
)
)
)
7: Stmt_ClassConst(
flags: 0
consts: array(
0: Const(
name: __CLASS__
value: Scalar_LNumber(
value: 1
)
)
1: Const(
name: __TRAIT__
value: Scalar_LNumber(
value: 2
)
)
2: Const(
name: __FUNCTION__
value: Scalar_LNumber(
value: 3
)
)
3: Const(
name: __METHOD__
value: Scalar_LNumber(
value: 4
)
)
4: Const(
name: __LINE__
value: Scalar_LNumber(
value: 5
)
)
5: Const(
name: __FILE__
value: Scalar_LNumber(
value: 6
)
)
6: Const(
name: __DIR__
value: Scalar_LNumber(
value: 7
)
)
7: Const(
name: __NAMESPACE__
value: Scalar_LNumber(
value: 8
)
)
)
)
)
)
1: Expr_Assign(
var: Expr_Variable(
name: t
)
expr: Expr_New(
class: Name(
parts: array(
0: Test
)
)
args: array(
)
)
)
2: Expr_MethodCall(
var: Expr_Variable(
name: t
)
name: array
args: array(
)
)
3: Expr_MethodCall(
var: Expr_Variable(
name: t
)
name: public
args: array(
)
)
4: Expr_StaticCall(
class: Name(
parts: array(
0: Test
)
)
name: list
args: array(
)
)
5: Expr_StaticCall(
class: Name(
parts: array(
0: Test
)
)
name: protected
args: array(
)
)
6: Expr_PropertyFetch(
var: Expr_Variable(
name: t
)
name: class
)
7: Expr_PropertyFetch(
var: Expr_Variable(
name: t
)
name: private
)
8: Expr_ClassConstFetch(
class: Name(
parts: array(
0: Test
)
)
name: TRAIT
)
9: Expr_ClassConstFetch(
class: Name(
parts: array(
0: Test
)
)
name: FINAL
)
10: Stmt_Class(
flags: 0
name: Foo
extends: null
implements: array(
)
stmts: array(
0: Stmt_TraitUse(
traits: array(
0: Name(
parts: array(
0: TraitA
)
)
1: Name(
parts: array(
0: TraitB
)
)
)
adaptations: array(
0: Stmt_TraitUseAdaptation_Precedence(
trait: Name(
parts: array(
0: TraitA
)
)
method: catch
insteadof: array(
0: Name_Relative(
parts: array(
0: TraitB
)
)
)
)
1: Stmt_TraitUseAdaptation_Alias(
trait: Name(
parts: array(
0: TraitA
)
)
method: list
newModifier: null
newName: foreach
)
2: Stmt_TraitUseAdaptation_Alias(
trait: Name(
parts: array(
0: TraitB
)
)
method: throw
newModifier: MODIFIER_PROTECTED (2)
newName: public
)
3: Stmt_TraitUseAdaptation_Alias(
trait: Name(
parts: array(
0: TraitB
)
)
method: self
newModifier: MODIFIER_PROTECTED (2)
newName: null
)
4: Stmt_TraitUseAdaptation_Alias(
trait: null
method: exit
newModifier: null
newName: die
)
5: Stmt_TraitUseAdaptation_Alias(
trait: Name_FullyQualified(
parts: array(
0: TraitC
)
)
method: exit
newModifier: null
newName: bye
)
6: Stmt_TraitUseAdaptation_Alias(
trait: Name_Relative(
parts: array(
0: TraitC
)
)
method: exit
newModifier: null
newName: byebye
)
7: Stmt_TraitUseAdaptation_Precedence(
trait: Name(
parts: array(
0: TraitA
)
)
method: catch
insteadof: array(
0: Name(
parts: array(
0: TraitB
)
)
)
)
)
)
)
)
)

View file

@ -0,0 +1,112 @@
Blockless statements for if/for/etc
-----
<?php
if ($a) $A;
elseif ($b) $B;
else $C;
for (;;) $foo;
foreach ($a as $b) $AB;
while ($a) $A;
do $A; while ($a);
declare (a='b') $C;
-----
array(
0: Stmt_If(
cond: Expr_Variable(
name: a
)
stmts: array(
0: Expr_Variable(
name: A
)
)
elseifs: array(
0: Stmt_ElseIf(
cond: Expr_Variable(
name: b
)
stmts: array(
0: Expr_Variable(
name: B
)
)
)
)
else: Stmt_Else(
stmts: array(
0: Expr_Variable(
name: C
)
)
)
)
1: Stmt_For(
init: array(
)
cond: array(
)
loop: array(
)
stmts: array(
0: Expr_Variable(
name: foo
)
)
)
2: Stmt_Foreach(
expr: Expr_Variable(
name: a
)
keyVar: null
byRef: false
valueVar: Expr_Variable(
name: b
)
stmts: array(
0: Expr_Variable(
name: AB
)
)
)
3: Stmt_While(
cond: Expr_Variable(
name: a
)
stmts: array(
0: Expr_Variable(
name: A
)
)
)
4: Stmt_Do(
cond: Expr_Variable(
name: a
)
stmts: array(
0: Expr_Variable(
name: A
)
)
)
5: Stmt_Declare(
declares: array(
0: Stmt_DeclareDeclare(
key: a
value: Scalar_String(
value: b
)
)
)
stmts: array(
0: Expr_Variable(
name: C
)
)
)
)

View file

@ -0,0 +1,39 @@
Abstract class
-----
<?php
abstract class A {
public function a() {}
abstract public function b();
}
-----
array(
0: Stmt_Class(
flags: MODIFIER_ABSTRACT (16)
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassMethod(
flags: MODIFIER_PUBLIC (1)
byRef: false
name: a
params: array(
)
returnType: null
stmts: array(
)
)
1: Stmt_ClassMethod(
flags: MODIFIER_PUBLIC | MODIFIER_ABSTRACT (17)
byRef: false
name: b
params: array(
)
returnType: null
stmts: null
)
)
)
)

View file

@ -0,0 +1,195 @@
Anonymous classes
-----
<?php
new class {
public function test() {}
};
new class extends A implements B, C {};
new class() {
public $foo;
};
new class($a, $b) extends A {
use T;
};
class A {
public function test() {
return new class($this) extends A {
const A = 'B';
};
}
}
-----
array(
0: Expr_New(
class: Stmt_Class(
flags: 0
name: null
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassMethod(
flags: MODIFIER_PUBLIC (1)
byRef: false
name: test
params: array(
)
returnType: null
stmts: array(
)
)
)
)
args: array(
)
)
1: Expr_New(
class: Stmt_Class(
flags: 0
name: null
extends: Name(
parts: array(
0: A
)
)
implements: array(
0: Name(
parts: array(
0: B
)
)
1: Name(
parts: array(
0: C
)
)
)
stmts: array(
)
)
args: array(
)
)
2: Expr_New(
class: Stmt_Class(
flags: 0
name: null
extends: null
implements: array(
)
stmts: array(
0: Stmt_Property(
flags: MODIFIER_PUBLIC (1)
props: array(
0: Stmt_PropertyProperty(
name: foo
default: null
)
)
)
)
)
args: array(
)
)
3: Expr_New(
class: Stmt_Class(
flags: 0
name: null
extends: Name(
parts: array(
0: A
)
)
implements: array(
)
stmts: array(
0: Stmt_TraitUse(
traits: array(
0: Name(
parts: array(
0: T
)
)
)
adaptations: array(
)
)
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: a
)
byRef: false
unpack: false
)
1: Arg(
value: Expr_Variable(
name: b
)
byRef: false
unpack: false
)
)
)
4: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassMethod(
flags: MODIFIER_PUBLIC (1)
byRef: false
name: test
params: array(
)
returnType: null
stmts: array(
0: Stmt_Return(
expr: Expr_New(
class: Stmt_Class(
flags: 0
name: null
extends: Name(
parts: array(
0: A
)
)
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: 0
consts: array(
0: Const(
name: A
value: Scalar_String(
value: B
)
)
)
)
)
)
args: array(
0: Arg(
value: Expr_Variable(
name: this
)
byRef: false
unpack: false
)
)
)
)
)
)
)
)
)

View file

@ -0,0 +1,33 @@
Conditional class definition
-----
<?php
if (true) {
class A {}
}
-----
array(
0: Stmt_If(
cond: Expr_ConstFetch(
name: Name(
parts: array(
0: true
)
)
)
stmts: array(
0: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
)
)
)
elseifs: array(
)
else: null
)
)

View file

@ -0,0 +1,121 @@
Invalid class constant modifiers
-----
<?php
class A {
static const X = 1;
}
-----
!!php7
Cannot use 'static' as constant modifier from 3:5 to 3:10
array(
0: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: MODIFIER_STATIC (8)
consts: array(
0: Const(
name: X
value: Scalar_LNumber(
value: 1
)
)
)
)
)
)
)
-----
<?php
class A {
abstract const X = 1;
}
-----
!!php7
Cannot use 'abstract' as constant modifier from 3:5 to 3:12
array(
0: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: MODIFIER_ABSTRACT (16)
consts: array(
0: Const(
name: X
value: Scalar_LNumber(
value: 1
)
)
)
)
)
)
)
-----
<?php
class A {
final const X = 1;
}
-----
!!php7
Cannot use 'final' as constant modifier from 3:5 to 3:9
array(
0: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: MODIFIER_FINAL (32)
consts: array(
0: Const(
name: X
value: Scalar_LNumber(
value: 1
)
)
)
)
)
)
)
-----
<?php
class A {
public public const X = 1;
}
-----
!!php7
Multiple access type modifiers are not allowed from 3:12 to 3:17
array(
0: Stmt_Class(
flags: 0
name: A
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: MODIFIER_PUBLIC (1)
consts: array(
0: Const(
name: X
value: Scalar_LNumber(
value: 1
)
)
)
)
)
)
)

View file

@ -0,0 +1,67 @@
Class constant modifiers
-----
<?php
class Foo {
const A = 1;
public const B = 2;
protected const C = 3;
private const D = 4;
}
-----
!!php7
array(
0: Stmt_Class(
flags: 0
name: Foo
extends: null
implements: array(
)
stmts: array(
0: Stmt_ClassConst(
flags: 0
consts: array(
0: Const(
name: A
value: Scalar_LNumber(
value: 1
)
)
)
)
1: Stmt_ClassConst(
flags: MODIFIER_PUBLIC (1)
consts: array(
0: Const(
name: B
value: Scalar_LNumber(
value: 2
)
)
)
)
2: Stmt_ClassConst(
flags: MODIFIER_PROTECTED (2)
consts: array(
0: Const(
name: C
value: Scalar_LNumber(
value: 3
)
)
)
)
3: Stmt_ClassConst(
flags: MODIFIER_PRIVATE (4)
consts: array(
0: Const(
name: D
value: Scalar_LNumber(
value: 4
)
)
)
)
)
)
)

View file

@ -0,0 +1,17 @@
Final class
-----
<?php
final class A {}
-----
array(
0: Stmt_Class(
flags: MODIFIER_FINAL (32)
name: A
extends: null
implements: array(
)
stmts: array(
)
)
)

Some files were not shown because too many files have changed in this diff Show more