Перенес код

This commit is contained in:
2024-09-03 20:16:34 +03:00
parent 88d85865a0
commit 5344b31f97
1716 changed files with 146261 additions and 6896 deletions

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface Builder {
/**
* Returns the built node.
*
* @return Node The built node
*/
public function getNode(): Node;
}

View File

@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Const_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
class ClassConst implements PhpParser\Builder {
protected int $flags = 0;
/** @var array<string, mixed> */
protected array $attributes = [];
/** @var list<Const_> */
protected array $constants = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType|null */
protected ?Node $type = null;
/**
* Creates a class constant builder
*
* @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value
*/
public function __construct($name, $value) {
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
}
/**
* Add another constant to const group
*
* @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value
*
* @return $this The builder instance (for fluid interface)
*/
public function addConst($name, $value) {
$this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
return $this;
}
/**
* Makes the constant public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this;
}
/**
* Makes the constant protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this;
}
/**
* Makes the constant private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this;
}
/**
* Makes the constant final.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this;
}
/**
* Sets doc comment for the constant.
*
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
*
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes = [
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
];
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Sets the constant type.
*
* @param string|Node\Name|Identifier|Node\ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/**
* Returns the built class node.
*
* @return Stmt\ClassConst The built constant node
*/
public function getNode(): PhpParser\Node {
return new Stmt\ClassConst(
$this->constants,
$this->flags,
$this->attributes,
$this->attributeGroups,
$this->type
);
}
}

View File

@ -0,0 +1,151 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class Class_ extends Declaration {
protected string $name;
protected ?Name $extends = null;
/** @var list<Name> */
protected array $implements = [];
protected int $flags = 0;
/** @var list<Stmt\TraitUse> */
protected array $uses = [];
/** @var list<Stmt\ClassConst> */
protected array $constants = [];
/** @var list<Stmt\Property> */
protected array $properties = [];
/** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates a class builder.
*
* @param string $name Name of the class
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Extends a class.
*
* @param Name|string $class Name of class to extend
*
* @return $this The builder instance (for fluid interface)
*/
public function extend($class) {
$this->extends = BuilderHelpers::normalizeName($class);
return $this;
}
/**
* Implements one or more interfaces.
*
* @param Name|string ...$interfaces Names of interfaces to implement
*
* @return $this The builder instance (for fluid interface)
*/
public function implement(...$interfaces) {
foreach ($interfaces as $interface) {
$this->implements[] = BuilderHelpers::normalizeName($interface);
}
return $this;
}
/**
* Makes the class abstract.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeAbstract() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT);
return $this;
}
/**
* Makes the class final.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL);
return $this;
}
/**
* Makes the class readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY);
return $this;
}
/**
* Adds a statement.
*
* @param Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt);
if ($stmt instanceof Stmt\Property) {
$this->properties[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassMethod) {
$this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built class node.
*
* @return Stmt\Class_ The built class node
*/
public function getNode(): PhpParser\Node {
return new Stmt\Class_($this->name, [
'flags' => $this->flags,
'extends' => $this->extends,
'implements' => $this->implements,
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
'attrGroups' => $this->attributeGroups,
], $this->attributes);
}
}

View File

@ -0,0 +1,50 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
abstract class Declaration implements PhpParser\Builder {
/** @var array<string, mixed> */
protected array $attributes = [];
/**
* Adds a statement.
*
* @param PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
abstract public function addStmt($stmt);
/**
* Adds multiple statements.
*
* @param (PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmts(array $stmts) {
foreach ($stmts as $stmt) {
$this->addStmt($stmt);
}
return $this;
}
/**
* Sets doc comment for the declaration.
*
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
*
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes['comments'] = [
BuilderHelpers::normalizeDocComment($docComment)
];
return $this;
}
}

View File

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
class EnumCase implements PhpParser\Builder {
/** @var Identifier|string */
protected $name;
/** @var ?Node\Expr */
protected ?Node\Expr $value = null;
/** @var array<string, mixed> */
protected array $attributes = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates an enum case builder.
*
* @param string|Identifier $name Name
*/
public function __construct($name) {
$this->name = $name;
}
/**
* Sets the value.
*
* @param Node\Expr|string|int $value
*
* @return $this
*/
public function setValue($value) {
$this->value = BuilderHelpers::normalizeValue($value);
return $this;
}
/**
* Sets doc comment for the constant.
*
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
*
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes = [
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
];
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built enum case node.
*
* @return Stmt\EnumCase The built constant node
*/
public function getNode(): PhpParser\Node {
return new Stmt\EnumCase(
$this->name,
$this->value,
$this->attributeGroups,
$this->attributes
);
}
}

View File

@ -0,0 +1,116 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class Enum_ extends Declaration {
protected string $name;
protected ?Identifier $scalarType = null;
/** @var list<Name> */
protected array $implements = [];
/** @var list<Stmt\TraitUse> */
protected array $uses = [];
/** @var list<Stmt\EnumCase> */
protected array $enumCases = [];
/** @var list<Stmt\ClassConst> */
protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates an enum builder.
*
* @param string $name Name of the enum
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Sets the scalar type.
*
* @param string|Identifier $scalarType
*
* @return $this
*/
public function setScalarType($scalarType) {
$this->scalarType = BuilderHelpers::normalizeType($scalarType);
return $this;
}
/**
* Implements one or more interfaces.
*
* @param Name|string ...$interfaces Names of interfaces to implement
*
* @return $this The builder instance (for fluid interface)
*/
public function implement(...$interfaces) {
foreach ($interfaces as $interface) {
$this->implements[] = BuilderHelpers::normalizeName($interface);
}
return $this;
}
/**
* Adds a statement.
*
* @param Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt);
if ($stmt instanceof Stmt\EnumCase) {
$this->enumCases[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassMethod) {
$this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built class node.
*
* @return Stmt\Enum_ The built enum node
*/
public function getNode(): PhpParser\Node {
return new Stmt\Enum_($this->name, [
'scalarType' => $this->scalarType,
'implements' => $this->implements,
'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods),
'attrGroups' => $this->attributeGroups,
], $this->attributes);
}
}

View File

@ -0,0 +1,73 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
abstract class FunctionLike extends Declaration {
protected bool $returnByRef = false;
/** @var Node\Param[] */
protected array $params = [];
/** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected ?Node $returnType = null;
/**
* Make the function return by reference.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReturnByRef() {
$this->returnByRef = true;
return $this;
}
/**
* Adds a parameter.
*
* @param Node\Param|Param $param The parameter to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addParam($param) {
$param = BuilderHelpers::normalizeNode($param);
if (!$param instanceof Node\Param) {
throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
}
$this->params[] = $param;
return $this;
}
/**
* Adds multiple parameters.
*
* @param (Node\Param|Param)[] $params The parameters to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addParams(array $params) {
foreach ($params as $param) {
$this->addParam($param);
}
return $this;
}
/**
* Sets the return type for PHP 7.
*
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type
*
* @return $this The builder instance (for fluid interface)
*/
public function setReturnType($type) {
$this->returnType = BuilderHelpers::normalizeType($type);
return $this;
}
}

View File

@ -0,0 +1,67 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Function_ extends FunctionLike {
protected string $name;
/** @var list<Stmt> */
protected array $stmts = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates a function builder.
*
* @param string $name Name of the function
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Adds a statement.
*
* @param Node|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built function node.
*
* @return Stmt\Function_ The built function node
*/
public function getNode(): Node {
return new Stmt\Function_($this->name, [
'byRef' => $this->returnByRef,
'params' => $this->params,
'returnType' => $this->returnType,
'stmts' => $this->stmts,
'attrGroups' => $this->attributeGroups,
], $this->attributes);
}
}

View File

@ -0,0 +1,94 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class Interface_ extends Declaration {
protected string $name;
/** @var list<Name> */
protected array $extends = [];
/** @var list<Stmt\ClassConst> */
protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates an interface builder.
*
* @param string $name Name of the interface
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Extends one or more interfaces.
*
* @param Name|string ...$interfaces Names of interfaces to extend
*
* @return $this The builder instance (for fluid interface)
*/
public function extend(...$interfaces) {
foreach ($interfaces as $interface) {
$this->extends[] = BuilderHelpers::normalizeName($interface);
}
return $this;
}
/**
* Adds a statement.
*
* @param Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt);
if ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassMethod) {
// we erase all statements in the body of an interface method
$stmt->stmts = null;
$this->methods[] = $stmt;
} else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built interface node.
*
* @return Stmt\Interface_ The built interface node
*/
public function getNode(): PhpParser\Node {
return new Stmt\Interface_($this->name, [
'extends' => $this->extends,
'stmts' => array_merge($this->constants, $this->methods),
'attrGroups' => $this->attributeGroups,
], $this->attributes);
}
}

View File

@ -0,0 +1,147 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Method extends FunctionLike {
protected string $name;
protected int $flags = 0;
/** @var list<Stmt>|null */
protected ?array $stmts = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates a method builder.
*
* @param string $name Name of the method
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Makes the method public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this;
}
/**
* Makes the method protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this;
}
/**
* Makes the method private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this;
}
/**
* Makes the method static.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this;
}
/**
* Makes the method abstract.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeAbstract() {
if (!empty($this->stmts)) {
throw new \LogicException('Cannot make method with statements abstract');
}
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
$this->stmts = null; // abstract methods don't have statements
return $this;
}
/**
* Makes the method final.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this;
}
/**
* Adds a statement.
*
* @param Node|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
if (null === $this->stmts) {
throw new \LogicException('Cannot add statements to an abstract method');
}
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built method node.
*
* @return Stmt\ClassMethod The built method node
*/
public function getNode(): Node {
return new Stmt\ClassMethod($this->name, [
'flags' => $this->flags,
'byRef' => $this->returnByRef,
'params' => $this->params,
'returnType' => $this->returnType,
'stmts' => $this->stmts,
'attrGroups' => $this->attributeGroups,
], $this->attributes);
}
}

View File

@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Namespace_ extends Declaration {
private ?Node\Name $name;
/** @var Stmt[] */
private array $stmts = [];
/**
* Creates a namespace builder.
*
* @param Node\Name|string|null $name Name of the namespace
*/
public function __construct($name) {
$this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
}
/**
* Adds a statement.
*
* @param Node|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
/**
* Returns the built node.
*
* @return Stmt\Namespace_ The built node
*/
public function getNode(): Node {
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
}
}

View File

@ -0,0 +1,149 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
class Param implements PhpParser\Builder {
protected string $name;
protected ?Node\Expr $default = null;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected ?Node $type = null;
protected bool $byRef = false;
protected int $flags = 0;
protected bool $variadic = false;
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates a parameter builder.
*
* @param string $name Name of the parameter
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Sets default value for the parameter.
*
* @param mixed $value Default value to use
*
* @return $this The builder instance (for fluid interface)
*/
public function setDefault($value) {
$this->default = BuilderHelpers::normalizeValue($value);
return $this;
}
/**
* Sets type for the parameter.
*
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
*
* @return $this The builder instance (for fluid interface)
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
if ($this->type == 'void') {
throw new \LogicException('Parameter type cannot be void');
}
return $this;
}
/**
* Make the parameter accept the value by reference.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeByRef() {
$this->byRef = true;
return $this;
}
/**
* Make the parameter variadic
*
* @return $this The builder instance (for fluid interface)
*/
public function makeVariadic() {
$this->variadic = true;
return $this;
}
/**
* Makes the (promoted) parameter public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this;
}
/**
* Makes the (promoted) parameter protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this;
}
/**
* Makes the (promoted) parameter private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this;
}
/**
* Makes the (promoted) parameter readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built parameter node.
*
* @return Node\Param The built parameter node
*/
public function getNode(): Node {
return new Node\Param(
new Node\Expr\Variable($this->name),
$this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
);
}
}

View File

@ -0,0 +1,161 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\ComplexType;
class Property implements PhpParser\Builder {
protected string $name;
protected int $flags = 0;
protected ?Node\Expr $default = null;
/** @var array<string, mixed> */
protected array $attributes = [];
/** @var null|Identifier|Name|ComplexType */
protected ?Node $type = null;
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates a property builder.
*
* @param string $name Name of the property
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Makes the property public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this;
}
/**
* Makes the property protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this;
}
/**
* Makes the property private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this;
}
/**
* Makes the property static.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this;
}
/**
* Makes the property readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this;
}
/**
* Sets default value for the property.
*
* @param mixed $value Default value to use
*
* @return $this The builder instance (for fluid interface)
*/
public function setDefault($value) {
$this->default = BuilderHelpers::normalizeValue($value);
return $this;
}
/**
* Sets doc comment for the property.
*
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
*
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes = [
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
];
return $this;
}
/**
* Sets the property type for PHP 7.4+.
*
* @param string|Name|Identifier|ComplexType $type
*
* @return $this
*/
public function setType($type) {
$this->type = BuilderHelpers::normalizeType($type);
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built class node.
*
* @return Stmt\Property The built property node
*/
public function getNode(): PhpParser\Node {
return new Stmt\Property(
$this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
[
new Node\PropertyItem($this->name, $this->default)
],
$this->attributes,
$this->type,
$this->attributeGroups
);
}
}

View File

@ -0,0 +1,65 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class TraitUse implements Builder {
/** @var Node\Name[] */
protected array $traits = [];
/** @var Stmt\TraitUseAdaptation[] */
protected array $adaptations = [];
/**
* Creates a trait use builder.
*
* @param Node\Name|string ...$traits Names of used traits
*/
public function __construct(...$traits) {
foreach ($traits as $trait) {
$this->and($trait);
}
}
/**
* Adds used trait.
*
* @param Node\Name|string $trait Trait name
*
* @return $this The builder instance (for fluid interface)
*/
public function and($trait) {
$this->traits[] = BuilderHelpers::normalizeName($trait);
return $this;
}
/**
* Adds trait adaptation.
*
* @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation
*
* @return $this The builder instance (for fluid interface)
*/
public function with($adaptation) {
$adaptation = BuilderHelpers::normalizeNode($adaptation);
if (!$adaptation instanceof Stmt\TraitUseAdaptation) {
throw new \LogicException('Adaptation must have type TraitUseAdaptation');
}
$this->adaptations[] = $adaptation;
return $this;
}
/**
* Returns the built node.
*
* @return Node The built node
*/
public function getNode(): Node {
return new Stmt\TraitUse($this->traits, $this->adaptations);
}
}

View File

@ -0,0 +1,145 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class TraitUseAdaptation implements Builder {
private const TYPE_UNDEFINED = 0;
private const TYPE_ALIAS = 1;
private const TYPE_PRECEDENCE = 2;
protected int $type;
protected ?Node\Name $trait;
protected Node\Identifier $method;
protected ?int $modifier = null;
protected ?Node\Identifier $alias = null;
/** @var Node\Name[] */
protected array $insteadof = [];
/**
* Creates a trait use adaptation builder.
*
* @param Node\Name|string|null $trait Name of adapted trait
* @param Node\Identifier|string $method Name of adapted method
*/
public function __construct($trait, $method) {
$this->type = self::TYPE_UNDEFINED;
$this->trait = is_null($trait) ? null : BuilderHelpers::normalizeName($trait);
$this->method = BuilderHelpers::normalizeIdentifier($method);
}
/**
* Sets alias of method.
*
* @param Node\Identifier|string $alias Alias for adapted method
*
* @return $this The builder instance (for fluid interface)
*/
public function as($alias) {
if ($this->type === self::TYPE_UNDEFINED) {
$this->type = self::TYPE_ALIAS;
}
if ($this->type !== self::TYPE_ALIAS) {
throw new \LogicException('Cannot set alias for not alias adaptation buider');
}
$this->alias = BuilderHelpers::normalizeIdentifier($alias);
return $this;
}
/**
* Sets adapted method public.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->setModifier(Modifiers::PUBLIC);
return $this;
}
/**
* Sets adapted method protected.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->setModifier(Modifiers::PROTECTED);
return $this;
}
/**
* Sets adapted method private.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->setModifier(Modifiers::PRIVATE);
return $this;
}
/**
* Adds overwritten traits.
*
* @param Node\Name|string ...$traits Traits for overwrite
*
* @return $this The builder instance (for fluid interface)
*/
public function insteadof(...$traits) {
if ($this->type === self::TYPE_UNDEFINED) {
if (is_null($this->trait)) {
throw new \LogicException('Precedence adaptation must have trait');
}
$this->type = self::TYPE_PRECEDENCE;
}
if ($this->type !== self::TYPE_PRECEDENCE) {
throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider');
}
foreach ($traits as $trait) {
$this->insteadof[] = BuilderHelpers::normalizeName($trait);
}
return $this;
}
protected function setModifier(int $modifier): void {
if ($this->type === self::TYPE_UNDEFINED) {
$this->type = self::TYPE_ALIAS;
}
if ($this->type !== self::TYPE_ALIAS) {
throw new \LogicException('Cannot set access modifier for not alias adaptation buider');
}
if (is_null($this->modifier)) {
$this->modifier = $modifier;
} else {
throw new \LogicException('Multiple access type modifiers are not allowed');
}
}
/**
* Returns the built node.
*
* @return Node The built node
*/
public function getNode(): Node {
switch ($this->type) {
case self::TYPE_ALIAS:
return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias);
case self::TYPE_PRECEDENCE:
return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof);
default:
throw new \LogicException('Type of adaptation is not defined');
}
}
}

View File

@ -0,0 +1,83 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Trait_ extends Declaration {
protected string $name;
/** @var list<Stmt\TraitUse> */
protected array $uses = [];
/** @var list<Stmt\ClassConst> */
protected array $constants = [];
/** @var list<Stmt\Property> */
protected array $properties = [];
/** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/**
* Creates an interface builder.
*
* @param string $name Name of the interface
*/
public function __construct(string $name) {
$this->name = $name;
}
/**
* Adds a statement.
*
* @param Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt);
if ($stmt instanceof Stmt\Property) {
$this->properties[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassMethod) {
$this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
return $this;
}
/**
* Adds an attribute group.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return $this The builder instance (for fluid interface)
*/
public function addAttribute($attribute) {
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
return $this;
}
/**
* Returns the built trait node.
*
* @return Stmt\Trait_ The built interface node
*/
public function getNode(): PhpParser\Node {
return new Stmt\Trait_(
$this->name, [
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
'attrGroups' => $this->attributeGroups,
], $this->attributes
);
}
}

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Use_ implements Builder {
protected Node\Name $name;
/** @var Stmt\Use_::TYPE_* */
protected int $type;
protected ?string $alias = null;
/**
* Creates a name use (alias) builder.
*
* @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
* @param Stmt\Use_::TYPE_* $type One of the Stmt\Use_::TYPE_* constants
*/
public function __construct($name, int $type) {
$this->name = BuilderHelpers::normalizeName($name);
$this->type = $type;
}
/**
* Sets alias for used name.
*
* @param string $alias Alias to use (last component of full name by default)
*
* @return $this The builder instance (for fluid interface)
*/
public function as(string $alias) {
$this->alias = $alias;
return $this;
}
/**
* Returns the built node.
*
* @return Stmt\Use_ The built node
*/
public function getNode(): Node {
return new Stmt\Use_([
new Node\UseItem($this->name, $this->alias)
], $this->type);
}
}

View File

@ -0,0 +1,375 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Use_;
class BuilderFactory {
/**
* Creates an attribute node.
*
* @param string|Name $name Name of the attribute
* @param array $args Attribute named arguments
*/
public function attribute($name, array $args = []): Node\Attribute {
return new Node\Attribute(
BuilderHelpers::normalizeName($name),
$this->args($args)
);
}
/**
* Creates a namespace builder.
*
* @param null|string|Node\Name $name Name of the namespace
*
* @return Builder\Namespace_ The created namespace builder
*/
public function namespace($name): Builder\Namespace_ {
return new Builder\Namespace_($name);
}
/**
* Creates a class builder.
*
* @param string $name Name of the class
*
* @return Builder\Class_ The created class builder
*/
public function class(string $name): Builder\Class_ {
return new Builder\Class_($name);
}
/**
* Creates an interface builder.
*
* @param string $name Name of the interface
*
* @return Builder\Interface_ The created interface builder
*/
public function interface(string $name): Builder\Interface_ {
return new Builder\Interface_($name);
}
/**
* Creates a trait builder.
*
* @param string $name Name of the trait
*
* @return Builder\Trait_ The created trait builder
*/
public function trait(string $name): Builder\Trait_ {
return new Builder\Trait_($name);
}
/**
* Creates an enum builder.
*
* @param string $name Name of the enum
*
* @return Builder\Enum_ The created enum builder
*/
public function enum(string $name): Builder\Enum_ {
return new Builder\Enum_($name);
}
/**
* Creates a trait use builder.
*
* @param Node\Name|string ...$traits Trait names
*
* @return Builder\TraitUse The created trait use builder
*/
public function useTrait(...$traits): Builder\TraitUse {
return new Builder\TraitUse(...$traits);
}
/**
* Creates a trait use adaptation builder.
*
* @param Node\Name|string|null $trait Trait name
* @param Node\Identifier|string $method Method name
*
* @return Builder\TraitUseAdaptation The created trait use adaptation builder
*/
public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation {
if ($method === null) {
$method = $trait;
$trait = null;
}
return new Builder\TraitUseAdaptation($trait, $method);
}
/**
* Creates a method builder.
*
* @param string $name Name of the method
*
* @return Builder\Method The created method builder
*/
public function method(string $name): Builder\Method {
return new Builder\Method($name);
}
/**
* Creates a parameter builder.
*
* @param string $name Name of the parameter
*
* @return Builder\Param The created parameter builder
*/
public function param(string $name): Builder\Param {
return new Builder\Param($name);
}
/**
* Creates a property builder.
*
* @param string $name Name of the property
*
* @return Builder\Property The created property builder
*/
public function property(string $name): Builder\Property {
return new Builder\Property($name);
}
/**
* Creates a function builder.
*
* @param string $name Name of the function
*
* @return Builder\Function_ The created function builder
*/
public function function(string $name): Builder\Function_ {
return new Builder\Function_($name);
}
/**
* Creates a namespace/class use builder.
*
* @param Node\Name|string $name Name of the entity (namespace or class) to alias
*
* @return Builder\Use_ The created use builder
*/
public function use($name): Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_NORMAL);
}
/**
* Creates a function use builder.
*
* @param Node\Name|string $name Name of the function to alias
*
* @return Builder\Use_ The created use function builder
*/
public function useFunction($name): Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_FUNCTION);
}
/**
* Creates a constant use builder.
*
* @param Node\Name|string $name Name of the const to alias
*
* @return Builder\Use_ The created use const builder
*/
public function useConst($name): Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_CONSTANT);
}
/**
* Creates a class constant builder.
*
* @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value
*
* @return Builder\ClassConst The created use const builder
*/
public function classConst($name, $value): Builder\ClassConst {
return new Builder\ClassConst($name, $value);
}
/**
* Creates an enum case builder.
*
* @param string|Identifier $name Name
*
* @return Builder\EnumCase The created use const builder
*/
public function enumCase($name): Builder\EnumCase {
return new Builder\EnumCase($name);
}
/**
* Creates node a for a literal value.
*
* @param Expr|bool|null|int|float|string|array $value $value
*/
public function val($value): Expr {
return BuilderHelpers::normalizeValue($value);
}
/**
* Creates variable node.
*
* @param string|Expr $name Name
*/
public function var($name): Expr\Variable {
if (!\is_string($name) && !$name instanceof Expr) {
throw new \LogicException('Variable name must be string or Expr');
}
return new Expr\Variable($name);
}
/**
* Normalizes an argument list.
*
* Creates Arg nodes for all arguments and converts literal values to expressions.
*
* @param array $args List of arguments to normalize
*
* @return list<Arg>
*/
public function args(array $args): array {
$normalizedArgs = [];
foreach ($args as $key => $arg) {
if (!($arg instanceof Arg)) {
$arg = new Arg(BuilderHelpers::normalizeValue($arg));
}
if (\is_string($key)) {
$arg->name = BuilderHelpers::normalizeIdentifier($key);
}
$normalizedArgs[] = $arg;
}
return $normalizedArgs;
}
/**
* Creates a function call node.
*
* @param string|Name|Expr $name Function name
* @param array $args Function arguments
*/
public function funcCall($name, array $args = []): Expr\FuncCall {
return new Expr\FuncCall(
BuilderHelpers::normalizeNameOrExpr($name),
$this->args($args)
);
}
/**
* Creates a method call node.
*
* @param Expr $var Variable the method is called on
* @param string|Identifier|Expr $name Method name
* @param array $args Method arguments
*/
public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall {
return new Expr\MethodCall(
$var,
BuilderHelpers::normalizeIdentifierOrExpr($name),
$this->args($args)
);
}
/**
* Creates a static method call node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Method name
* @param array $args Method arguments
*/
public function staticCall($class, $name, array $args = []): Expr\StaticCall {
return new Expr\StaticCall(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifierOrExpr($name),
$this->args($args)
);
}
/**
* Creates an object creation node.
*
* @param string|Name|Expr $class Class name
* @param array $args Constructor arguments
*/
public function new($class, array $args = []): Expr\New_ {
return new Expr\New_(
BuilderHelpers::normalizeNameOrExpr($class),
$this->args($args)
);
}
/**
* Creates a constant fetch node.
*
* @param string|Name $name Constant name
*/
public function constFetch($name): Expr\ConstFetch {
return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
}
/**
* Creates a property fetch node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Property name
*/
public function propertyFetch(Expr $var, $name): Expr\PropertyFetch {
return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
}
/**
* Creates a class constant fetch node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name
*/
public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifierOrExpr($name)
);
}
/**
* Creates nested Concat nodes from a list of expressions.
*
* @param Expr|string ...$exprs Expressions or literal strings
*/
public function concat(...$exprs): Concat {
$numExprs = count($exprs);
if ($numExprs < 2) {
throw new \LogicException('Expected at least two expressions');
}
$lastConcat = $this->normalizeStringExpr($exprs[0]);
for ($i = 1; $i < $numExprs; $i++) {
$lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i]));
}
return $lastConcat;
}
/**
* @param string|Expr $expr
*/
private function normalizeStringExpr($expr): Expr {
if ($expr instanceof Expr) {
return $expr;
}
if (\is_string($expr)) {
return new String_($expr);
}
throw new \LogicException('Expected string or Expr');
}
}

View File

@ -0,0 +1,333 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
/**
* This class defines helpers used in the implementation of builders. Don't use it directly.
*
* @internal
*/
final class BuilderHelpers {
/**
* Normalizes a node: Converts builder objects to nodes.
*
* @param Node|Builder $node The node to normalize
*
* @return Node The normalized node
*/
public static function normalizeNode($node): Node {
if ($node instanceof Builder) {
return $node->getNode();
}
if ($node instanceof Node) {
return $node;
}
throw new \LogicException('Expected node or builder object');
}
/**
* Normalizes a node to a statement.
*
* Expressions are wrapped in a Stmt\Expression node.
*
* @param Node|Builder $node The node to normalize
*
* @return Stmt The normalized statement node
*/
public static function normalizeStmt($node): Stmt {
$node = self::normalizeNode($node);
if ($node instanceof Stmt) {
return $node;
}
if ($node instanceof Expr) {
return new Stmt\Expression($node);
}
throw new \LogicException('Expected statement or expression node');
}
/**
* Normalizes strings to Identifier.
*
* @param string|Identifier $name The identifier to normalize
*
* @return Identifier The normalized identifier
*/
public static function normalizeIdentifier($name): Identifier {
if ($name instanceof Identifier) {
return $name;
}
if (\is_string($name)) {
return new Identifier($name);
}
throw new \LogicException('Expected string or instance of Node\Identifier');
}
/**
* Normalizes strings to Identifier, also allowing expressions.
*
* @param string|Identifier|Expr $name The identifier to normalize
*
* @return Identifier|Expr The normalized identifier or expression
*/
public static function normalizeIdentifierOrExpr($name) {
if ($name instanceof Identifier || $name instanceof Expr) {
return $name;
}
if (\is_string($name)) {
return new Identifier($name);
}
throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr');
}
/**
* Normalizes a name: Converts string names to Name nodes.
*
* @param Name|string $name The name to normalize
*
* @return Name The normalized name
*/
public static function normalizeName($name): Name {
if ($name instanceof Name) {
return $name;
}
if (is_string($name)) {
if (!$name) {
throw new \LogicException('Name cannot be empty');
}
if ($name[0] === '\\') {
return new Name\FullyQualified(substr($name, 1));
}
if (0 === strpos($name, 'namespace\\')) {
return new Name\Relative(substr($name, strlen('namespace\\')));
}
return new Name($name);
}
throw new \LogicException('Name must be a string or an instance of Node\Name');
}
/**
* Normalizes a name: Converts string names to Name nodes, while also allowing expressions.
*
* @param Expr|Name|string $name The name to normalize
*
* @return Name|Expr The normalized name or expression
*/
public static function normalizeNameOrExpr($name) {
if ($name instanceof Expr) {
return $name;
}
if (!is_string($name) && !($name instanceof Name)) {
throw new \LogicException(
'Name must be a string or an instance of Node\Name or Node\Expr'
);
}
return self::normalizeName($name);
}
/**
* Normalizes a type: Converts plain-text type names into proper AST representation.
*
* In particular, builtin types become Identifiers, custom types become Names and nullables
* are wrapped in NullableType nodes.
*
* @param string|Name|Identifier|ComplexType $type The type to normalize
*
* @return Name|Identifier|ComplexType The normalized type
*/
public static function normalizeType($type) {
if (!is_string($type)) {
if (
!$type instanceof Name && !$type instanceof Identifier &&
!$type instanceof ComplexType
) {
throw new \LogicException(
'Type must be a string, or an instance of Name, Identifier or ComplexType'
);
}
return $type;
}
$nullable = false;
if (strlen($type) > 0 && $type[0] === '?') {
$nullable = true;
$type = substr($type, 1);
}
$builtinTypes = [
'array',
'callable',
'bool',
'int',
'float',
'string',
'iterable',
'void',
'object',
'null',
'false',
'mixed',
'never',
'true',
];
$lowerType = strtolower($type);
if (in_array($lowerType, $builtinTypes)) {
$type = new Identifier($lowerType);
} else {
$type = self::normalizeName($type);
}
$notNullableTypes = [
'void', 'mixed', 'never',
];
if ($nullable && in_array((string) $type, $notNullableTypes)) {
throw new \LogicException(sprintf('%s type cannot be nullable', $type));
}
return $nullable ? new NullableType($type) : $type;
}
/**
* Normalizes a value: Converts nulls, booleans, integers,
* floats, strings and arrays into their respective nodes
*
* @param Node\Expr|bool|null|int|float|string|array $value The value to normalize
*
* @return Expr The normalized value
*/
public static function normalizeValue($value): Expr {
if ($value instanceof Node\Expr) {
return $value;
}
if (is_null($value)) {
return new Expr\ConstFetch(
new Name('null')
);
}
if (is_bool($value)) {
return new Expr\ConstFetch(
new Name($value ? 'true' : 'false')
);
}
if (is_int($value)) {
return new Scalar\Int_($value);
}
if (is_float($value)) {
return new Scalar\Float_($value);
}
if (is_string($value)) {
return new Scalar\String_($value);
}
if (is_array($value)) {
$items = [];
$lastKey = -1;
foreach ($value as $itemKey => $itemValue) {
// for consecutive, numeric keys don't generate keys
if (null !== $lastKey && ++$lastKey === $itemKey) {
$items[] = new Node\ArrayItem(
self::normalizeValue($itemValue)
);
} else {
$lastKey = null;
$items[] = new Node\ArrayItem(
self::normalizeValue($itemValue),
self::normalizeValue($itemKey)
);
}
}
return new Expr\Array_($items);
}
throw new \LogicException('Invalid value');
}
/**
* Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc.
*
* @param Comment\Doc|string $docComment The doc comment to normalize
*
* @return Comment\Doc The normalized doc comment
*/
public static function normalizeDocComment($docComment): Comment\Doc {
if ($docComment instanceof Comment\Doc) {
return $docComment;
}
if (is_string($docComment)) {
return new Comment\Doc($docComment);
}
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
}
/**
* Normalizes a attribute: Converts attribute to the Attribute Group if needed.
*
* @param Node\Attribute|Node\AttributeGroup $attribute
*
* @return Node\AttributeGroup The Attribute Group
*/
public static function normalizeAttribute($attribute): Node\AttributeGroup {
if ($attribute instanceof Node\AttributeGroup) {
return $attribute;
}
if (!($attribute instanceof Node\Attribute)) {
throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
}
return new Node\AttributeGroup([$attribute]);
}
/**
* Adds a modifier and returns new modifier bitmask.
*
* @param int $modifiers Existing modifiers
* @param int $modifier Modifier to set
*
* @return int New modifiers
*/
public static function addModifier(int $modifiers, int $modifier): int {
Modifiers::verifyModifier($modifiers, $modifier);
return $modifiers | $modifier;
}
/**
* Adds a modifier and returns new modifier bitmask.
* @return int New modifiers
*/
public static function addClassModifier(int $existingModifiers, int $modifierToSet): int {
Modifiers::verifyClassModifier($existingModifiers, $modifierToSet);
return $existingModifiers | $modifierToSet;
}
}

View File

@ -0,0 +1,209 @@
<?php declare(strict_types=1);
namespace PhpParser;
class Comment implements \JsonSerializable {
protected string $text;
protected int $startLine;
protected int $startFilePos;
protected int $startTokenPos;
protected int $endLine;
protected int $endFilePos;
protected int $endTokenPos;
/**
* Constructs a comment node.
*
* @param string $text Comment text (including comment delimiters like /*)
* @param int $startLine Line number the comment started on
* @param int $startFilePos File offset the comment started on
* @param int $startTokenPos Token offset the comment started on
*/
public function __construct(
string $text,
int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,
int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1
) {
$this->text = $text;
$this->startLine = $startLine;
$this->startFilePos = $startFilePos;
$this->startTokenPos = $startTokenPos;
$this->endLine = $endLine;
$this->endFilePos = $endFilePos;
$this->endTokenPos = $endTokenPos;
}
/**
* Gets the comment text.
*
* @return string The comment text (including comment delimiters like /*)
*/
public function getText(): string {
return $this->text;
}
/**
* Gets the line number the comment started on.
*
* @return int Line number (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getStartLine(): int {
return $this->startLine;
}
/**
* Gets the file offset the comment started on.
*
* @return int File offset (or -1 if not available)
*/
public function getStartFilePos(): int {
return $this->startFilePos;
}
/**
* Gets the token offset the comment started on.
*
* @return int Token offset (or -1 if not available)
*/
public function getStartTokenPos(): int {
return $this->startTokenPos;
}
/**
* Gets the line number the comment ends on.
*
* @return int Line number (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getEndLine(): int {
return $this->endLine;
}
/**
* Gets the file offset the comment ends on.
*
* @return int File offset (or -1 if not available)
*/
public function getEndFilePos(): int {
return $this->endFilePos;
}
/**
* Gets the token offset the comment ends on.
*
* @return int Token offset (or -1 if not available)
*/
public function getEndTokenPos(): int {
return $this->endTokenPos;
}
/**
* Gets the comment text.
*
* @return string The comment text (including comment delimiters like /*)
*/
public function __toString(): string {
return $this->text;
}
/**
* Gets the reformatted comment text.
*
* "Reformatted" here means that we try to clean up the whitespace at the
* starts of the lines. This is necessary because we receive the comments
* without leading whitespace on the first line, but with leading whitespace
* on all subsequent lines.
*
* Additionally, this normalizes CRLF newlines to LF newlines.
*/
public function getReformattedText(): string {
$text = str_replace("\r\n", "\n", $this->text);
$newlinePos = strpos($text, "\n");
if (false === $newlinePos) {
// Single line comments don't need further processing
return $text;
}
if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) {
// Multi line comment of the type
//
// /*
// * Some text.
// * Some more text.
// */
//
// is handled by replacing the whitespace sequences before the * by a single space
return preg_replace('(^\s+\*)m', ' *', $text);
}
if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
// Multi line comment of the type
//
// /*
// Some text.
// Some more text.
// */
//
// is handled by removing the whitespace sequence on the line before the closing
// */ on all lines. So if the last line is " */", then " " is removed at the
// start of all lines.
return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
}
if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
// Multi line comment of the type
//
// /* Some text.
// Some more text.
// Indented text.
// Even more text. */
//
// is handled by removing the difference between the shortest whitespace prefix on all
// lines and the length of the "/* " opening sequence.
$prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1));
$removeLen = $prefixLen - strlen($matches[0]);
return preg_replace('(^\s{' . $removeLen . '})m', '', $text);
}
// No idea how to format this comment, so simply return as is
return $text;
}
/**
* Get length of shortest whitespace prefix (at the start of a line).
*
* If there is a line with no prefix whitespace, 0 is a valid return value.
*
* @param string $str String to check
* @return int Length in characters. Tabs count as single characters.
*/
private function getShortestWhitespacePrefixLen(string $str): int {
$lines = explode("\n", $str);
$shortestPrefixLen = \PHP_INT_MAX;
foreach ($lines as $line) {
preg_match('(^\s*)', $line, $matches);
$prefixLen = strlen($matches[0]);
if ($prefixLen < $shortestPrefixLen) {
$shortestPrefixLen = $prefixLen;
}
}
return $shortestPrefixLen;
}
/**
* @return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
*/
public function jsonSerialize(): array {
// Technically not a node, but we make it look like one anyway
$type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment';
return [
'nodeType' => $type,
'text' => $this->text,
// TODO: Rename these to include "start".
'line' => $this->startLine,
'filePos' => $this->startFilePos,
'tokenPos' => $this->startTokenPos,
'endLine' => $this->endLine,
'endFilePos' => $this->endFilePos,
'endTokenPos' => $this->endTokenPos,
];
}
}

View File

@ -0,0 +1,6 @@
<?php declare(strict_types=1);
namespace PhpParser\Comment;
class Doc extends \PhpParser\Comment {
}

View File

@ -0,0 +1,6 @@
<?php declare(strict_types=1);
namespace PhpParser;
class ConstExprEvaluationException extends \Exception {
}

View File

@ -0,0 +1,234 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;
use function array_merge;
/**
* Evaluates constant expressions.
*
* This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
* evaluated without further context. If a subexpression is not of this type, a user-provided
* fallback evaluator is invoked. To support all constant expressions that are also supported by
* PHP (and not already handled by this class), the fallback evaluator must be able to handle the
* following node types:
*
* * All Scalar\MagicConst\* nodes.
* * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
* * Expr\ClassConstFetch nodes.
*
* The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
*
* The evaluation is dependent on runtime configuration in two respects: Firstly, floating
* point to string conversions are affected by the precision ini setting. Secondly, they are also
* affected by the LC_NUMERIC locale.
*/
class ConstExprEvaluator {
/** @var callable|null */
private $fallbackEvaluator;
/**
* Create a constant expression evaluator.
*
* The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
* class doc comment for more information.
*
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
*/
public function __construct(?callable $fallbackEvaluator = null) {
$this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
throw new ConstExprEvaluationException(
"Expression of type {$expr->getType()} cannot be evaluated"
);
};
}
/**
* Silently evaluates a constant expression into a PHP value.
*
* Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
* The original source of the exception is available through getPrevious().
*
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
* constructor will be invoked. By default, if no fallback is provided, an exception of type
* ConstExprEvaluationException is thrown.
*
* See class doc comment for caveats and limitations.
*
* @param Expr $expr Constant expression to evaluate
* @return mixed Result of evaluation
*
* @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
*/
public function evaluateSilently(Expr $expr) {
set_error_handler(function ($num, $str, $file, $line) {
throw new \ErrorException($str, 0, $num, $file, $line);
});
try {
return $this->evaluate($expr);
} catch (\Throwable $e) {
if (!$e instanceof ConstExprEvaluationException) {
$e = new ConstExprEvaluationException(
"An error occurred during constant expression evaluation", 0, $e);
}
throw $e;
} finally {
restore_error_handler();
}
}
/**
* Directly evaluates a constant expression into a PHP value.
*
* May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
* into a ConstExprEvaluationException.
*
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
* constructor will be invoked. By default, if no fallback is provided, an exception of type
* ConstExprEvaluationException is thrown.
*
* See class doc comment for caveats and limitations.
*
* @param Expr $expr Constant expression to evaluate
* @return mixed Result of evaluation
*
* @throws ConstExprEvaluationException if the expression cannot be evaluated
*/
public function evaluateDirectly(Expr $expr) {
return $this->evaluate($expr);
}
/** @return mixed */
private function evaluate(Expr $expr) {
if ($expr instanceof Scalar\Int_
|| $expr instanceof Scalar\Float_
|| $expr instanceof Scalar\String_
) {
return $expr->value;
}
if ($expr instanceof Expr\Array_) {
return $this->evaluateArray($expr);
}
// Unary operators
if ($expr instanceof Expr\UnaryPlus) {
return +$this->evaluate($expr->expr);
}
if ($expr instanceof Expr\UnaryMinus) {
return -$this->evaluate($expr->expr);
}
if ($expr instanceof Expr\BooleanNot) {
return !$this->evaluate($expr->expr);
}
if ($expr instanceof Expr\BitwiseNot) {
return ~$this->evaluate($expr->expr);
}
if ($expr instanceof Expr\BinaryOp) {
return $this->evaluateBinaryOp($expr);
}
if ($expr instanceof Expr\Ternary) {
return $this->evaluateTernary($expr);
}
if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
}
if ($expr instanceof Expr\ConstFetch) {
return $this->evaluateConstFetch($expr);
}
return ($this->fallbackEvaluator)($expr);
}
private function evaluateArray(Expr\Array_ $expr): array {
$array = [];
foreach ($expr->items as $item) {
if (null !== $item->key) {
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
} elseif ($item->unpack) {
$array = array_merge($array, $this->evaluate($item->value));
} else {
$array[] = $this->evaluate($item->value);
}
}
return $array;
}
/** @return mixed */
private function evaluateTernary(Expr\Ternary $expr) {
if (null === $expr->if) {
return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
}
return $this->evaluate($expr->cond)
? $this->evaluate($expr->if)
: $this->evaluate($expr->else);
}
/** @return mixed */
private function evaluateBinaryOp(Expr\BinaryOp $expr) {
if ($expr instanceof Expr\BinaryOp\Coalesce
&& $expr->left instanceof Expr\ArrayDimFetch
) {
// This needs to be special cased to respect BP_VAR_IS fetch semantics
return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
?? $this->evaluate($expr->right);
}
// The evaluate() calls are repeated in each branch, because some of the operators are
// short-circuiting and evaluating the RHS in advance may be illegal in that case
$l = $expr->left;
$r = $expr->right;
switch ($expr->getOperatorSigil()) {
case '&': return $this->evaluate($l) & $this->evaluate($r);
case '|': return $this->evaluate($l) | $this->evaluate($r);
case '^': return $this->evaluate($l) ^ $this->evaluate($r);
case '&&': return $this->evaluate($l) && $this->evaluate($r);
case '||': return $this->evaluate($l) || $this->evaluate($r);
case '??': return $this->evaluate($l) ?? $this->evaluate($r);
case '.': return $this->evaluate($l) . $this->evaluate($r);
case '/': return $this->evaluate($l) / $this->evaluate($r);
case '==': return $this->evaluate($l) == $this->evaluate($r);
case '>': return $this->evaluate($l) > $this->evaluate($r);
case '>=': return $this->evaluate($l) >= $this->evaluate($r);
case '===': return $this->evaluate($l) === $this->evaluate($r);
case 'and': return $this->evaluate($l) and $this->evaluate($r);
case 'or': return $this->evaluate($l) or $this->evaluate($r);
case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
case '-': return $this->evaluate($l) - $this->evaluate($r);
case '%': return $this->evaluate($l) % $this->evaluate($r);
case '*': return $this->evaluate($l) * $this->evaluate($r);
case '!=': return $this->evaluate($l) != $this->evaluate($r);
case '!==': return $this->evaluate($l) !== $this->evaluate($r);
case '+': return $this->evaluate($l) + $this->evaluate($r);
case '**': return $this->evaluate($l) ** $this->evaluate($r);
case '<<': return $this->evaluate($l) << $this->evaluate($r);
case '>>': return $this->evaluate($l) >> $this->evaluate($r);
case '<': return $this->evaluate($l) < $this->evaluate($r);
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
}
throw new \Exception('Should not happen');
}
/** @return mixed */
private function evaluateConstFetch(Expr\ConstFetch $expr) {
$name = $expr->name->toLowerString();
switch ($name) {
case 'null': return null;
case 'false': return false;
case 'true': return true;
}
return ($this->fallbackEvaluator)($expr);
}
}

View File

@ -0,0 +1,173 @@
<?php declare(strict_types=1);
namespace PhpParser;
class Error extends \RuntimeException {
protected string $rawMessage;
/** @var array<string, mixed> */
protected array $attributes;
/**
* Creates an Exception signifying a parse error.
*
* @param string $message Error message
* @param array<string, mixed> $attributes Attributes of node/token where error occurred
*/
public function __construct(string $message, array $attributes = []) {
$this->rawMessage = $message;
$this->attributes = $attributes;
$this->updateMessage();
}
/**
* Gets the error message
*
* @return string Error message
*/
public function getRawMessage(): string {
return $this->rawMessage;
}
/**
* Gets the line the error starts in.
*
* @return int Error start line
* @phpstan-return -1|positive-int
*/
public function getStartLine(): int {
return $this->attributes['startLine'] ?? -1;
}
/**
* Gets the line the error ends in.
*
* @return int Error end line
* @phpstan-return -1|positive-int
*/
public function getEndLine(): int {
return $this->attributes['endLine'] ?? -1;
}
/**
* Gets the attributes of the node/token the error occurred at.
*
* @return array<string, mixed>
*/
public function getAttributes(): array {
return $this->attributes;
}
/**
* Sets the attributes of the node/token the error occurred at.
*
* @param array<string, mixed> $attributes
*/
public function setAttributes(array $attributes): void {
$this->attributes = $attributes;
$this->updateMessage();
}
/**
* Sets the line of the PHP file the error occurred in.
*
* @param string $message Error message
*/
public function setRawMessage(string $message): void {
$this->rawMessage = $message;
$this->updateMessage();
}
/**
* Sets the line the error starts in.
*
* @param int $line Error start line
*/
public function setStartLine(int $line): void {
$this->attributes['startLine'] = $line;
$this->updateMessage();
}
/**
* Returns whether the error has start and end column information.
*
* For column information enable the startFilePos and endFilePos in the lexer options.
*/
public function hasColumnInfo(): bool {
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
}
/**
* Gets the start column (1-based) into the line where the error started.
*
* @param string $code Source code of the file
*/
public function getStartColumn(string $code): int {
if (!$this->hasColumnInfo()) {
throw new \RuntimeException('Error does not have column information');
}
return $this->toColumn($code, $this->attributes['startFilePos']);
}
/**
* Gets the end column (1-based) into the line where the error ended.
*
* @param string $code Source code of the file
*/
public function getEndColumn(string $code): int {
if (!$this->hasColumnInfo()) {
throw new \RuntimeException('Error does not have column information');
}
return $this->toColumn($code, $this->attributes['endFilePos']);
}
/**
* Formats message including line and column information.
*
* @param string $code Source code associated with the error, for calculation of the columns
*
* @return string Formatted message
*/
public function getMessageWithColumnInfo(string $code): string {
return sprintf(
'%s from %d:%d to %d:%d', $this->getRawMessage(),
$this->getStartLine(), $this->getStartColumn($code),
$this->getEndLine(), $this->getEndColumn($code)
);
}
/**
* Converts a file offset into a column.
*
* @param string $code Source code that $pos indexes into
* @param int $pos 0-based position in $code
*
* @return int 1-based column (relative to start of line)
*/
private function toColumn(string $code, int $pos): int {
if ($pos > strlen($code)) {
throw new \RuntimeException('Invalid position information');
}
$lineStartPos = strrpos($code, "\n", $pos - strlen($code));
if (false === $lineStartPos) {
$lineStartPos = -1;
}
return $pos - $lineStartPos;
}
/**
* Updates the exception message after a change to rawMessage or rawLine.
*/
protected function updateMessage(): void {
$this->message = $this->rawMessage;
if (-1 === $this->getStartLine()) {
$this->message .= ' on unknown line';
} else {
$this->message .= ' on line ' . $this->getStartLine();
}
}
}

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface ErrorHandler {
/**
* Handle an error generated during lexing, parsing or some other operation.
*
* @param Error $error The error that needs to be handled
*/
public function handleError(Error $error): void;
}

View File

@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace PhpParser\ErrorHandler;
use PhpParser\Error;
use PhpParser\ErrorHandler;
/**
* Error handler that collects all errors into an array.
*
* This allows graceful handling of errors.
*/
class Collecting implements ErrorHandler {
/** @var Error[] Collected errors */
private array $errors = [];
public function handleError(Error $error): void {
$this->errors[] = $error;
}
/**
* Get collected errors.
*
* @return Error[]
*/
public function getErrors(): array {
return $this->errors;
}
/**
* Check whether there are any errors.
*/
public function hasErrors(): bool {
return !empty($this->errors);
}
/**
* Reset/clear collected errors.
*/
public function clearErrors(): void {
$this->errors = [];
}
}

View File

@ -0,0 +1,17 @@
<?php declare(strict_types=1);
namespace PhpParser\ErrorHandler;
use PhpParser\Error;
use PhpParser\ErrorHandler;
/**
* Error handler that handles all errors by throwing them.
*
* This is the default strategy used by all components.
*/
class Throwing implements ErrorHandler {
public function handleError(Error $error): void {
throw $error;
}
}

View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
/**
* @internal
*/
class DiffElem {
public const TYPE_KEEP = 0;
public const TYPE_REMOVE = 1;
public const TYPE_ADD = 2;
public const TYPE_REPLACE = 3;
/** @var int One of the TYPE_* constants */
public int $type;
/** @var mixed Is null for add operations */
public $old;
/** @var mixed Is null for remove operations */
public $new;
/**
* @param int $type One of the TYPE_* constants
* @param mixed $old Is null for add operations
* @param mixed $new Is null for remove operations
*/
public function __construct(int $type, $old, $new) {
$this->type = $type;
$this->old = $old;
$this->new = $new;
}
}

View File

@ -0,0 +1,178 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
/**
* Implements the Myers diff algorithm.
*
* Myers, Eugene W. "An O (ND) difference algorithm and its variations."
* Algorithmica 1.1 (1986): 251-266.
*
* @template T
* @internal
*/
class Differ {
/** @var callable(T, T): bool */
private $isEqual;
/**
* Create differ over the given equality relation.
*
* @param callable(T, T): bool $isEqual Equality relation
*/
public function __construct(callable $isEqual) {
$this->isEqual = $isEqual;
}
/**
* Calculate diff (edit script) from $old to $new.
*
* @param T[] $old Original array
* @param T[] $new New array
*
* @return DiffElem[] Diff (edit script)
*/
public function diff(array $old, array $new): array {
$old = \array_values($old);
$new = \array_values($new);
list($trace, $x, $y) = $this->calculateTrace($old, $new);
return $this->extractDiff($trace, $x, $y, $old, $new);
}
/**
* Calculate diff, including "replace" operations.
*
* If a sequence of remove operations is followed by the same number of add operations, these
* will be coalesced into replace operations.
*
* @param T[] $old Original array
* @param T[] $new New array
*
* @return DiffElem[] Diff (edit script), including replace operations
*/
public function diffWithReplacements(array $old, array $new): array {
return $this->coalesceReplacements($this->diff($old, $new));
}
/**
* @param T[] $old
* @param T[] $new
* @return array{array<int, array<int, int>>, int, int}
*/
private function calculateTrace(array $old, array $new): array {
$n = \count($old);
$m = \count($new);
$max = $n + $m;
$v = [1 => 0];
$trace = [];
for ($d = 0; $d <= $max; $d++) {
$trace[] = $v;
for ($k = -$d; $k <= $d; $k += 2) {
if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
$x = $v[$k + 1];
} else {
$x = $v[$k - 1] + 1;
}
$y = $x - $k;
while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
$x++;
$y++;
}
$v[$k] = $x;
if ($x >= $n && $y >= $m) {
return [$trace, $x, $y];
}
}
}
throw new \Exception('Should not happen');
}
/**
* @param array<int, array<int, int>> $trace
* @param T[] $old
* @param T[] $new
* @return DiffElem[]
*/
private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array {
$result = [];
for ($d = \count($trace) - 1; $d >= 0; $d--) {
$v = $trace[$d];
$k = $x - $y;
if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
$prevK = $k + 1;
} else {
$prevK = $k - 1;
}
$prevX = $v[$prevK];
$prevY = $prevX - $prevK;
while ($x > $prevX && $y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
$x--;
$y--;
}
if ($d === 0) {
break;
}
while ($x > $prevX) {
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
$x--;
}
while ($y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
$y--;
}
}
return array_reverse($result);
}
/**
* Coalesce equal-length sequences of remove+add into a replace operation.
*
* @param DiffElem[] $diff
* @return DiffElem[]
*/
private function coalesceReplacements(array $diff): array {
$newDiff = [];
$c = \count($diff);
for ($i = 0; $i < $c; $i++) {
$diffType = $diff[$i]->type;
if ($diffType !== DiffElem::TYPE_REMOVE) {
$newDiff[] = $diff[$i];
continue;
}
$j = $i;
while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
$j++;
}
$k = $j;
while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
$k++;
}
if ($j - $i === $k - $j) {
$len = $j - $i;
for ($n = 0; $n < $len; $n++) {
$newDiff[] = new DiffElem(
DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new
);
}
} else {
for (; $i < $k; $i++) {
$newDiff[] = $diff[$i];
}
}
$i = $k - 1;
}
return $newDiff;
}
}

View File

@ -0,0 +1,71 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
use PhpParser\Node;
use PhpParser\Node\Expr;
/**
* This node is used internally by the format-preserving pretty printer to print anonymous classes.
*
* The normal anonymous class structure violates assumptions about the order of token offsets.
* Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even
* though they are actually interleaved with them. This special node type is used temporarily to
* restore a sane token offset order.
*
* @internal
*/
class PrintableNewAnonClassNode extends Expr {
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/** @var int Modifiers */
public int $flags;
/** @var (Node\Arg|Node\VariadicPlaceholder)[] Arguments */
public array $args;
/** @var null|Node\Name Name of extended class */
public ?Node\Name $extends;
/** @var Node\Name[] Names of implemented interfaces */
public array $implements;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param (Node\Arg|Node\VariadicPlaceholder)[] $args Arguments
* @param Node\Name|null $extends Name of extended class
* @param Node\Name[] $implements Names of implemented interfaces
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Attributes
*/
public function __construct(
array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->attrGroups = $attrGroups;
$this->flags = $flags;
$this->args = $args;
$this->extends = $extends;
$this->implements = $implements;
$this->stmts = $stmts;
}
public static function fromNewNode(Expr\New_ $newNode): self {
$class = $newNode->class;
assert($class instanceof Node\Stmt\Class_);
// We don't assert that $class->name is null here, to allow consumers to assign unique names
// to anonymous classes for their own purposes. We simplify ignore the name here.
return new self(
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes()
);
}
public function getType(): string {
return 'Expr_PrintableNewAnonClass';
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
}
}

View File

@ -0,0 +1,237 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
if (\PHP_VERSION_ID >= 80000) {
class TokenPolyfill extends \PhpToken {
}
return;
}
/**
* This is a polyfill for the PhpToken class introduced in PHP 8.0. We do not actually polyfill
* PhpToken, because composer might end up picking a different polyfill implementation, which does
* not meet our requirements.
*
* @internal
*/
class TokenPolyfill {
/** @var int The ID of the token. Either a T_* constant of a character code < 256. */
public int $id;
/** @var string The textual content of the token. */
public string $text;
/** @var int The 1-based starting line of the token (or -1 if unknown). */
public int $line;
/** @var int The 0-based starting position of the token (or -1 if unknown). */
public int $pos;
/** @var array<int, bool> Tokens ignored by the PHP parser. */
private const IGNORABLE_TOKENS = [
\T_WHITESPACE => true,
\T_COMMENT => true,
\T_DOC_COMMENT => true,
\T_OPEN_TAG => true,
];
/** @var array<int, bool> Tokens that may be part of a T_NAME_* identifier. */
private static array $identifierTokens;
/**
* Create a Token with the given ID and text, as well optional line and position information.
*/
final public function __construct(int $id, string $text, int $line = -1, int $pos = -1) {
$this->id = $id;
$this->text = $text;
$this->line = $line;
$this->pos = $pos;
}
/**
* Get the name of the token. For single-char tokens this will be the token character.
* Otherwise it will be a T_* style name, or null if the token ID is unknown.
*/
public function getTokenName(): ?string {
if ($this->id < 256) {
return \chr($this->id);
}
$name = token_name($this->id);
return $name === 'UNKNOWN' ? null : $name;
}
/**
* Check whether the token is of the given kind. The kind may be either an integer that matches
* the token ID, a string that matches the token text, or an array of integers/strings. In the
* latter case, the function returns true if any of the kinds in the array match.
*
* @param int|string|(int|string)[] $kind
*/
public function is($kind): bool {
if (\is_int($kind)) {
return $this->id === $kind;
}
if (\is_string($kind)) {
return $this->text === $kind;
}
if (\is_array($kind)) {
foreach ($kind as $entry) {
if (\is_int($entry)) {
if ($this->id === $entry) {
return true;
}
} elseif (\is_string($entry)) {
if ($this->text === $entry) {
return true;
}
} else {
throw new \TypeError(
'Argument #1 ($kind) must only have elements of type string|int, ' .
gettype($entry) . ' given');
}
}
return false;
}
throw new \TypeError(
'Argument #1 ($kind) must be of type string|int|array, ' .gettype($kind) . ' given');
}
/**
* Check whether this token would be ignored by the PHP parser. Returns true for T_WHITESPACE,
* T_COMMENT, T_DOC_COMMENT and T_OPEN_TAG, and false for everything else.
*/
public function isIgnorable(): bool {
return isset(self::IGNORABLE_TOKENS[$this->id]);
}
/**
* Return the textual content of the token.
*/
public function __toString(): string {
return $this->text;
}
/**
* Tokenize the given source code and return an array of tokens.
*
* This performs certain canonicalizations to match the PHP 8.0 token format:
* * Bad characters are represented using T_BAD_CHARACTER rather than omitted.
* * T_COMMENT does not include trailing newlines, instead the newline is part of a following
* T_WHITESPACE token.
* * Namespaced names are represented using T_NAME_* tokens.
*
* @return static[]
*/
public static function tokenize(string $code, int $flags = 0): array {
self::init();
$tokens = [];
$line = 1;
$pos = 0;
$origTokens = \token_get_all($code, $flags);
$numTokens = \count($origTokens);
for ($i = 0; $i < $numTokens; $i++) {
$token = $origTokens[$i];
if (\is_string($token)) {
if (\strlen($token) === 2) {
// b" and B" are tokenized as single-char tokens, even though they aren't.
$tokens[] = new static(\ord('"'), $token, $line, $pos);
$pos += 2;
} else {
$tokens[] = new static(\ord($token), $token, $line, $pos);
$pos++;
}
} else {
$id = $token[0];
$text = $token[1];
// Emulate PHP 8.0 comment format, which does not include trailing whitespace anymore.
if ($id === \T_COMMENT && \substr($text, 0, 2) !== '/*' &&
\preg_match('/(\r\n|\n|\r)$/D', $text, $matches)
) {
$trailingNewline = $matches[0];
$text = \substr($text, 0, -\strlen($trailingNewline));
$tokens[] = new static($id, $text, $line, $pos);
$pos += \strlen($text);
if ($i + 1 < $numTokens && $origTokens[$i + 1][0] === \T_WHITESPACE) {
// Move trailing newline into following T_WHITESPACE token, if it already exists.
$origTokens[$i + 1][1] = $trailingNewline . $origTokens[$i + 1][1];
$origTokens[$i + 1][2]--;
} else {
// Otherwise, we need to create a new T_WHITESPACE token.
$tokens[] = new static(\T_WHITESPACE, $trailingNewline, $line, $pos);
$line++;
$pos += \strlen($trailingNewline);
}
continue;
}
// Emulate PHP 8.0 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and
// T_STRING into a single token.
if (($id === \T_NS_SEPARATOR || isset(self::$identifierTokens[$id]))) {
$newText = $text;
$lastWasSeparator = $id === \T_NS_SEPARATOR;
for ($j = $i + 1; $j < $numTokens; $j++) {
if ($lastWasSeparator) {
if (!isset(self::$identifierTokens[$origTokens[$j][0]])) {
break;
}
$lastWasSeparator = false;
} else {
if ($origTokens[$j][0] !== \T_NS_SEPARATOR) {
break;
}
$lastWasSeparator = true;
}
$newText .= $origTokens[$j][1];
}
if ($lastWasSeparator) {
// Trailing separator is not part of the name.
$j--;
$newText = \substr($newText, 0, -1);
}
if ($j > $i + 1) {
if ($id === \T_NS_SEPARATOR) {
$id = \T_NAME_FULLY_QUALIFIED;
} elseif ($id === \T_NAMESPACE) {
$id = \T_NAME_RELATIVE;
} else {
$id = \T_NAME_QUALIFIED;
}
$tokens[] = new static($id, $newText, $line, $pos);
$pos += \strlen($newText);
$i = $j - 1;
continue;
}
}
$tokens[] = new static($id, $text, $line, $pos);
$line += \substr_count($text, "\n");
$pos += \strlen($text);
}
}
return $tokens;
}
/** Initialize private static state needed by tokenize(). */
private static function init(): void {
if (isset(self::$identifierTokens)) {
return;
}
// Based on semi_reserved production.
self::$identifierTokens = \array_fill_keys([
\T_STRING,
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
\T_MATCH,
], true);
}
}

View File

@ -0,0 +1,275 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
use PhpParser\Token;
/**
* Provides operations on token streams, for use by pretty printer.
*
* @internal
*/
class TokenStream {
/** @var Token[] Tokens (in PhpToken::tokenize() format) */
private array $tokens;
/** @var int[] Map from position to indentation */
private array $indentMap;
/**
* Create token stream instance.
*
* @param Token[] $tokens Tokens in PhpToken::tokenize() format
*/
public function __construct(array $tokens) {
$this->tokens = $tokens;
$this->indentMap = $this->calcIndentMap();
}
/**
* Whether the given position is immediately surrounded by parenthesis.
*
* @param int $startPos Start position
* @param int $endPos End position
*/
public function haveParens(int $startPos, int $endPos): bool {
return $this->haveTokenImmediatelyBefore($startPos, '(')
&& $this->haveTokenImmediatelyAfter($endPos, ')');
}
/**
* Whether the given position is immediately surrounded by braces.
*
* @param int $startPos Start position
* @param int $endPos End position
*/
public function haveBraces(int $startPos, int $endPos): bool {
return ($this->haveTokenImmediatelyBefore($startPos, '{')
|| $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
&& $this->haveTokenImmediatelyAfter($endPos, '}');
}
/**
* Check whether the position is directly preceded by a certain token type.
*
* During this check whitespace and comments are skipped.
*
* @param int $pos Position before which the token should occur
* @param int|string $expectedTokenType Token to check for
*
* @return bool Whether the expected token was found
*/
public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType): bool {
$tokens = $this->tokens;
$pos--;
for (; $pos >= 0; $pos--) {
$token = $tokens[$pos];
if ($token->is($expectedTokenType)) {
return true;
}
if (!$token->isIgnorable()) {
break;
}
}
return false;
}
/**
* Check whether the position is directly followed by a certain token type.
*
* During this check whitespace and comments are skipped.
*
* @param int $pos Position after which the token should occur
* @param int|string $expectedTokenType Token to check for
*
* @return bool Whether the expected token was found
*/
public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool {
$tokens = $this->tokens;
$pos++;
for ($c = \count($tokens); $pos < $c; $pos++) {
$token = $tokens[$pos];
if ($token->is($expectedTokenType)) {
return true;
}
if (!$token->isIgnorable()) {
break;
}
}
return false;
}
/** @param int|string|(int|string)[] $skipTokenType */
public function skipLeft(int $pos, $skipTokenType): int {
$tokens = $this->tokens;
$pos = $this->skipLeftWhitespace($pos);
if ($skipTokenType === \T_WHITESPACE) {
return $pos;
}
if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token');
}
$pos--;
return $this->skipLeftWhitespace($pos);
}
/** @param int|string|(int|string)[] $skipTokenType */
public function skipRight(int $pos, $skipTokenType): int {
$tokens = $this->tokens;
$pos = $this->skipRightWhitespace($pos);
if ($skipTokenType === \T_WHITESPACE) {
return $pos;
}
if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token');
}
$pos++;
return $this->skipRightWhitespace($pos);
}
/**
* Return first non-whitespace token position smaller or equal to passed position.
*
* @param int $pos Token position
* @return int Non-whitespace token position
*/
public function skipLeftWhitespace(int $pos): int {
$tokens = $this->tokens;
for (; $pos >= 0; $pos--) {
if (!$tokens[$pos]->isIgnorable()) {
break;
}
}
return $pos;
}
/**
* Return first non-whitespace position greater or equal to passed position.
*
* @param int $pos Token position
* @return int Non-whitespace token position
*/
public function skipRightWhitespace(int $pos): int {
$tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) {
if (!$tokens[$pos]->isIgnorable()) {
break;
}
}
return $pos;
}
/** @param int|string|(int|string)[] $findTokenType */
public function findRight(int $pos, $findTokenType): int {
$tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) {
if ($tokens[$pos]->is($findTokenType)) {
return $pos;
}
}
return -1;
}
/**
* Whether the given position range contains a certain token type.
*
* @param int $startPos Starting position (inclusive)
* @param int $endPos Ending position (exclusive)
* @param int|string $tokenType Token type to look for
* @return bool Whether the token occurs in the given range
*/
public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool {
$tokens = $this->tokens;
for ($pos = $startPos; $pos < $endPos; $pos++) {
if ($tokens[$pos]->is($tokenType)) {
return true;
}
}
return false;
}
public function haveTagInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
}
/**
* Get indentation before token position.
*
* @param int $pos Token position
*
* @return int Indentation depth (in spaces)
*/
public function getIndentationBefore(int $pos): int {
return $this->indentMap[$pos];
}
/**
* Get the code corresponding to a token offset range, optionally adjusted for indentation.
*
* @param int $from Token start position (inclusive)
* @param int $to Token end position (exclusive)
* @param int $indent By how much the code should be indented (can be negative as well)
*
* @return string Code corresponding to token range, adjusted for indentation
*/
public function getTokenCode(int $from, int $to, int $indent): string {
$tokens = $this->tokens;
$result = '';
for ($pos = $from; $pos < $to; $pos++) {
$token = $tokens[$pos];
$id = $token->id;
$text = $token->text;
if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) {
$result .= $text;
} else {
// TODO Handle non-space indentation
if ($indent < 0) {
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text);
} elseif ($indent > 0) {
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text);
} else {
$result .= $text;
}
}
}
return $result;
}
/**
* Precalculate the indentation at every token position.
*
* @return int[] Token position to indentation map
*/
private function calcIndentMap(): array {
$indentMap = [];
$indent = 0;
foreach ($this->tokens as $i => $token) {
$indentMap[] = $indent;
if ($token->id === \T_WHITESPACE) {
$content = $token->text;
$newlinePos = \strrpos($content, "\n");
if (false !== $newlinePos) {
$indent = \strlen($content) - $newlinePos - 1;
} elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG &&
$this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") {
// Special case: Newline at the end of opening tag followed by whitespace.
$indent = \strlen($content);
}
}
}
// Add a sentinel for one past end of the file
$indentMap[] = $indent;
return $indentMap;
}
}

View File

@ -0,0 +1,108 @@
<?php declare(strict_types=1);
namespace PhpParser;
class JsonDecoder {
/** @var \ReflectionClass<Node>[] Node type to reflection class map */
private array $reflectionClassCache;
/** @return mixed */
public function decode(string $json) {
$value = json_decode($json, true);
if (json_last_error()) {
throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
}
return $this->decodeRecursive($value);
}
/**
* @param mixed $value
* @return mixed
*/
private function decodeRecursive($value) {
if (\is_array($value)) {
if (isset($value['nodeType'])) {
if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') {
return $this->decodeComment($value);
}
return $this->decodeNode($value);
}
return $this->decodeArray($value);
}
return $value;
}
private function decodeArray(array $array): array {
$decodedArray = [];
foreach ($array as $key => $value) {
$decodedArray[$key] = $this->decodeRecursive($value);
}
return $decodedArray;
}
private function decodeNode(array $value): Node {
$nodeType = $value['nodeType'];
if (!\is_string($nodeType)) {
throw new \RuntimeException('Node type must be a string');
}
$reflectionClass = $this->reflectionClassFromNodeType($nodeType);
$node = $reflectionClass->newInstanceWithoutConstructor();
if (isset($value['attributes'])) {
if (!\is_array($value['attributes'])) {
throw new \RuntimeException('Attributes must be an array');
}
$node->setAttributes($this->decodeArray($value['attributes']));
}
foreach ($value as $name => $subNode) {
if ($name === 'nodeType' || $name === 'attributes') {
continue;
}
$node->$name = $this->decodeRecursive($subNode);
}
return $node;
}
private function decodeComment(array $value): Comment {
$className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class;
if (!isset($value['text'])) {
throw new \RuntimeException('Comment must have text');
}
return new $className(
$value['text'],
$value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1,
$value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1
);
}
/** @return \ReflectionClass<Node> */
private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass {
if (!isset($this->reflectionClassCache[$nodeType])) {
$className = $this->classNameFromNodeType($nodeType);
$this->reflectionClassCache[$nodeType] = new \ReflectionClass($className);
}
return $this->reflectionClassCache[$nodeType];
}
/** @return class-string<Node> */
private function classNameFromNodeType(string $nodeType): string {
$className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
if (class_exists($className)) {
return $className;
}
$className .= '_';
if (class_exists($className)) {
return $className;
}
throw new \RuntimeException("Unknown node type \"$nodeType\"");
}
}

View File

@ -0,0 +1,116 @@
<?php declare(strict_types=1);
namespace PhpParser;
require __DIR__ . '/compatibility_tokens.php';
class Lexer {
/**
* Tokenize the provided source code.
*
* The token array is in the same format as provided by the PhpToken::tokenize() method in
* PHP 8.0. The tokens are instances of PhpParser\Token, to abstract over a polyfill
* implementation in earlier PHP version.
*
* The token array is terminated by a sentinel token with token ID 0.
* The token array does not discard any tokens (i.e. whitespace and comments are included).
* The token position attributes are against this token array.
*
* @param string $code The source code to tokenize.
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
* ErrorHandler\Throwing.
* @return Token[] Tokens
*/
public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
if (null === $errorHandler) {
$errorHandler = new ErrorHandler\Throwing();
}
$scream = ini_set('xdebug.scream', '0');
$tokens = @Token::tokenize($code);
$this->postprocessTokens($tokens, $errorHandler);
if (false !== $scream) {
ini_set('xdebug.scream', $scream);
}
return $tokens;
}
private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void {
$chr = $token->text;
if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte';
} else {
$errorMsg = sprintf(
'Unexpected character "%s" (ASCII %d)', $chr, ord($chr)
);
}
$errorHandler->handleError(new Error($errorMsg, [
'startLine' => $token->line,
'endLine' => $token->line,
'startFilePos' => $token->pos,
'endFilePos' => $token->pos,
]));
}
private function isUnterminatedComment(Token $token): bool {
return $token->is([\T_COMMENT, \T_DOC_COMMENT])
&& substr($token->text, 0, 2) === '/*'
&& substr($token->text, -2) !== '*/';
}
/**
* @param list<Token> $tokens
*/
protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void {
// This function reports errors (bad characters and unterminated comments) in the token
// array, and performs certain canonicalizations:
// * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
// T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
// * Add a sentinel token with ID 0.
$numTokens = \count($tokens);
if ($numTokens === 0) {
// Empty input edge case: Just add the sentinel token.
$tokens[] = new Token(0, "\0", 1, 0);
return;
}
for ($i = 0; $i < $numTokens; $i++) {
$token = $tokens[$i];
if ($token->id === \T_BAD_CHARACTER) {
$this->handleInvalidCharacter($token, $errorHandler);
}
if ($token->id === \ord('&')) {
$next = $i + 1;
while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) {
$next++;
}
$followedByVarOrVarArg = isset($tokens[$next]) &&
$tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]);
$token->id = $followedByVarOrVarArg
? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
: \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
}
}
// Check for unterminated comment
$lastToken = $tokens[$numTokens - 1];
if ($this->isUnterminatedComment($lastToken)) {
$errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $lastToken->line,
'endLine' => $lastToken->getEndLine(),
'startFilePos' => $lastToken->pos,
'endFilePos' => $lastToken->getEndPos(),
]));
}
// Add sentinel token.
$tokens[] = new Token(0, "\0", $lastToken->getEndLine(), $lastToken->getEndPos());
}
}

View File

@ -0,0 +1,226 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer;
use PhpParser\Error;
use PhpParser\ErrorHandler;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
class Emulative extends Lexer {
/** @var array{int, string, string}[] Patches used to reverse changes introduced in the code */
private array $patches = [];
/** @var list<TokenEmulator> */
private array $emulators = [];
private PhpVersion $targetPhpVersion;
private PhpVersion $hostPhpVersion;
/**
* @param PhpVersion|null $phpVersion PHP version to emulate. Defaults to newest supported.
*/
public function __construct(?PhpVersion $phpVersion = null) {
$this->targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported();
$this->hostPhpVersion = PhpVersion::getHostVersion();
$emulators = [
new MatchTokenEmulator(),
new NullsafeTokenEmulator(),
new AttributeEmulator(),
new EnumTokenEmulator(),
new ReadonlyTokenEmulator(),
new ExplicitOctalEmulator(),
new ReadonlyFunctionTokenEmulator(),
];
// Collect emulators that are relevant for the PHP version we're running
// and the PHP version we're targeting for emulation.
foreach ($emulators as $emulator) {
$emulatorPhpVersion = $emulator->getPhpVersion();
if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) {
$this->emulators[] = $emulator;
} elseif ($this->isReverseEmulationNeeded($emulatorPhpVersion)) {
$this->emulators[] = new ReverseEmulator($emulator);
}
}
}
public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
$emulators = array_filter($this->emulators, function ($emulator) use ($code) {
return $emulator->isEmulationNeeded($code);
});
if (empty($emulators)) {
// Nothing to emulate, yay
return parent::tokenize($code, $errorHandler);
}
if ($errorHandler === null) {
$errorHandler = new ErrorHandler\Throwing();
}
$this->patches = [];
foreach ($emulators as $emulator) {
$code = $emulator->preprocessCode($code, $this->patches);
}
$collector = new ErrorHandler\Collecting();
$tokens = parent::tokenize($code, $collector);
$this->sortPatches();
$tokens = $this->fixupTokens($tokens);
$errors = $collector->getErrors();
if (!empty($errors)) {
$this->fixupErrors($errors);
foreach ($errors as $error) {
$errorHandler->handleError($error);
}
}
foreach ($emulators as $emulator) {
$tokens = $emulator->emulate($code, $tokens);
}
return $tokens;
}
private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
return $this->hostPhpVersion->older($emulatorPhpVersion)
&& $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion);
}
private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion)
&& $this->targetPhpVersion->older($emulatorPhpVersion);
}
private function sortPatches(): void {
// Patches may be contributed by different emulators.
// Make sure they are sorted by increasing patch position.
usort($this->patches, function ($p1, $p2) {
return $p1[0] <=> $p2[0];
});
}
/**
* @param list<Token> $tokens
* @return list<Token>
*/
private function fixupTokens(array $tokens): array {
if (\count($this->patches) === 0) {
return $tokens;
}
// Load first patch
$patchIdx = 0;
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// We use a manual loop over the tokens, because we modify the array on the fly
$posDelta = 0;
$lineDelta = 0;
for ($i = 0, $c = \count($tokens); $i < $c; $i++) {
$token = $tokens[$i];
$pos = $token->pos;
$token->pos += $posDelta;
$token->line += $lineDelta;
$localPosDelta = 0;
$len = \strlen($token->text);
while ($patchPos >= $pos && $patchPos < $pos + $len) {
$patchTextLen = \strlen($patchText);
if ($patchType === 'remove') {
if ($patchPos === $pos && $patchTextLen === $len) {
// Remove token entirely
array_splice($tokens, $i, 1, []);
$i--;
$c--;
} else {
// Remove from token string
$token->text = substr_replace(
$token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen
);
$localPosDelta -= $patchTextLen;
}
$lineDelta -= \substr_count($patchText, "\n");
} elseif ($patchType === 'add') {
// Insert into the token string
$token->text = substr_replace(
$token->text, $patchText, $patchPos - $pos + $localPosDelta, 0
);
$localPosDelta += $patchTextLen;
$lineDelta += \substr_count($patchText, "\n");
} elseif ($patchType === 'replace') {
// Replace inside the token string
$token->text = substr_replace(
$token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen
);
} else {
assert(false);
}
// Fetch the next patch
$patchIdx++;
if ($patchIdx >= \count($this->patches)) {
// No more patches. However, we still need to adjust position.
$patchPos = \PHP_INT_MAX;
break;
}
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
}
$posDelta += $localPosDelta;
}
return $tokens;
}
/**
* Fixup line and position information in errors.
*
* @param Error[] $errors
*/
private function fixupErrors(array $errors): void {
foreach ($errors as $error) {
$attrs = $error->getAttributes();
$posDelta = 0;
$lineDelta = 0;
foreach ($this->patches as $patch) {
list($patchPos, $patchType, $patchText) = $patch;
if ($patchPos >= $attrs['startFilePos']) {
// No longer relevant
break;
}
if ($patchType === 'add') {
$posDelta += strlen($patchText);
$lineDelta += substr_count($patchText, "\n");
} elseif ($patchType === 'remove') {
$posDelta -= strlen($patchText);
$lineDelta -= substr_count($patchText, "\n");
}
}
$attrs['startFilePos'] += $posDelta;
$attrs['endFilePos'] += $posDelta;
$attrs['startLine'] += $lineDelta;
$attrs['endLine'] += $lineDelta;
$error->setAttributes($attrs);
}
}
}

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
final class AttributeEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}
public function isEmulationNeeded(string $code): bool {
return strpos($code, '#[') !== false;
}
public function emulate(string $code, array $tokens): array {
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way.
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if ($token->text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') {
array_splice($tokens, $i, 2, [
new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos),
]);
$c--;
continue;
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array {
// TODO
return $tokens;
}
public function preprocessCode(string $code, array &$patches): string {
$pos = 0;
while (false !== $pos = strpos($code, '#[', $pos)) {
// Replace #[ with %[
$code[$pos] = '%';
$patches[] = [$pos, 'replace', '#'];
$pos += 2;
}
return $code;
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
final class EnumTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}
public function getKeywordString(): string {
return 'enum';
}
public function getKeywordToken(): int {
return \T_ENUM;
}
protected function isKeywordContext(array $tokens, int $pos): bool {
return parent::isKeywordContext($tokens, $pos)
&& isset($tokens[$pos + 2])
&& $tokens[$pos + 1]->id === \T_WHITESPACE
&& $tokens[$pos + 2]->id === \T_STRING;
}
}

View File

@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
class ExplicitOctalEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}
public function isEmulationNeeded(string $code): bool {
return strpos($code, '0o') !== false || strpos($code, '0O') !== false;
}
public function emulate(string $code, array $tokens): array {
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if ($token->id == \T_LNUMBER && $token->text === '0' &&
isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING &&
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text)
) {
$tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text);
array_splice($tokens, $i, 2, [
new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos),
]);
$c--;
}
}
return $tokens;
}
private function resolveIntegerOrFloatToken(string $str): int {
$str = substr($str, 1);
$str = str_replace('_', '', $str);
$num = octdec($str);
return is_float($num) ? \T_DNUMBER : \T_LNUMBER;
}
public function reverseEmulate(string $code, array $tokens): array {
// Explicit octals were not legal code previously, don't bother.
return $tokens;
}
}

View File

@ -0,0 +1,56 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Token;
abstract class KeywordEmulator extends TokenEmulator {
abstract public function getKeywordString(): string;
abstract public function getKeywordToken(): int;
public function isEmulationNeeded(string $code): bool {
return strpos(strtolower($code), $this->getKeywordString()) !== false;
}
/** @param Token[] $tokens */
protected function isKeywordContext(array $tokens, int $pos): bool {
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
return $previousNonSpaceToken === null || $previousNonSpaceToken->id !== \T_OBJECT_OPERATOR;
}
public function emulate(string $code, array $tokens): array {
$keywordString = $this->getKeywordString();
foreach ($tokens as $i => $token) {
if ($token->id === T_STRING && strtolower($token->text) === $keywordString
&& $this->isKeywordContext($tokens, $i)) {
$token->id = $this->getKeywordToken();
}
}
return $tokens;
}
/** @param Token[] $tokens */
private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token {
for ($i = $start - 1; $i >= 0; --$i) {
if ($tokens[$i]->id === T_WHITESPACE) {
continue;
}
return $tokens[$i];
}
return null;
}
public function reverseEmulate(string $code, array $tokens): array {
$keywordToken = $this->getKeywordToken();
foreach ($tokens as $token) {
if ($token->id === $keywordToken) {
$token->id = \T_STRING;
}
}
return $tokens;
}
}

View File

@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
final class MatchTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}
public function getKeywordString(): string {
return 'match';
}
public function getKeywordToken(): int {
return \T_MATCH;
}
}

View File

@ -0,0 +1,60 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
final class NullsafeTokenEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 0);
}
public function isEmulationNeeded(string $code): bool {
return strpos($code, '?->') !== false;
}
public function emulate(string $code, array $tokens): array {
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if ($token->text === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id === \T_OBJECT_OPERATOR) {
array_splice($tokens, $i, 2, [
new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
]);
$c--;
continue;
}
// Handle ?-> inside encapsed string.
if ($token->id === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
&& $tokens[$i - 1]->id === \T_VARIABLE
&& preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $token->text, $matches)
) {
$replacement = [
new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos),
new Token(\T_STRING, $matches[1], $token->line, $token->pos + 3),
];
$matchLen = \strlen($matches[0]);
if ($matchLen !== \strlen($token->text)) {
$replacement[] = new Token(
\T_ENCAPSED_AND_WHITESPACE,
\substr($token->text, $matchLen),
$token->line, $token->pos + $matchLen
);
}
array_splice($tokens, $i, 1, $replacement);
$c += \count($replacement) - 1;
continue;
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array {
// ?-> was not valid code previously, don't bother.
return $tokens;
}
}

View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
/*
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
* name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
* this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
* parser. This emulator only exists to handle this special case, which is skipped by the
* PHP 8.1 ReadonlyTokenEmulator.
*/
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
public function getKeywordString(): string {
return 'readonly';
}
public function getKeywordToken(): int {
return \T_READONLY;
}
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 2);
}
public function reverseEmulate(string $code, array $tokens): array {
// Don't bother
return $tokens;
}
}

View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
final class ReadonlyTokenEmulator extends KeywordEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 1);
}
public function getKeywordString(): string {
return 'readonly';
}
public function getKeywordToken(): int {
return \T_READONLY;
}
protected function isKeywordContext(array $tokens, int $pos): bool {
if (!parent::isKeywordContext($tokens, $pos)) {
return false;
}
// Support "function readonly("
return !(isset($tokens[$pos + 1]) &&
($tokens[$pos + 1]->text === '(' ||
($tokens[$pos + 1]->id === \T_WHITESPACE &&
isset($tokens[$pos + 2]) &&
$tokens[$pos + 2]->text === '(')));
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
/**
* Reverses emulation direction of the inner emulator.
*/
final class ReverseEmulator extends TokenEmulator {
/** @var TokenEmulator Inner emulator */
private TokenEmulator $emulator;
public function __construct(TokenEmulator $emulator) {
$this->emulator = $emulator;
}
public function getPhpVersion(): PhpVersion {
return $this->emulator->getPhpVersion();
}
public function isEmulationNeeded(string $code): bool {
return $this->emulator->isEmulationNeeded($code);
}
public function emulate(string $code, array $tokens): array {
return $this->emulator->reverseEmulate($code, $tokens);
}
public function reverseEmulate(string $code, array $tokens): array {
return $this->emulator->emulate($code, $tokens);
}
public function preprocessCode(string $code, array &$patches): string {
return $code;
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\PhpVersion;
use PhpParser\Token;
/** @internal */
abstract class TokenEmulator {
abstract public function getPhpVersion(): PhpVersion;
abstract public function isEmulationNeeded(string $code): bool;
/**
* @param Token[] $tokens Original tokens
* @return Token[] Modified Tokens
*/
abstract public function emulate(string $code, array $tokens): array;
/**
* @param Token[] $tokens Original tokens
* @return Token[] Modified Tokens
*/
abstract public function reverseEmulate(string $code, array $tokens): array;
/** @param array{int, string, string}[] $patches */
public function preprocessCode(string $code, array &$patches): string {
return $code;
}
}

View File

@ -0,0 +1,69 @@
<?php declare(strict_types=1);
namespace PhpParser;
/**
* Modifiers used (as a bit mask) by various flags subnodes, for example on classes, functions,
* properties and constants.
*/
final class Modifiers {
public const PUBLIC = 1;
public const PROTECTED = 2;
public const PRIVATE = 4;
public const STATIC = 8;
public const ABSTRACT = 16;
public const FINAL = 32;
public const READONLY = 64;
public const VISIBILITY_MASK = 1 | 2 | 4;
/**
* @internal
*/
public static function verifyClassModifier(int $a, int $b): void {
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
throw new Error('Multiple abstract modifiers are not allowed');
}
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
throw new Error('Multiple final modifiers are not allowed');
}
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
throw new Error('Multiple readonly modifiers are not allowed');
}
if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class');
}
}
/**
* @internal
*/
public static function verifyModifier(int $a, int $b): void {
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
throw new Error('Multiple access type modifiers are not allowed');
}
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
throw new Error('Multiple abstract modifiers are not allowed');
}
if ($a & Modifiers::STATIC && $b & Modifiers::STATIC) {
throw new Error('Multiple static modifiers are not allowed');
}
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
throw new Error('Multiple final modifiers are not allowed');
}
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
throw new Error('Multiple readonly modifiers are not allowed');
}
if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class member');
}
}
}

View File

@ -0,0 +1,284 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
class NameContext {
/** @var null|Name Current namespace */
protected ?Name $namespace;
/** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
protected array $aliases = [];
/** @var Name[][] Same as $aliases but preserving original case */
protected array $origAliases = [];
/** @var ErrorHandler Error handler */
protected ErrorHandler $errorHandler;
/**
* Create a name context.
*
* @param ErrorHandler $errorHandler Error handling used to report errors
*/
public function __construct(ErrorHandler $errorHandler) {
$this->errorHandler = $errorHandler;
}
/**
* Start a new namespace.
*
* This also resets the alias table.
*
* @param Name|null $namespace Null is the global namespace
*/
public function startNamespace(?Name $namespace = null): void {
$this->namespace = $namespace;
$this->origAliases = $this->aliases = [
Stmt\Use_::TYPE_NORMAL => [],
Stmt\Use_::TYPE_FUNCTION => [],
Stmt\Use_::TYPE_CONSTANT => [],
];
}
/**
* Add an alias / import.
*
* @param Name $name Original name
* @param string $aliasName Aliased name
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
* @param array<string, mixed> $errorAttrs Attributes to use to report an error
*/
public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void {
// Constant names are case sensitive, everything else case insensitive
if ($type === Stmt\Use_::TYPE_CONSTANT) {
$aliasLookupName = $aliasName;
} else {
$aliasLookupName = strtolower($aliasName);
}
if (isset($this->aliases[$type][$aliasLookupName])) {
$typeStringMap = [
Stmt\Use_::TYPE_NORMAL => '',
Stmt\Use_::TYPE_FUNCTION => 'function ',
Stmt\Use_::TYPE_CONSTANT => 'const ',
];
$this->errorHandler->handleError(new Error(
sprintf(
'Cannot use %s%s as %s because the name is already in use',
$typeStringMap[$type], $name, $aliasName
),
$errorAttrs
));
return;
}
$this->aliases[$type][$aliasLookupName] = $name;
$this->origAliases[$type][$aliasName] = $name;
}
/**
* Get current namespace.
*
* @return null|Name Namespace (or null if global namespace)
*/
public function getNamespace(): ?Name {
return $this->namespace;
}
/**
* Get resolved name.
*
* @param Name $name Name to resolve
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT}
*
* @return null|Name Resolved name, or null if static resolution is not possible
*/
public function getResolvedName(Name $name, int $type): ?Name {
// don't resolve special class names
if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) {
if (!$name->isUnqualified()) {
$this->errorHandler->handleError(new Error(
sprintf("'\\%s' is an invalid class name", $name->toString()),
$name->getAttributes()
));
}
return $name;
}
// fully qualified names are already resolved
if ($name->isFullyQualified()) {
return $name;
}
// Try to resolve aliases
if (null !== $resolvedName = $this->resolveAlias($name, $type)) {
return $resolvedName;
}
if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) {
if (null === $this->namespace) {
// outside of a namespace unaliased unqualified is same as fully qualified
return new FullyQualified($name, $name->getAttributes());
}
// Cannot resolve statically
return null;
}
// if no alias exists prepend current namespace
return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
}
/**
* Get resolved class name.
*
* @param Name $name Class ame to resolve
*
* @return Name Resolved name
*/
public function getResolvedClassName(Name $name): Name {
return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL);
}
/**
* Get possible ways of writing a fully qualified name (e.g., by making use of aliases).
*
* @param string $name Fully-qualified name (without leading namespace separator)
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
*
* @return Name[] Possible representations of the name
*/
public function getPossibleNames(string $name, int $type): array {
$lcName = strtolower($name);
if ($type === Stmt\Use_::TYPE_NORMAL) {
// self, parent and static must always be unqualified
if ($lcName === "self" || $lcName === "parent" || $lcName === "static") {
return [new Name($name)];
}
}
// Collect possible ways to write this name, starting with the fully-qualified name
$possibleNames = [new FullyQualified($name)];
if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) {
// Make sure there is no alias that makes the normally namespace-relative name
// into something else
if (null === $this->resolveAlias($nsRelativeName, $type)) {
$possibleNames[] = $nsRelativeName;
}
}
// Check for relevant namespace use statements
foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) {
$lcOrig = $orig->toLowerString();
if (0 === strpos($lcName, $lcOrig . '\\')) {
$possibleNames[] = new Name($alias . substr($name, strlen($lcOrig)));
}
}
// Check for relevant type-specific use statements
foreach ($this->origAliases[$type] as $alias => $orig) {
if ($type === Stmt\Use_::TYPE_CONSTANT) {
// Constants are are complicated-sensitive
$normalizedOrig = $this->normalizeConstName($orig->toString());
if ($normalizedOrig === $this->normalizeConstName($name)) {
$possibleNames[] = new Name($alias);
}
} else {
// Everything else is case-insensitive
if ($orig->toLowerString() === $lcName) {
$possibleNames[] = new Name($alias);
}
}
}
return $possibleNames;
}
/**
* Get shortest representation of this fully-qualified name.
*
* @param string $name Fully-qualified name (without leading namespace separator)
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
*
* @return Name Shortest representation
*/
public function getShortName(string $name, int $type): Name {
$possibleNames = $this->getPossibleNames($name, $type);
// Find shortest name
$shortestName = null;
$shortestLength = \INF;
foreach ($possibleNames as $possibleName) {
$length = strlen($possibleName->toCodeString());
if ($length < $shortestLength) {
$shortestName = $possibleName;
$shortestLength = $length;
}
}
return $shortestName;
}
private function resolveAlias(Name $name, int $type): ?FullyQualified {
$firstPart = $name->getFirst();
if ($name->isQualified()) {
// resolve aliases for qualified names, always against class alias table
$checkName = strtolower($firstPart);
if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) {
$alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName];
return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
}
} elseif ($name->isUnqualified()) {
// constant aliases are case-sensitive, function aliases case-insensitive
$checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart);
if (isset($this->aliases[$type][$checkName])) {
// resolve unqualified aliases
return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes());
}
}
// No applicable aliases
return null;
}
private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name {
if (null === $this->namespace) {
return new Name($name);
}
if ($type === Stmt\Use_::TYPE_CONSTANT) {
// The constants true/false/null always resolve to the global symbols, even inside a
// namespace, so they may be used without qualification
if ($lcName === "true" || $lcName === "false" || $lcName === "null") {
return new Name($name);
}
}
$namespacePrefix = strtolower($this->namespace . '\\');
if (0 === strpos($lcName, $namespacePrefix)) {
return new Name(substr($name, strlen($namespacePrefix)));
}
return null;
}
private function normalizeConstName(string $name): string {
$nsSep = strrpos($name, '\\');
if (false === $nsSep) {
return $name;
}
// Constants have case-insensitive namespace and case-sensitive short-name
$ns = substr($name, 0, $nsSep);
$shortName = substr($name, $nsSep + 1);
return strtolower($ns) . '\\' . $shortName;
}
}

View File

@ -0,0 +1,150 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface Node {
/**
* Gets the type of the node.
*
* @psalm-return non-empty-string
* @return string Type of the node
*/
public function getType(): string;
/**
* Gets the names of the sub nodes.
*
* @return string[] Names of sub nodes
*/
public function getSubNodeNames(): array;
/**
* Gets line the node started in (alias of getStartLine).
*
* @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*
* @deprecated Use getStartLine() instead
*/
public function getLine(): int;
/**
* Gets line the node started in.
*
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getStartLine(): int;
/**
* Gets the line the node ended in.
*
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int End line (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getEndLine(): int;
/**
* Gets the token offset of the first token that is part of this node.
*
* The offset is an index into the array returned by Lexer::getTokens().
*
* Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int Token start position (or -1 if not available)
*/
public function getStartTokenPos(): int;
/**
* Gets the token offset of the last token that is part of this node.
*
* The offset is an index into the array returned by Lexer::getTokens().
*
* Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int Token end position (or -1 if not available)
*/
public function getEndTokenPos(): int;
/**
* Gets the file offset of the first character that is part of this node.
*
* Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int File start position (or -1 if not available)
*/
public function getStartFilePos(): int;
/**
* Gets the file offset of the last character that is part of this node.
*
* Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int File end position (or -1 if not available)
*/
public function getEndFilePos(): int;
/**
* Gets all comments directly preceding this node.
*
* The comments are also available through the "comments" attribute.
*
* @return Comment[]
*/
public function getComments(): array;
/**
* Gets the doc comment of the node.
*
* @return null|Comment\Doc Doc comment object or null
*/
public function getDocComment(): ?Comment\Doc;
/**
* Sets the doc comment of the node.
*
* This will either replace an existing doc comment or add it to the comments array.
*
* @param Comment\Doc $docComment Doc comment to set
*/
public function setDocComment(Comment\Doc $docComment): void;
/**
* Sets an attribute on a node.
*
* @param mixed $value
*/
public function setAttribute(string $key, $value): void;
/**
* Returns whether an attribute exists.
*/
public function hasAttribute(string $key): bool;
/**
* Returns the value of an attribute.
*
* @param mixed $default
*
* @return mixed
*/
public function getAttribute(string $key, $default = null);
/**
* Returns all the attributes of this node.
*
* @return array<string, mixed>
*/
public function getAttributes(): array;
/**
* Replaces all the attributes of this node.
*
* @param array<string, mixed> $attributes
*/
public function setAttributes(array $attributes): void;
}

View File

@ -0,0 +1,44 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class Arg extends NodeAbstract {
/** @var Identifier|null Parameter name (for named parameters) */
public ?Identifier $name;
/** @var Expr Value to pass */
public Expr $value;
/** @var bool Whether to pass by ref */
public bool $byRef;
/** @var bool Whether to unpack the argument */
public bool $unpack;
/**
* Constructs a function call argument node.
*
* @param Expr $value Value to pass
* @param bool $byRef Whether to pass by ref
* @param bool $unpack Whether to unpack the argument
* @param array<string, mixed> $attributes Additional attributes
* @param Identifier|null $name Parameter name (for named parameters)
*/
public function __construct(
Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
?Identifier $name = null
) {
$this->attributes = $attributes;
$this->name = $name;
$this->value = $value;
$this->byRef = $byRef;
$this->unpack = $unpack;
}
public function getSubNodeNames(): array {
return ['name', 'value', 'byRef', 'unpack'];
}
public function getType(): string {
return 'Arg';
}
}

View File

@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class ArrayItem extends NodeAbstract {
/** @var null|Expr Key */
public ?Expr $key;
/** @var Expr Value */
public Expr $value;
/** @var bool Whether to assign by reference */
public bool $byRef;
/** @var bool Whether to unpack the argument */
public bool $unpack;
/**
* Constructs an array item node.
*
* @param Expr $value Value
* @param null|Expr $key Key
* @param bool $byRef Whether to assign by reference
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $value, ?Expr $key = null, bool $byRef = false, array $attributes = [], bool $unpack = false) {
$this->attributes = $attributes;
$this->key = $key;
$this->value = $value;
$this->byRef = $byRef;
$this->unpack = $unpack;
}
public function getSubNodeNames(): array {
return ['key', 'value', 'byRef', 'unpack'];
}
public function getType(): string {
return 'ArrayItem';
}
}
// @deprecated compatibility alias
class_alias(ArrayItem::class, Expr\ArrayItem::class);

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class Attribute extends NodeAbstract {
/** @var Name Attribute name */
public Name $name;
/** @var list<Arg> Attribute arguments */
public array $args;
/**
* @param Node\Name $name Attribute name
* @param list<Arg> $args Attribute arguments
* @param array<string, mixed> $attributes Additional node attributes
*/
public function __construct(Name $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['name', 'args'];
}
public function getType(): string {
return 'Attribute';
}
}

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class AttributeGroup extends NodeAbstract {
/** @var Attribute[] Attributes */
public array $attrs;
/**
* @param Attribute[] $attrs PHP attributes
* @param array<string, mixed> $attributes Additional node attributes
*/
public function __construct(array $attrs, array $attributes = []) {
$this->attributes = $attributes;
$this->attrs = $attrs;
}
public function getSubNodeNames(): array {
return ['attrs'];
}
public function getType(): string {
return 'AttributeGroup';
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class ClosureUse extends NodeAbstract {
/** @var Expr\Variable Variable to use */
public Expr\Variable $var;
/** @var bool Whether to use by reference */
public bool $byRef;
/**
* Constructs a closure use node.
*
* @param Expr\Variable $var Variable to use
* @param bool $byRef Whether to use by reference
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->byRef = $byRef;
}
public function getSubNodeNames(): array {
return ['var', 'byRef'];
}
public function getType(): string {
return 'ClosureUse';
}
}
// @deprecated compatibility alias
class_alias(ClosureUse::class, Expr\ClosureUse::class);

View File

@ -0,0 +1,13 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
/**
* This is a base class for complex types, including nullable types and union types.
*
* It does not provide any shared behavior and exists only for type-checking purposes.
*/
abstract class ComplexType extends NodeAbstract {
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class Const_ extends NodeAbstract {
/** @var Identifier Name */
public Identifier $name;
/** @var Expr Value */
public Expr $value;
/** @var Name|null Namespaced name (if using NameResolver) */
public ?Name $namespacedName;
/**
* Constructs a const node for use in class const and const statements.
*
* @param string|Identifier $name Name
* @param Expr $value Value
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, Expr $value, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['name', 'value'];
}
public function getType(): string {
return 'Const';
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class DeclareItem extends NodeAbstract {
/** @var Node\Identifier Key */
public Identifier $key;
/** @var Node\Expr Value */
public Expr $value;
/**
* Constructs a declare key=>value pair node.
*
* @param string|Node\Identifier $key Key
* @param Node\Expr $value Value
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($key, Node\Expr $value, array $attributes = []) {
$this->attributes = $attributes;
$this->key = \is_string($key) ? new Node\Identifier($key) : $key;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['key', 'value'];
}
public function getType(): string {
return 'DeclareItem';
}
}
// @deprecated compatibility alias
class_alias(DeclareItem::class, Stmt\DeclareDeclare::class);

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
abstract class Expr extends NodeAbstract {
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class ArrayDimFetch extends Expr {
/** @var Expr Variable */
public Expr $var;
/** @var null|Expr Array index / dim */
public ?Expr $dim;
/**
* Constructs an array index fetch node.
*
* @param Expr $var Variable
* @param null|Expr $dim Array index / dim
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->dim = $dim;
}
public function getSubNodeNames(): array {
return ['var', 'dim'];
}
public function getType(): string {
return 'Expr_ArrayDimFetch';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../ArrayItem.php';

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;
class Array_ extends Expr {
// For use in "kind" attribute
public const KIND_LONG = 1; // array() syntax
public const KIND_SHORT = 2; // [] syntax
/** @var ArrayItem[] Items */
public array $items;
/**
* Constructs an array node.
*
* @param ArrayItem[] $items Items of the array
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $items = [], array $attributes = []) {
$this->attributes = $attributes;
$this->items = $items;
}
public function getSubNodeNames(): array {
return ['items'];
}
public function getType(): string {
return 'Expr_Array';
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;
class ArrowFunction extends Expr implements FunctionLike {
/** @var bool Whether the closure is static */
public bool $static;
/** @var bool Whether to return by reference */
public bool $byRef;
/** @var Node\Param[] */
public array $params = [];
/** @var null|Node\Identifier|Node\Name|Node\ComplexType */
public ?Node $returnType;
/** @var Expr Expression body */
public Expr $expr;
/** @var Node\AttributeGroup[] */
public array $attrGroups;
/**
* @param array{
* expr: Expr,
* static?: bool,
* byRef?: bool,
* params?: Node\Param[],
* returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
* attrGroups?: Node\AttributeGroup[]
* } $subNodes Array of the following subnodes:
* 'expr' : Expression body
* 'static' => false : Whether the closure is static
* 'byRef' => false : Whether to return by reference
* 'params' => array() : Parameters
* 'returnType' => null : Return type
* 'attrGroups' => array() : PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $subNodes, array $attributes = []) {
$this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? [];
$this->returnType = $subNodes['returnType'] ?? null;
$this->expr = $subNodes['expr'];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr'];
}
public function returnsByRef(): bool {
return $this->byRef;
}
public function getParams(): array {
return $this->params;
}
public function getReturnType() {
return $this->returnType;
}
public function getAttrGroups(): array {
return $this->attrGroups;
}
/**
* @return Node\Stmt\Return_[]
*/
public function getStmts(): array {
return [new Node\Stmt\Return_($this->expr)];
}
public function getType(): string {
return 'Expr_ArrowFunction';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Assign extends Expr {
/** @var Expr Variable */
public Expr $var;
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs an assignment node.
*
* @param Expr $var Variable
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['var', 'expr'];
}
public function getType(): string {
return 'Expr_Assign';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
abstract class AssignOp extends Expr {
/** @var Expr Variable */
public Expr $var;
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a compound assignment operation node.
*
* @param Expr $var Variable
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['var', 'expr'];
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class BitwiseAnd extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseAnd';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class BitwiseOr extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseOr';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class BitwiseXor extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_BitwiseXor';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Coalesce extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Coalesce';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Concat extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Concat';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Div extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Div';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Minus extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Minus';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Mod extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Mod';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Mul extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Mul';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Plus extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Plus';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class Pow extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_Pow';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class ShiftLeft extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_ShiftLeft';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\AssignOp;
class ShiftRight extends AssignOp {
public function getType(): string {
return 'Expr_AssignOp_ShiftRight';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class AssignRef extends Expr {
/** @var Expr Variable reference is assigned to */
public Expr $var;
/** @var Expr Variable which is referenced */
public Expr $expr;
/**
* Constructs an assignment node.
*
* @param Expr $var Variable
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['var', 'expr'];
}
public function getType(): string {
return 'Expr_AssignRef';
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
abstract class BinaryOp extends Expr {
/** @var Expr The left hand side expression */
public Expr $left;
/** @var Expr The right hand side expression */
public Expr $right;
/**
* Constructs a binary operator node.
*
* @param Expr $left The left hand side expression
* @param Expr $right The right hand side expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $left, Expr $right, array $attributes = []) {
$this->attributes = $attributes;
$this->left = $left;
$this->right = $right;
}
public function getSubNodeNames(): array {
return ['left', 'right'];
}
/**
* Get the operator sigil for this binary operation.
*
* In the case there are multiple possible sigils for an operator, this method does not
* necessarily return the one used in the parsed code.
*/
abstract public function getOperatorSigil(): string;
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class BitwiseAnd extends BinaryOp {
public function getOperatorSigil(): string {
return '&';
}
public function getType(): string {
return 'Expr_BinaryOp_BitwiseAnd';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class BitwiseOr extends BinaryOp {
public function getOperatorSigil(): string {
return '|';
}
public function getType(): string {
return 'Expr_BinaryOp_BitwiseOr';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class BitwiseXor extends BinaryOp {
public function getOperatorSigil(): string {
return '^';
}
public function getType(): string {
return 'Expr_BinaryOp_BitwiseXor';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class BooleanAnd extends BinaryOp {
public function getOperatorSigil(): string {
return '&&';
}
public function getType(): string {
return 'Expr_BinaryOp_BooleanAnd';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class BooleanOr extends BinaryOp {
public function getOperatorSigil(): string {
return '||';
}
public function getType(): string {
return 'Expr_BinaryOp_BooleanOr';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Coalesce extends BinaryOp {
public function getOperatorSigil(): string {
return '??';
}
public function getType(): string {
return 'Expr_BinaryOp_Coalesce';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Concat extends BinaryOp {
public function getOperatorSigil(): string {
return '.';
}
public function getType(): string {
return 'Expr_BinaryOp_Concat';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Div extends BinaryOp {
public function getOperatorSigil(): string {
return '/';
}
public function getType(): string {
return 'Expr_BinaryOp_Div';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Equal extends BinaryOp {
public function getOperatorSigil(): string {
return '==';
}
public function getType(): string {
return 'Expr_BinaryOp_Equal';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Greater extends BinaryOp {
public function getOperatorSigil(): string {
return '>';
}
public function getType(): string {
return 'Expr_BinaryOp_Greater';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class GreaterOrEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '>=';
}
public function getType(): string {
return 'Expr_BinaryOp_GreaterOrEqual';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Identical extends BinaryOp {
public function getOperatorSigil(): string {
return '===';
}
public function getType(): string {
return 'Expr_BinaryOp_Identical';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class LogicalAnd extends BinaryOp {
public function getOperatorSigil(): string {
return 'and';
}
public function getType(): string {
return 'Expr_BinaryOp_LogicalAnd';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class LogicalOr extends BinaryOp {
public function getOperatorSigil(): string {
return 'or';
}
public function getType(): string {
return 'Expr_BinaryOp_LogicalOr';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class LogicalXor extends BinaryOp {
public function getOperatorSigil(): string {
return 'xor';
}
public function getType(): string {
return 'Expr_BinaryOp_LogicalXor';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Minus extends BinaryOp {
public function getOperatorSigil(): string {
return '-';
}
public function getType(): string {
return 'Expr_BinaryOp_Minus';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Mod extends BinaryOp {
public function getOperatorSigil(): string {
return '%';
}
public function getType(): string {
return 'Expr_BinaryOp_Mod';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Mul extends BinaryOp {
public function getOperatorSigil(): string {
return '*';
}
public function getType(): string {
return 'Expr_BinaryOp_Mul';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class NotEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '!=';
}
public function getType(): string {
return 'Expr_BinaryOp_NotEqual';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class NotIdentical extends BinaryOp {
public function getOperatorSigil(): string {
return '!==';
}
public function getType(): string {
return 'Expr_BinaryOp_NotIdentical';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Plus extends BinaryOp {
public function getOperatorSigil(): string {
return '+';
}
public function getType(): string {
return 'Expr_BinaryOp_Plus';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Pow extends BinaryOp {
public function getOperatorSigil(): string {
return '**';
}
public function getType(): string {
return 'Expr_BinaryOp_Pow';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class ShiftLeft extends BinaryOp {
public function getOperatorSigil(): string {
return '<<';
}
public function getType(): string {
return 'Expr_BinaryOp_ShiftLeft';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class ShiftRight extends BinaryOp {
public function getOperatorSigil(): string {
return '>>';
}
public function getType(): string {
return 'Expr_BinaryOp_ShiftRight';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Smaller extends BinaryOp {
public function getOperatorSigil(): string {
return '<';
}
public function getType(): string {
return 'Expr_BinaryOp_Smaller';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class SmallerOrEqual extends BinaryOp {
public function getOperatorSigil(): string {
return '<=';
}
public function getType(): string {
return 'Expr_BinaryOp_SmallerOrEqual';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp;
class Spaceship extends BinaryOp {
public function getOperatorSigil(): string {
return '<=>';
}
public function getType(): string {
return 'Expr_BinaryOp_Spaceship';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class BitwiseNot extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a bitwise not node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_BitwiseNot';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class BooleanNot extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a boolean not node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_BooleanNot';
}
}

View File

@ -0,0 +1,35 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;
abstract class CallLike extends Expr {
/**
* Return raw arguments, which may be actual Args, or VariadicPlaceholders for first-class
* callables.
*
* @return array<Arg|VariadicPlaceholder>
*/
abstract public function getRawArgs(): array;
/**
* Returns whether this call expression is actually a first class callable.
*/
public function isFirstClassCallable(): bool {
$rawArgs = $this->getRawArgs();
return count($rawArgs) === 1 && current($rawArgs) instanceof VariadicPlaceholder;
}
/**
* Assert that this is not a first-class callable and return only ordinary Args.
*
* @return Arg[]
*/
public function getArgs(): array {
assert(!$this->isFirstClassCallable());
return $this->getRawArgs();
}
}

View File

@ -0,0 +1,25 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
abstract class Cast extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a cast node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Array_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Array';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Bool_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Bool';
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Double extends Cast {
// For use in "kind" attribute
public const KIND_DOUBLE = 1; // "double" syntax
public const KIND_FLOAT = 2; // "float" syntax
public const KIND_REAL = 3; // "real" syntax
public function getType(): string {
return 'Expr_Cast_Double';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Int_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Int';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Object_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Object';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class String_ extends Cast {
public function getType(): string {
return 'Expr_Cast_String';
}
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast;
class Unset_ extends Cast {
public function getType(): string {
return 'Expr_Cast_Unset';
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
class ClassConstFetch extends Expr {
/** @var Name|Expr Class name */
public Node $class;
/** @var Identifier|Expr|Error Constant name */
public Node $name;
/**
* Constructs a class const fetch node.
*
* @param Name|Expr $class Class name
* @param string|Identifier|Expr|Error $name Constant name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, $name, array $attributes = []) {
$this->attributes = $attributes;
$this->class = $class;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['class', 'name'];
}
public function getType(): string {
return 'Expr_ClassConstFetch';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Clone_ extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a clone node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Clone';
}
}

View File

@ -0,0 +1,86 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\ClosureUse;
use PhpParser\Node\Expr;
use PhpParser\Node\FunctionLike;
class Closure extends Expr implements FunctionLike {
/** @var bool Whether the closure is static */
public bool $static;
/** @var bool Whether to return by reference */
public bool $byRef;
/** @var Node\Param[] Parameters */
public array $params;
/** @var ClosureUse[] use()s */
public array $uses;
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
public ?Node $returnType;
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/**
* Constructs a lambda function node.
*
* @param array{
* static?: bool,
* byRef?: bool,
* params?: Node\Param[],
* uses?: ClosureUse[],
* returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'static' => false : Whether the closure is static
* 'byRef' => false : Whether to return by reference
* 'params' => array(): Parameters
* 'uses' => array(): use()s
* 'returnType' => null : Return type
* 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attributes groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? [];
$this->uses = $subNodes['uses'] ?? [];
$this->returnType = $subNodes['returnType'] ?? null;
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
}
public function returnsByRef(): bool {
return $this->byRef;
}
public function getParams(): array {
return $this->params;
}
public function getReturnType() {
return $this->returnType;
}
/** @return Node\Stmt[] */
public function getStmts(): array {
return $this->stmts;
}
public function getAttrGroups(): array {
return $this->attrGroups;
}
public function getType(): string {
return 'Expr_Closure';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../ClosureUse.php';

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
class ConstFetch extends Expr {
/** @var Name Constant name */
public Name $name;
/**
* Constructs a const fetch node.
*
* @param Name $name Constant name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Name $name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
}
public function getSubNodeNames(): array {
return ['name'];
}
public function getType(): string {
return 'Expr_ConstFetch';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Empty_ extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs an empty() node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Empty';
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
/**
* Error node used during parsing with error recovery.
*
* An error node may be placed at a position where an expression is required, but an error occurred.
* Error nodes will not be present if the parser is run in throwOnError mode (the default).
*/
class Error extends Expr {
/**
* Constructs an error node.
*
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}
public function getSubNodeNames(): array {
return [];
}
public function getType(): string {
return 'Expr_Error';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class ErrorSuppress extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs an error suppress node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_ErrorSuppress';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Eval_ extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs an eval() node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Eval';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Exit_ extends Expr {
/* For use in "kind" attribute */
public const KIND_EXIT = 1;
public const KIND_DIE = 2;
/** @var null|Expr Expression */
public ?Expr $expr;
/**
* Constructs an exit() node.
*
* @param null|Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Expr $expr = null, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Exit';
}
}

View File

@ -0,0 +1,38 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
class FuncCall extends CallLike {
/** @var Node\Name|Expr Function name */
public Node $name;
/** @var array<Node\Arg|Node\VariadicPlaceholder> Arguments */
public array $args;
/**
* Constructs a function call node.
*
* @param Node\Name|Expr $name Function name
* @param array<Node\Arg|Node\VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['name', 'args'];
}
public function getType(): string {
return 'Expr_FuncCall';
}
public function getRawArgs(): array {
return $this->args;
}
}

View File

@ -0,0 +1,38 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Include_ extends Expr {
public const TYPE_INCLUDE = 1;
public const TYPE_INCLUDE_ONCE = 2;
public const TYPE_REQUIRE = 3;
public const TYPE_REQUIRE_ONCE = 4;
/** @var Expr Expression */
public Expr $expr;
/** @var int Type of include */
public int $type;
/**
* Constructs an include node.
*
* @param Expr $expr Expression
* @param int $type Type of include
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, int $type, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
$this->type = $type;
}
public function getSubNodeNames(): array {
return ['expr', 'type'];
}
public function getType(): string {
return 'Expr_Include';
}
}

View File

@ -0,0 +1,35 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
class Instanceof_ extends Expr {
/** @var Expr Expression */
public Expr $expr;
/** @var Name|Expr Class name */
public Node $class;
/**
* Constructs an instanceof check node.
*
* @param Expr $expr Expression
* @param Name|Expr $class Class name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, Node $class, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
$this->class = $class;
}
public function getSubNodeNames(): array {
return ['expr', 'class'];
}
public function getType(): string {
return 'Expr_Instanceof';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Isset_ extends Expr {
/** @var Expr[] Variables */
public array $vars;
/**
* Constructs an array node.
*
* @param Expr[] $vars Variables
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}
public function getSubNodeNames(): array {
return ['vars'];
}
public function getType(): string {
return 'Expr_Isset';
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr;
class List_ extends Expr {
// For use in "kind" attribute
public const KIND_LIST = 1; // list() syntax
public const KIND_ARRAY = 2; // [] syntax
/** @var (ArrayItem|null)[] List of items to assign to */
public array $items;
/**
* Constructs a list() destructuring node.
*
* @param (ArrayItem|null)[] $items List of items to assign to
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $items, array $attributes = []) {
$this->attributes = $attributes;
$this->items = $items;
}
public function getSubNodeNames(): array {
return ['items'];
}
public function getType(): string {
return 'Expr_List';
}
}

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\MatchArm;
class Match_ extends Node\Expr {
/** @var Node\Expr Condition */
public Node\Expr $cond;
/** @var MatchArm[] */
public array $arms;
/**
* @param Node\Expr $cond Condition
* @param MatchArm[] $arms
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $arms = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->arms = $arms;
}
public function getSubNodeNames(): array {
return ['cond', 'arms'];
}
public function getType(): string {
return 'Expr_Match';
}
}

View File

@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;
class MethodCall extends CallLike {
/** @var Expr Variable holding object */
public Expr $var;
/** @var Identifier|Expr Method name */
public Node $name;
/** @var array<Arg|VariadicPlaceholder> Arguments */
public array $args;
/**
* Constructs a function call node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Method name
* @param array<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['var', 'name', 'args'];
}
public function getType(): string {
return 'Expr_MethodCall';
}
public function getRawArgs(): array {
return $this->args;
}
}

View File

@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\VariadicPlaceholder;
class New_ extends CallLike {
/** @var Node\Name|Expr|Node\Stmt\Class_ Class name */
public Node $class;
/** @var array<Arg|VariadicPlaceholder> Arguments */
public array $args;
/**
* Constructs a function call node.
*
* @param Node\Name|Expr|Node\Stmt\Class_ $class Class name (or class node for anonymous classes)
* @param array<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->class = $class;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['class', 'args'];
}
public function getType(): string {
return 'Expr_New';
}
public function getRawArgs(): array {
return $this->args;
}
}

View File

@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;
class NullsafeMethodCall extends CallLike {
/** @var Expr Variable holding object */
public Expr $var;
/** @var Identifier|Expr Method name */
public Node $name;
/** @var array<Arg|VariadicPlaceholder> Arguments */
public array $args;
/**
* Constructs a nullsafe method call node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Method name
* @param array<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['var', 'name', 'args'];
}
public function getType(): string {
return 'Expr_NullsafeMethodCall';
}
public function getRawArgs(): array {
return $this->args;
}
}

View File

@ -0,0 +1,35 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
class NullsafePropertyFetch extends Expr {
/** @var Expr Variable holding object */
public Expr $var;
/** @var Identifier|Expr Property name */
public Node $name;
/**
* Constructs a nullsafe property fetch node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Property name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['var', 'name'];
}
public function getType(): string {
return 'Expr_NullsafePropertyFetch';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class PostDec extends Expr {
/** @var Expr Variable */
public Expr $var;
/**
* Constructs a post decrement node.
*
* @param Expr $var Variable
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}
public function getSubNodeNames(): array {
return ['var'];
}
public function getType(): string {
return 'Expr_PostDec';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class PostInc extends Expr {
/** @var Expr Variable */
public Expr $var;
/**
* Constructs a post increment node.
*
* @param Expr $var Variable
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}
public function getSubNodeNames(): array {
return ['var'];
}
public function getType(): string {
return 'Expr_PostInc';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class PreDec extends Expr {
/** @var Expr Variable */
public Expr $var;
/**
* Constructs a pre decrement node.
*
* @param Expr $var Variable
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}
public function getSubNodeNames(): array {
return ['var'];
}
public function getType(): string {
return 'Expr_PreDec';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class PreInc extends Expr {
/** @var Expr Variable */
public Expr $var;
/**
* Constructs a pre increment node.
*
* @param Expr $var Variable
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
}
public function getSubNodeNames(): array {
return ['var'];
}
public function getType(): string {
return 'Expr_PreInc';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Print_ extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs an print() node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Print';
}
}

View File

@ -0,0 +1,35 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
class PropertyFetch extends Expr {
/** @var Expr Variable holding object */
public Expr $var;
/** @var Identifier|Expr Property name */
public Node $name;
/**
* Constructs a function call node.
*
* @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Property name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $var, $name, array $attributes = []) {
$this->attributes = $attributes;
$this->var = $var;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['var', 'name'];
}
public function getType(): string {
return 'Expr_PropertyFetch';
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;
class ShellExec extends Expr {
/** @var (Expr|InterpolatedStringPart)[] Interpolated string array */
public array $parts;
/**
* Constructs a shell exec (backtick) node.
*
* @param (Expr|InterpolatedStringPart)[] $parts Interpolated string array
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $parts, array $attributes = []) {
$this->attributes = $attributes;
$this->parts = $parts;
}
public function getSubNodeNames(): array {
return ['parts'];
}
public function getType(): string {
return 'Expr_ShellExec';
}
}

View File

@ -0,0 +1,45 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\VariadicPlaceholder;
class StaticCall extends CallLike {
/** @var Node\Name|Expr Class name */
public Node $class;
/** @var Identifier|Expr Method name */
public Node $name;
/** @var array<Arg|VariadicPlaceholder> Arguments */
public array $args;
/**
* Constructs a static method call node.
*
* @param Node\Name|Expr $class Class name
* @param string|Identifier|Expr $name Method name
* @param array<Arg|VariadicPlaceholder> $args Arguments
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes;
$this->class = $class;
$this->name = \is_string($name) ? new Identifier($name) : $name;
$this->args = $args;
}
public function getSubNodeNames(): array {
return ['class', 'name', 'args'];
}
public function getType(): string {
return 'Expr_StaticCall';
}
public function getRawArgs(): array {
return $this->args;
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\VarLikeIdentifier;
class StaticPropertyFetch extends Expr {
/** @var Name|Expr Class name */
public Node $class;
/** @var VarLikeIdentifier|Expr Property name */
public Node $name;
/**
* Constructs a static property fetch node.
*
* @param Name|Expr $class Class name
* @param string|VarLikeIdentifier|Expr $name Property name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $class, $name, array $attributes = []) {
$this->attributes = $attributes;
$this->class = $class;
$this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['class', 'name'];
}
public function getType(): string {
return 'Expr_StaticPropertyFetch';
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Ternary extends Expr {
/** @var Expr Condition */
public Expr $cond;
/** @var null|Expr Expression for true */
public ?Expr $if;
/** @var Expr Expression for false */
public Expr $else;
/**
* Constructs a ternary operator node.
*
* @param Expr $cond Condition
* @param null|Expr $if Expression for true
* @param Expr $else Expression for false
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $cond, ?Expr $if, Expr $else, array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->if = $if;
$this->else = $else;
}
public function getSubNodeNames(): array {
return ['cond', 'if', 'else'];
}
public function getType(): string {
return 'Expr_Ternary';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node;
class Throw_ extends Node\Expr {
/** @var Node\Expr Expression */
public Node\Expr $expr;
/**
* Constructs a throw expression node.
*
* @param Node\Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_Throw';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class UnaryMinus extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a unary minus node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_UnaryMinus';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class UnaryPlus extends Expr {
/** @var Expr Expression */
public Expr $expr;
/**
* Constructs a unary plus node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_UnaryPlus';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Variable extends Expr {
/** @var string|Expr Name */
public $name;
/**
* Constructs a variable node.
*
* @param string|Expr $name Name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
}
public function getSubNodeNames(): array {
return ['name'];
}
public function getType(): string {
return 'Expr_Variable';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class YieldFrom extends Expr {
/** @var Expr Expression to yield from */
public Expr $expr;
/**
* Constructs an "yield from" node.
*
* @param Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Expr_YieldFrom';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
class Yield_ extends Expr {
/** @var null|Expr Key expression */
public ?Expr $key;
/** @var null|Expr Value expression */
public ?Expr $value;
/**
* Constructs a yield expression node.
*
* @param null|Expr $value Value expression
* @param null|Expr $key Key expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Expr $value = null, ?Expr $key = null, array $attributes = []) {
$this->attributes = $attributes;
$this->key = $key;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['key', 'value'];
}
public function getType(): string {
return 'Expr_Yield';
}
}

View File

@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
interface FunctionLike extends Node {
/**
* Whether to return by reference
*/
public function returnsByRef(): bool;
/**
* List of parameters
*
* @return Param[]
*/
public function getParams(): array;
/**
* Get the declared return type or null
*
* @return null|Identifier|Name|ComplexType
*/
public function getReturnType();
/**
* The function body
*
* @return Stmt[]|null
*/
public function getStmts(): ?array;
/**
* Get PHP attribute groups.
*
* @return AttributeGroup[]
*/
public function getAttrGroups(): array;
}

View File

@ -0,0 +1,85 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
/**
* Represents a non-namespaced name. Namespaced names are represented using Name nodes.
*/
class Identifier extends NodeAbstract {
/**
* @psalm-var non-empty-string
* @var string Identifier as string
*/
public string $name;
/** @var array<string, bool> */
private static array $specialClassNames = [
'self' => true,
'parent' => true,
'static' => true,
];
/**
* Constructs an identifier node.
*
* @param string $name Identifier as string
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(string $name, array $attributes = []) {
if ($name === '') {
throw new \InvalidArgumentException('Identifier name cannot be empty');
}
$this->attributes = $attributes;
$this->name = $name;
}
public function getSubNodeNames(): array {
return ['name'];
}
/**
* Get identifier as string.
*
* @psalm-return non-empty-string
* @return string Identifier as string.
*/
public function toString(): string {
return $this->name;
}
/**
* Get lowercased identifier as string.
*
* @psalm-return non-empty-string
* @return string Lowercased identifier as string
*/
public function toLowerString(): string {
return strtolower($this->name);
}
/**
* Checks whether the identifier is a special class name (self, parent or static).
*
* @return bool Whether identifier is a special class name
*/
public function isSpecialClassName(): bool {
return isset(self::$specialClassNames[strtolower($this->name)]);
}
/**
* Get identifier as string.
*
* @psalm-return non-empty-string
* @return string Identifier as string
*/
public function __toString(): string {
return $this->name;
}
public function getType(): string {
return 'Identifier';
}
}

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class InterpolatedStringPart extends NodeAbstract {
/** @var string String value */
public string $value;
/**
* Constructs a node representing a string part of an interpolated string.
*
* @param string $value String value
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(string $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['value'];
}
public function getType(): string {
return 'InterpolatedStringPart';
}
}
// @deprecated compatibility alias
class_alias(InterpolatedStringPart::class, Scalar\EncapsedStringPart::class);

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
class IntersectionType extends ComplexType {
/** @var (Identifier|Name)[] Types */
public array $types;
/**
* Constructs an intersection type.
*
* @param (Identifier|Name)[] $types Types
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $types, array $attributes = []) {
$this->attributes = $attributes;
$this->types = $types;
}
public function getSubNodeNames(): array {
return ['types'];
}
public function getType(): string {
return 'IntersectionType';
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class MatchArm extends NodeAbstract {
/** @var null|list<Node\Expr> */
public ?array $conds;
/** @var Node\Expr */
public Expr $body;
/**
* @param null|list<Node\Expr> $conds
*/
public function __construct(?array $conds, Node\Expr $body, array $attributes = []) {
$this->conds = $conds;
$this->body = $body;
$this->attributes = $attributes;
}
public function getSubNodeNames(): array {
return ['conds', 'body'];
}
public function getType(): string {
return 'MatchArm';
}
}

View File

@ -0,0 +1,278 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
class Name extends NodeAbstract {
/**
* @psalm-var non-empty-string
* @var string Name as string
*/
public string $name;
/** @var array<string, bool> */
private static array $specialClassNames = [
'self' => true,
'parent' => true,
'static' => true,
];
/**
* Constructs a name node.
*
* @param string|string[]|self $name Name as string, part array or Name instance (copy ctor)
* @param array<string, mixed> $attributes Additional attributes
*/
final public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = self::prepareName($name);
}
public function getSubNodeNames(): array {
return ['name'];
}
/**
* Get parts of name (split by the namespace separator).
*
* @psalm-return non-empty-list<string>
* @return string[] Parts of name
*/
public function getParts(): array {
return \explode('\\', $this->name);
}
/**
* Gets the first part of the name, i.e. everything before the first namespace separator.
*
* @return string First part of the name
*/
public function getFirst(): string {
if (false !== $pos = \strpos($this->name, '\\')) {
return \substr($this->name, 0, $pos);
}
return $this->name;
}
/**
* Gets the last part of the name, i.e. everything after the last namespace separator.
*
* @return string Last part of the name
*/
public function getLast(): string {
if (false !== $pos = \strrpos($this->name, '\\')) {
return \substr($this->name, $pos + 1);
}
return $this->name;
}
/**
* Checks whether the name is unqualified. (E.g. Name)
*
* @return bool Whether the name is unqualified
*/
public function isUnqualified(): bool {
return false === \strpos($this->name, '\\');
}
/**
* Checks whether the name is qualified. (E.g. Name\Name)
*
* @return bool Whether the name is qualified
*/
public function isQualified(): bool {
return false !== \strpos($this->name, '\\');
}
/**
* Checks whether the name is fully qualified. (E.g. \Name)
*
* @return bool Whether the name is fully qualified
*/
public function isFullyQualified(): bool {
return false;
}
/**
* Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
*
* @return bool Whether the name is relative
*/
public function isRelative(): bool {
return false;
}
/**
* Returns a string representation of the name itself, without taking the name type into
* account (e.g., not including a leading backslash for fully qualified names).
*
* @psalm-return non-empty-string
* @return string String representation
*/
public function toString(): string {
return $this->name;
}
/**
* Returns a string representation of the name as it would occur in code (e.g., including
* leading backslash for fully qualified names.
*
* @psalm-return non-empty-string
* @return string String representation
*/
public function toCodeString(): string {
return $this->toString();
}
/**
* Returns lowercased string representation of the name, without taking the name type into
* account (e.g., no leading backslash for fully qualified names).
*
* @psalm-return non-empty-string
* @return string Lowercased string representation
*/
public function toLowerString(): string {
return strtolower($this->name);
}
/**
* Checks whether the identifier is a special class name (self, parent or static).
*
* @return bool Whether identifier is a special class name
*/
public function isSpecialClassName(): bool {
return isset(self::$specialClassNames[strtolower($this->name)]);
}
/**
* Returns a string representation of the name by imploding the namespace parts with the
* namespace separator.
*
* @psalm-return non-empty-string
* @return string String representation
*/
public function __toString(): string {
return $this->name;
}
/**
* Gets a slice of a name (similar to array_slice).
*
* This method returns a new instance of the same type as the original and with the same
* attributes.
*
* If the slice is empty, null is returned. The null value will be correctly handled in
* concatenations using concat().
*
* Offset and length have the same meaning as in array_slice().
*
* @param int $offset Offset to start the slice at (may be negative)
* @param int|null $length Length of the slice (may be negative)
*
* @return static|null Sliced name
*/
public function slice(int $offset, ?int $length = null) {
if ($offset === 1 && $length === null) {
// Short-circuit the common case.
if (false !== $pos = \strpos($this->name, '\\')) {
return new static(\substr($this->name, $pos + 1));
}
return null;
}
$parts = \explode('\\', $this->name);
$numParts = \count($parts);
$realOffset = $offset < 0 ? $offset + $numParts : $offset;
if ($realOffset < 0 || $realOffset > $numParts) {
throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset));
}
if (null === $length) {
$realLength = $numParts - $realOffset;
} else {
$realLength = $length < 0 ? $length + $numParts - $realOffset : $length;
if ($realLength < 0 || $realLength > $numParts - $realOffset) {
throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length));
}
}
if ($realLength === 0) {
// Empty slice is represented as null
return null;
}
return new static(array_slice($parts, $realOffset, $realLength), $this->attributes);
}
/**
* Concatenate two names, yielding a new Name instance.
*
* The type of the generated instance depends on which class this method is called on, for
* example Name\FullyQualified::concat() will yield a Name\FullyQualified instance.
*
* If one of the arguments is null, a new instance of the other name will be returned. If both
* arguments are null, null will be returned. As such, writing
* Name::concat($namespace, $shortName)
* where $namespace is a Name node or null will work as expected.
*
* @param string|string[]|self|null $name1 The first name
* @param string|string[]|self|null $name2 The second name
* @param array<string, mixed> $attributes Attributes to assign to concatenated name
*
* @return static|null Concatenated name
*/
public static function concat($name1, $name2, array $attributes = []) {
if (null === $name1 && null === $name2) {
return null;
}
if (null === $name1) {
return new static($name2, $attributes);
}
if (null === $name2) {
return new static($name1, $attributes);
} else {
return new static(
self::prepareName($name1) . '\\' . self::prepareName($name2), $attributes
);
}
}
/**
* Prepares a (string, array or Name node) name for use in name changing methods by converting
* it to a string.
*
* @param string|string[]|self $name Name to prepare
*
* @psalm-return non-empty-string
* @return string Prepared name
*/
private static function prepareName($name): string {
if (\is_string($name)) {
if ('' === $name) {
throw new \InvalidArgumentException('Name cannot be empty');
}
return $name;
}
if (\is_array($name)) {
if (empty($name)) {
throw new \InvalidArgumentException('Name cannot be empty');
}
return implode('\\', $name);
}
if ($name instanceof self) {
return $name->name;
}
throw new \InvalidArgumentException(
'Expected string, array of parts or Name instance'
);
}
public function getType(): string {
return 'Name';
}
}

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Name;
class FullyQualified extends \PhpParser\Node\Name {
/**
* Checks whether the name is unqualified. (E.g. Name)
*
* @return bool Whether the name is unqualified
*/
public function isUnqualified(): bool {
return false;
}
/**
* Checks whether the name is qualified. (E.g. Name\Name)
*
* @return bool Whether the name is qualified
*/
public function isQualified(): bool {
return false;
}
/**
* Checks whether the name is fully qualified. (E.g. \Name)
*
* @return bool Whether the name is fully qualified
*/
public function isFullyQualified(): bool {
return true;
}
/**
* Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
*
* @return bool Whether the name is relative
*/
public function isRelative(): bool {
return false;
}
public function toCodeString(): string {
return '\\' . $this->toString();
}
public function getType(): string {
return 'Name_FullyQualified';
}
}

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Name;
class Relative extends \PhpParser\Node\Name {
/**
* Checks whether the name is unqualified. (E.g. Name)
*
* @return bool Whether the name is unqualified
*/
public function isUnqualified(): bool {
return false;
}
/**
* Checks whether the name is qualified. (E.g. Name\Name)
*
* @return bool Whether the name is qualified
*/
public function isQualified(): bool {
return false;
}
/**
* Checks whether the name is fully qualified. (E.g. \Name)
*
* @return bool Whether the name is fully qualified
*/
public function isFullyQualified(): bool {
return false;
}
/**
* Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name)
*
* @return bool Whether the name is relative
*/
public function isRelative(): bool {
return true;
}
public function toCodeString(): string {
return 'namespace\\' . $this->toString();
}
public function getType(): string {
return 'Name_Relative';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
class NullableType extends ComplexType {
/** @var Identifier|Name Type */
public Node $type;
/**
* Constructs a nullable type (wrapping another type).
*
* @param Identifier|Name $type Type
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node $type, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
}
public function getSubNodeNames(): array {
return ['type'];
}
public function getType(): string {
return 'NullableType';
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class Param extends NodeAbstract {
/** @var null|Identifier|Name|ComplexType Type declaration */
public ?Node $type;
/** @var bool Whether parameter is passed by reference */
public bool $byRef;
/** @var bool Whether this is a variadic argument */
public bool $variadic;
/** @var Expr\Variable|Expr\Error Parameter variable */
public Expr $var;
/** @var null|Expr Default value */
public ?Expr $default;
/** @var int Optional visibility flags */
public int $flags;
/** @var AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/**
* Constructs a parameter node.
*
* @param Expr\Variable|Expr\Error $var Parameter variable
* @param null|Expr $default Default value
* @param null|Identifier|Name|ComplexType $type Type declaration
* @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument
* @param array<string, mixed> $attributes Additional attributes
* @param int $flags Optional visibility flags
* @param list<AttributeGroup> $attrGroups PHP attribute groups
*/
public function __construct(
Expr $var, ?Expr $default = null, ?Node $type = null,
bool $byRef = false, bool $variadic = false,
array $attributes = [],
int $flags = 0,
array $attrGroups = []
) {
$this->attributes = $attributes;
$this->type = $type;
$this->byRef = $byRef;
$this->variadic = $variadic;
$this->var = $var;
$this->default = $default;
$this->flags = $flags;
$this->attrGroups = $attrGroups;
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default'];
}
public function getType(): string {
return 'Param';
}
/**
* Whether this parameter uses constructor property promotion.
*/
public function isPromoted(): bool {
return $this->flags !== 0;
}
public function isPublic(): bool {
return (bool) ($this->flags & Modifiers::PUBLIC);
}
public function isProtected(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED);
}
public function isPrivate(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE);
}
public function isReadonly(): bool {
return (bool) ($this->flags & Modifiers::READONLY);
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class PropertyItem extends NodeAbstract {
/** @var Node\VarLikeIdentifier Name */
public VarLikeIdentifier $name;
/** @var null|Node\Expr Default */
public ?Expr $default;
/**
* Constructs a class property item node.
*
* @param string|Node\VarLikeIdentifier $name Name
* @param null|Node\Expr $default Default value
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, ?Node\Expr $default = null, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name;
$this->default = $default;
}
public function getSubNodeNames(): array {
return ['name', 'default'];
}
public function getType(): string {
return 'PropertyItem';
}
}
// @deprecated compatibility alias
class_alias(PropertyItem::class, Stmt\PropertyProperty::class);

View File

@ -0,0 +1,6 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
abstract class Scalar extends Expr {
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/Float_.php';

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/InterpolatedString.php';

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../InterpolatedStringPart.php';

View File

@ -0,0 +1,78 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Node\Scalar;
class Float_ extends Scalar {
/** @var float Number value */
public float $value;
/**
* Constructs a float number scalar node.
*
* @param float $value Value of the number
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(float $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['value'];
}
/**
* @param mixed[] $attributes
*/
public static function fromString(string $str, array $attributes = []): Float_ {
$attributes['rawValue'] = $str;
$float = self::parse($str);
return new Float_($float, $attributes);
}
/**
* @internal
*
* Parses a DNUMBER token like PHP would.
*
* @param string $str A string number
*
* @return float The parsed number
*/
public static function parse(string $str): float {
$str = str_replace('_', '', $str);
// Check whether this is one of the special integer notations.
if ('0' === $str[0]) {
// hex
if ('x' === $str[1] || 'X' === $str[1]) {
return hexdec($str);
}
// bin
if ('b' === $str[1] || 'B' === $str[1]) {
return bindec($str);
}
// oct, but only if the string does not contain any of '.eE'.
if (false === strpbrk($str, '.eE')) {
// substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit
// (8 or 9) so that only the digits before that are used.
return octdec(substr($str, 0, strcspn($str, '89')));
}
}
// dec
return (float) $str;
}
public function getType(): string {
return 'Scalar_Float';
}
}
// @deprecated compatibility alias
class_alias(Float_::class, DNumber::class);

View File

@ -0,0 +1,82 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Error;
use PhpParser\Node\Scalar;
class Int_ extends Scalar {
/* For use in "kind" attribute */
public const KIND_BIN = 2;
public const KIND_OCT = 8;
public const KIND_DEC = 10;
public const KIND_HEX = 16;
/** @var int Number value */
public int $value;
/**
* Constructs an integer number scalar node.
*
* @param int $value Value of the number
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(int $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['value'];
}
/**
* Constructs an Int node from a string number literal.
*
* @param string $str String number literal (decimal, octal, hex or binary)
* @param array<string, mixed> $attributes Additional attributes
* @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5)
*
* @return Int_ The constructed LNumber, including kind attribute
*/
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false): Int_ {
$attributes['rawValue'] = $str;
$str = str_replace('_', '', $str);
if ('0' !== $str[0] || '0' === $str) {
$attributes['kind'] = Int_::KIND_DEC;
return new Int_((int) $str, $attributes);
}
if ('x' === $str[1] || 'X' === $str[1]) {
$attributes['kind'] = Int_::KIND_HEX;
return new Int_(hexdec($str), $attributes);
}
if ('b' === $str[1] || 'B' === $str[1]) {
$attributes['kind'] = Int_::KIND_BIN;
return new Int_(bindec($str), $attributes);
}
if (!$allowInvalidOctal && strpbrk($str, '89')) {
throw new Error('Invalid numeric literal', $attributes);
}
// Strip optional explicit octal prefix.
if ('o' === $str[1] || 'O' === $str[1]) {
$str = substr($str, 2);
}
// use intval instead of octdec to get proper cutting behavior with malformed numbers
$attributes['kind'] = Int_::KIND_OCT;
return new Int_(intval($str, 8), $attributes);
}
public function getType(): string {
return 'Scalar_Int';
}
}
// @deprecated compatibility alias
class_alias(Int_::class, LNumber::class);

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Node\Expr;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Scalar;
class InterpolatedString extends Scalar {
/** @var (Expr|InterpolatedStringPart)[] list of string parts */
public array $parts;
/**
* Constructs an interpolated string node.
*
* @param (Expr|InterpolatedStringPart)[] $parts Interpolated string parts
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $parts, array $attributes = []) {
$this->attributes = $attributes;
$this->parts = $parts;
}
public function getSubNodeNames(): array {
return ['parts'];
}
public function getType(): string {
return 'Scalar_InterpolatedString';
}
}
// @deprecated compatibility alias
class_alias(InterpolatedString::class, Encapsed::class);

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/Int_.php';

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Node\Scalar;
abstract class MagicConst extends Scalar {
/**
* Constructs a magic constant node.
*
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}
public function getSubNodeNames(): array {
return [];
}
/**
* Get name of magic constant.
*
* @return string Name of magic constant
*/
abstract public function getName(): string;
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Class_ extends MagicConst {
public function getName(): string {
return '__CLASS__';
}
public function getType(): string {
return 'Scalar_MagicConst_Class';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Dir extends MagicConst {
public function getName(): string {
return '__DIR__';
}
public function getType(): string {
return 'Scalar_MagicConst_Dir';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class File extends MagicConst {
public function getName(): string {
return '__FILE__';
}
public function getType(): string {
return 'Scalar_MagicConst_File';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Function_ extends MagicConst {
public function getName(): string {
return '__FUNCTION__';
}
public function getType(): string {
return 'Scalar_MagicConst_Function';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Line extends MagicConst {
public function getName(): string {
return '__LINE__';
}
public function getType(): string {
return 'Scalar_MagicConst_Line';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Method extends MagicConst {
public function getName(): string {
return '__METHOD__';
}
public function getType(): string {
return 'Scalar_MagicConst_Method';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Namespace_ extends MagicConst {
public function getName(): string {
return '__NAMESPACE__';
}
public function getType(): string {
return 'Scalar_MagicConst_Namespace';
}
}

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Scalar\MagicConst;
class Trait_ extends MagicConst {
public function getName(): string {
return '__TRAIT__';
}
public function getType(): string {
return 'Scalar_MagicConst_Trait';
}
}

View File

@ -0,0 +1,161 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Scalar;
use PhpParser\Error;
use PhpParser\Node\Scalar;
class String_ extends Scalar {
/* For use in "kind" attribute */
public const KIND_SINGLE_QUOTED = 1;
public const KIND_DOUBLE_QUOTED = 2;
public const KIND_HEREDOC = 3;
public const KIND_NOWDOC = 4;
/** @var string String value */
public string $value;
/** @var array<string, string> Escaped character to its decoded value */
protected static array $replacements = [
'\\' => '\\',
'$' => '$',
'n' => "\n",
'r' => "\r",
't' => "\t",
'f' => "\f",
'v' => "\v",
'e' => "\x1B",
];
/**
* Constructs a string scalar node.
*
* @param string $value Value of the string
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(string $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['value'];
}
/**
* @param array<string, mixed> $attributes
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
*/
public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self {
$attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B')))
? Scalar\String_::KIND_SINGLE_QUOTED
: Scalar\String_::KIND_DOUBLE_QUOTED;
$attributes['rawValue'] = $str;
$string = self::parse($str, $parseUnicodeEscape);
return new self($string, $attributes);
}
/**
* @internal
*
* Parses a string token.
*
* @param string $str String token content
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
*
* @return string The parsed string
*/
public static function parse(string $str, bool $parseUnicodeEscape = true): string {
$bLength = 0;
if ('b' === $str[0] || 'B' === $str[0]) {
$bLength = 1;
}
if ('\'' === $str[$bLength]) {
return str_replace(
['\\\\', '\\\''],
['\\', '\''],
substr($str, $bLength + 1, -1)
);
} else {
return self::parseEscapeSequences(
substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape
);
}
}
/**
* @internal
*
* Parses escape sequences in strings (all string types apart from single quoted).
*
* @param string $str String without quotes
* @param null|string $quote Quote type
* @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes
*
* @return string String with escape sequences parsed
*/
public static function parseEscapeSequences(string $str, ?string $quote, bool $parseUnicodeEscape = true): string {
if (null !== $quote) {
$str = str_replace('\\' . $quote, $quote, $str);
}
$extra = '';
if ($parseUnicodeEscape) {
$extra = '|u\{([0-9a-fA-F]+)\}';
}
return preg_replace_callback(
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~',
function ($matches) {
$str = $matches[1];
if (isset(self::$replacements[$str])) {
return self::$replacements[$str];
}
if ('x' === $str[0] || 'X' === $str[0]) {
return chr(hexdec(substr($str, 1)));
}
if ('u' === $str[0]) {
$dec = hexdec($matches[2]);
// If it overflowed to float, treat as INT_MAX, it will throw an error anyway.
return self::codePointToUtf8(\is_int($dec) ? $dec : \PHP_INT_MAX);
} else {
return chr(octdec($str));
}
},
$str
);
}
/**
* Converts a Unicode code point to its UTF-8 encoded representation.
*
* @param int $num Code point
*
* @return string UTF-8 representation of code point
*/
private static function codePointToUtf8(int $num): string {
if ($num <= 0x7F) {
return chr($num);
}
if ($num <= 0x7FF) {
return chr(($num >> 6) + 0xC0) . chr(($num & 0x3F) + 0x80);
}
if ($num <= 0xFFFF) {
return chr(($num >> 12) + 0xE0) . chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80);
}
if ($num <= 0x1FFFFF) {
return chr(($num >> 18) + 0xF0) . chr((($num >> 12) & 0x3F) + 0x80)
. chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80);
}
throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
}
public function getType(): string {
return 'Scalar_String';
}
}

View File

@ -0,0 +1,39 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
class StaticVar extends NodeAbstract {
/** @var Expr\Variable Variable */
public Expr\Variable $var;
/** @var null|Node\Expr Default value */
public ?Expr $default;
/**
* Constructs a static variable node.
*
* @param Expr\Variable $var Name
* @param null|Node\Expr $default Default value
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(
Expr\Variable $var, ?Node\Expr $default = null, array $attributes = []
) {
$this->attributes = $attributes;
$this->var = $var;
$this->default = $default;
}
public function getSubNodeNames(): array {
return ['var', 'default'];
}
public function getType(): string {
return 'StaticVar';
}
}
// @deprecated compatibility alias
class_alias(StaticVar::class, Stmt\StaticVar::class);

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
abstract class Stmt extends NodeAbstract {
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Stmt;
class Block extends Stmt {
/** @var Stmt[] Statements */
public array $stmts;
/**
* A block of statements.
*
* @param Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $stmts, array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}
public function getType(): string {
return 'Stmt_Block';
}
public function getSubNodeNames(): array {
return ['stmts'];
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Break_ extends Node\Stmt {
/** @var null|Node\Expr Number of loops to break */
public ?Node\Expr $num;
/**
* Constructs a break node.
*
* @param null|Node\Expr $num Number of loops to break
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Expr $num = null, array $attributes = []) {
$this->attributes = $attributes;
$this->num = $num;
}
public function getSubNodeNames(): array {
return ['num'];
}
public function getType(): string {
return 'Stmt_Break';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Case_ extends Node\Stmt {
/** @var null|Node\Expr Condition (null for default) */
public ?Node\Expr $cond;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a case node.
*
* @param null|Node\Expr $cond Condition (null for default)
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}
public function getType(): string {
return 'Stmt_Case';
}
}

View File

@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
use PhpParser\Node\Expr;
class Catch_ extends Node\Stmt {
/** @var Node\Name[] Types of exceptions to catch */
public array $types;
/** @var Expr\Variable|null Variable for exception */
public ?Expr\Variable $var;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a catch node.
*
* @param Node\Name[] $types Types of exceptions to catch
* @param Expr\Variable|null $var Variable for exception
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(
array $types, ?Expr\Variable $var = null, array $stmts = [], array $attributes = []
) {
$this->attributes = $attributes;
$this->types = $types;
$this->var = $var;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['types', 'var', 'stmts'];
}
public function getType(): string {
return 'Stmt_Catch';
}
}

View File

@ -0,0 +1,77 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Modifiers;
use PhpParser\Node;
class ClassConst extends Node\Stmt {
/** @var int Modifiers */
public int $flags;
/** @var Node\Const_[] Constant declarations */
public array $consts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null Type declaration */
public ?Node $type;
/**
* Constructs a class const list node.
*
* @param Node\Const_[] $consts Constant declarations
* @param int $flags Modifiers
* @param array<string, mixed> $attributes Additional attributes
* @param list<Node\AttributeGroup> $attrGroups PHP attribute groups
* @param null|Node\Identifier|Node\Name|Node\ComplexType $type Type declaration
*/
public function __construct(
array $consts,
int $flags = 0,
array $attributes = [],
array $attrGroups = [],
?Node $type = null
) {
$this->attributes = $attributes;
$this->flags = $flags;
$this->consts = $consts;
$this->attrGroups = $attrGroups;
$this->type = $type;
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'type', 'consts'];
}
/**
* Whether constant is explicitly or implicitly public.
*/
public function isPublic(): bool {
return ($this->flags & Modifiers::PUBLIC) !== 0
|| ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
}
/**
* Whether constant is protected.
*/
public function isProtected(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED);
}
/**
* Whether constant is private.
*/
public function isPrivate(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE);
}
/**
* Whether constant is final.
*/
public function isFinal(): bool {
return (bool) ($this->flags & Modifiers::FINAL);
}
public function getType(): string {
return 'Stmt_ClassConst';
}
}

View File

@ -0,0 +1,109 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
use PhpParser\Node\PropertyItem;
abstract class ClassLike extends Node\Stmt {
/** @var Node\Identifier|null Name */
public ?Node\Identifier $name;
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/** @var Node\Name|null Namespaced name (if using NameResolver) */
public ?Node\Name $namespacedName;
/**
* @return TraitUse[]
*/
public function getTraitUses(): array {
$traitUses = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof TraitUse) {
$traitUses[] = $stmt;
}
}
return $traitUses;
}
/**
* @return ClassConst[]
*/
public function getConstants(): array {
$constants = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof ClassConst) {
$constants[] = $stmt;
}
}
return $constants;
}
/**
* @return Property[]
*/
public function getProperties(): array {
$properties = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof Property) {
$properties[] = $stmt;
}
}
return $properties;
}
/**
* Gets property with the given name defined directly in this class/interface/trait.
*
* @param string $name Name of the property
*
* @return Property|null Property node or null if the property does not exist
*/
public function getProperty(string $name): ?Property {
foreach ($this->stmts as $stmt) {
if ($stmt instanceof Property) {
foreach ($stmt->props as $prop) {
if ($prop instanceof PropertyItem && $name === $prop->name->toString()) {
return $stmt;
}
}
}
}
return null;
}
/**
* Gets all methods defined directly in this class/interface/trait
*
* @return ClassMethod[]
*/
public function getMethods(): array {
$methods = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof ClassMethod) {
$methods[] = $stmt;
}
}
return $methods;
}
/**
* Gets method with the given name defined directly in this class/interface/trait.
*
* @param string $name Name of the method (compared case-insensitively)
*
* @return ClassMethod|null Method node or null if the method does not exist
*/
public function getMethod(string $name): ?ClassMethod {
$lowerName = strtolower($name);
foreach ($this->stmts as $stmt) {
if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) {
return $stmt;
}
}
return null;
}
}

View File

@ -0,0 +1,154 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;
class ClassMethod extends Node\Stmt implements FunctionLike {
/** @var int Flags */
public int $flags;
/** @var bool Whether to return by reference */
public bool $byRef;
/** @var Node\Identifier Name */
public Node\Identifier $name;
/** @var Node\Param[] Parameters */
public array $params;
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
public ?Node $returnType;
/** @var Node\Stmt[]|null Statements */
public ?array $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/** @var array<string, bool> */
private static array $magicNames = [
'__construct' => true,
'__destruct' => true,
'__call' => true,
'__callstatic' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
'__unset' => true,
'__sleep' => true,
'__wakeup' => true,
'__tostring' => true,
'__set_state' => true,
'__clone' => true,
'__invoke' => true,
'__debuginfo' => true,
'__serialize' => true,
'__unserialize' => true,
];
/**
* Constructs a class method node.
*
* @param string|Node\Identifier $name Name
* @param array{
* flags?: int,
* byRef?: bool,
* params?: Node\Param[],
* returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
* stmts?: Node\Stmt[]|null,
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'flags => 0 : Flags
* 'byRef' => false : Whether to return by reference
* 'params' => array() : Parameters
* 'returnType' => null : Return type
* 'stmts' => array() : Statements
* 'attrGroups' => array() : PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
$this->byRef = $subNodes['byRef'] ?? false;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->params = $subNodes['params'] ?? [];
$this->returnType = $subNodes['returnType'] ?? null;
$this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts'];
}
public function returnsByRef(): bool {
return $this->byRef;
}
public function getParams(): array {
return $this->params;
}
public function getReturnType() {
return $this->returnType;
}
public function getStmts(): ?array {
return $this->stmts;
}
public function getAttrGroups(): array {
return $this->attrGroups;
}
/**
* Whether the method is explicitly or implicitly public.
*/
public function isPublic(): bool {
return ($this->flags & Modifiers::PUBLIC) !== 0
|| ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
}
/**
* Whether the method is protected.
*/
public function isProtected(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED);
}
/**
* Whether the method is private.
*/
public function isPrivate(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE);
}
/**
* Whether the method is abstract.
*/
public function isAbstract(): bool {
return (bool) ($this->flags & Modifiers::ABSTRACT);
}
/**
* Whether the method is final.
*/
public function isFinal(): bool {
return (bool) ($this->flags & Modifiers::FINAL);
}
/**
* Whether the method is static.
*/
public function isStatic(): bool {
return (bool) ($this->flags & Modifiers::STATIC);
}
/**
* Whether the method is magic.
*/
public function isMagic(): bool {
return isset(self::$magicNames[$this->name->toLowerString()]);
}
public function getType(): string {
return 'Stmt_ClassMethod';
}
}

View File

@ -0,0 +1,94 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Modifiers;
use PhpParser\Node;
class Class_ extends ClassLike {
/** @deprecated Use Modifiers::PUBLIC instead */
public const MODIFIER_PUBLIC = 1;
/** @deprecated Use Modifiers::PROTECTED instead */
public const MODIFIER_PROTECTED = 2;
/** @deprecated Use Modifiers::PRIVATE instead */
public const MODIFIER_PRIVATE = 4;
/** @deprecated Use Modifiers::STATIC instead */
public const MODIFIER_STATIC = 8;
/** @deprecated Use Modifiers::ABSTRACT instead */
public const MODIFIER_ABSTRACT = 16;
/** @deprecated Use Modifiers::FINAL instead */
public const MODIFIER_FINAL = 32;
/** @deprecated Use Modifiers::READONLY instead */
public const MODIFIER_READONLY = 64;
/** @deprecated Use Modifiers::VISIBILITY_MASK instead */
public const VISIBILITY_MODIFIER_MASK = 7; // 1 | 2 | 4
/** @var int Modifiers */
public int $flags;
/** @var null|Node\Name Name of extended class */
public ?Node\Name $extends;
/** @var Node\Name[] Names of implemented interfaces */
public array $implements;
/**
* Constructs a class node.
*
* @param string|Node\Identifier|null $name Name
* @param array{
* flags?: int,
* extends?: Node\Name|null,
* implements?: Node\Name[],
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'flags' => 0 : Flags
* 'extends' => null : Name of extended class
* 'implements' => array(): Names of implemented interfaces
* 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->extends = $subNodes['extends'] ?? null;
$this->implements = $subNodes['implements'] ?? [];
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts'];
}
/**
* Whether the class is explicitly abstract.
*/
public function isAbstract(): bool {
return (bool) ($this->flags & Modifiers::ABSTRACT);
}
/**
* Whether the class is final.
*/
public function isFinal(): bool {
return (bool) ($this->flags & Modifiers::FINAL);
}
public function isReadonly(): bool {
return (bool) ($this->flags & Modifiers::READONLY);
}
/**
* Whether the class is anonymous.
*/
public function isAnonymous(): bool {
return null === $this->name;
}
public function getType(): string {
return 'Stmt_Class';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Const_ extends Node\Stmt {
/** @var Node\Const_[] Constant declarations */
public array $consts;
/**
* Constructs a const list node.
*
* @param Node\Const_[] $consts Constant declarations
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $consts, array $attributes = []) {
$this->attributes = $attributes;
$this->consts = $consts;
}
public function getSubNodeNames(): array {
return ['consts'];
}
public function getType(): string {
return 'Stmt_Const';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Continue_ extends Node\Stmt {
/** @var null|Node\Expr Number of loops to continue */
public ?Node\Expr $num;
/**
* Constructs a continue node.
*
* @param null|Node\Expr $num Number of loops to continue
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Expr $num = null, array $attributes = []) {
$this->attributes = $attributes;
$this->num = $num;
}
public function getSubNodeNames(): array {
return ['num'];
}
public function getType(): string {
return 'Stmt_Continue';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../DeclareItem.php';

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
use PhpParser\Node\DeclareItem;
class Declare_ extends Node\Stmt {
/** @var DeclareItem[] List of declares */
public array $declares;
/** @var Node\Stmt[]|null Statements */
public ?array $stmts;
/**
* Constructs a declare node.
*
* @param DeclareItem[] $declares List of declares
* @param Node\Stmt[]|null $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $declares, ?array $stmts = null, array $attributes = []) {
$this->attributes = $attributes;
$this->declares = $declares;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['declares', 'stmts'];
}
public function getType(): string {
return 'Stmt_Declare';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Do_ extends Node\Stmt {
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var Node\Expr Condition */
public Node\Expr $cond;
/**
* Constructs a do while node.
*
* @param Node\Expr $cond Condition
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['stmts', 'cond'];
}
public function getType(): string {
return 'Stmt_Do';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Echo_ extends Node\Stmt {
/** @var Node\Expr[] Expressions */
public array $exprs;
/**
* Constructs an echo node.
*
* @param Node\Expr[] $exprs Expressions
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $exprs, array $attributes = []) {
$this->attributes = $attributes;
$this->exprs = $exprs;
}
public function getSubNodeNames(): array {
return ['exprs'];
}
public function getType(): string {
return 'Stmt_Echo';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class ElseIf_ extends Node\Stmt {
/** @var Node\Expr Condition */
public Node\Expr $cond;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs an elseif node.
*
* @param Node\Expr $cond Condition
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}
public function getType(): string {
return 'Stmt_ElseIf';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Else_ extends Node\Stmt {
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs an else node.
*
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['stmts'];
}
public function getType(): string {
return 'Stmt_Else';
}
}

View File

@ -0,0 +1,36 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
use PhpParser\Node\AttributeGroup;
class EnumCase extends Node\Stmt {
/** @var Node\Identifier Enum case name */
public Node\Identifier $name;
/** @var Node\Expr|null Enum case expression */
public ?Node\Expr $expr;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/**
* @param string|Node\Identifier $name Enum case name
* @param Node\Expr|null $expr Enum case expression
* @param list<AttributeGroup> $attrGroups PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, ?Node\Expr $expr = null, array $attrGroups = [], array $attributes = []) {
parent::__construct($attributes);
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->expr = $expr;
$this->attrGroups = $attrGroups;
}
public function getSubNodeNames(): array {
return ['attrGroups', 'name', 'expr'];
}
public function getType(): string {
return 'Stmt_EnumCase';
}
}

View File

@ -0,0 +1,44 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Enum_ extends ClassLike {
/** @var null|Node\Identifier Scalar Type */
public ?Node $scalarType;
/** @var Node\Name[] Names of implemented interfaces */
public array $implements;
/**
* @param string|Node\Identifier|null $name Name
* @param array{
* scalarType?: Node\Identifier|null,
* implements?: Node\Name[],
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'scalarType' => null : Scalar type
* 'implements' => array() : Names of implemented interfaces
* 'stmts' => array() : Statements
* 'attrGroups' => array() : PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->scalarType = $subNodes['scalarType'] ?? null;
$this->implements = $subNodes['implements'] ?? [];
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
parent::__construct($attributes);
}
public function getSubNodeNames(): array {
return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts'];
}
public function getType(): string {
return 'Stmt_Enum';
}
}

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
/**
* Represents statements of type "expr;"
*/
class Expression extends Node\Stmt {
/** @var Node\Expr Expression */
public Node\Expr $expr;
/**
* Constructs an expression statement.
*
* @param Node\Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $expr, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Stmt_Expression';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Finally_ extends Node\Stmt {
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a finally node.
*
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['stmts'];
}
public function getType(): string {
return 'Stmt_Finally';
}
}

View File

@ -0,0 +1,47 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class For_ extends Node\Stmt {
/** @var Node\Expr[] Init expressions */
public array $init;
/** @var Node\Expr[] Loop conditions */
public array $cond;
/** @var Node\Expr[] Loop expressions */
public array $loop;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a for loop node.
*
* @param array{
* init?: Node\Expr[],
* cond?: Node\Expr[],
* loop?: Node\Expr[],
* stmts?: Node\Stmt[],
* } $subNodes Array of the following optional subnodes:
* 'init' => array(): Init expressions
* 'cond' => array(): Loop conditions
* 'loop' => array(): Loop expressions
* 'stmts' => array(): Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->init = $subNodes['init'] ?? [];
$this->cond = $subNodes['cond'] ?? [];
$this->loop = $subNodes['loop'] ?? [];
$this->stmts = $subNodes['stmts'] ?? [];
}
public function getSubNodeNames(): array {
return ['init', 'cond', 'loop', 'stmts'];
}
public function getType(): string {
return 'Stmt_For';
}
}

View File

@ -0,0 +1,50 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Foreach_ extends Node\Stmt {
/** @var Node\Expr Expression to iterate */
public Node\Expr $expr;
/** @var null|Node\Expr Variable to assign key to */
public ?Node\Expr $keyVar;
/** @var bool Whether to assign value by reference */
public bool $byRef;
/** @var Node\Expr Variable to assign value to */
public Node\Expr $valueVar;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a foreach node.
*
* @param Node\Expr $expr Expression to iterate
* @param Node\Expr $valueVar Variable to assign value to
* @param array{
* keyVar?: Node\Expr|null,
* byRef?: bool,
* stmts?: Node\Stmt[],
* } $subNodes Array of the following optional subnodes:
* 'keyVar' => null : Variable to assign key to
* 'byRef' => false : Whether to assign value by reference
* 'stmts' => array(): Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
$this->keyVar = $subNodes['keyVar'] ?? null;
$this->byRef = $subNodes['byRef'] ?? false;
$this->valueVar = $valueVar;
$this->stmts = $subNodes['stmts'] ?? [];
}
public function getSubNodeNames(): array {
return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts'];
}
public function getType(): string {
return 'Stmt_Foreach';
}
}

View File

@ -0,0 +1,81 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;
class Function_ extends Node\Stmt implements FunctionLike {
/** @var bool Whether function returns by reference */
public bool $byRef;
/** @var Node\Identifier Name */
public Node\Identifier $name;
/** @var Node\Param[] Parameters */
public array $params;
/** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */
public ?Node $returnType;
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/** @var Node\Name|null Namespaced name (if using NameResolver) */
public ?Node\Name $namespacedName;
/**
* Constructs a function node.
*
* @param string|Node\Identifier $name Name
* @param array{
* byRef?: bool,
* params?: Node\Param[],
* returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'byRef' => false : Whether to return by reference
* 'params' => array(): Parameters
* 'returnType' => null : Return type
* 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->byRef = $subNodes['byRef'] ?? false;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->params = $subNodes['params'] ?? [];
$this->returnType = $subNodes['returnType'] ?? null;
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts'];
}
public function returnsByRef(): bool {
return $this->byRef;
}
public function getParams(): array {
return $this->params;
}
public function getReturnType() {
return $this->returnType;
}
public function getAttrGroups(): array {
return $this->attrGroups;
}
/** @return Node\Stmt[] */
public function getStmts(): array {
return $this->stmts;
}
public function getType(): string {
return 'Stmt_Function';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Global_ extends Node\Stmt {
/** @var Node\Expr[] Variables */
public array $vars;
/**
* Constructs a global variables list node.
*
* @param Node\Expr[] $vars Variables to unset
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}
public function getSubNodeNames(): array {
return ['vars'];
}
public function getType(): string {
return 'Stmt_Global';
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
class Goto_ extends Stmt {
/** @var Identifier Name of label to jump to */
public Identifier $name;
/**
* Constructs a goto node.
*
* @param string|Identifier $name Name of label to jump to
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['name'];
}
public function getType(): string {
return 'Stmt_Goto';
}
}

View File

@ -0,0 +1,41 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;
class GroupUse extends Stmt {
/**
* @var Use_::TYPE_* Type of group use
*/
public int $type;
/** @var Name Prefix for uses */
public Name $prefix;
/** @var UseItem[] Uses */
public array $uses;
/**
* Constructs a group use node.
*
* @param Name $prefix Prefix for uses
* @param UseItem[] $uses Uses
* @param Use_::TYPE_* $type Type of group use
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Name $prefix, array $uses, int $type = Use_::TYPE_NORMAL, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
$this->prefix = $prefix;
$this->uses = $uses;
}
public function getSubNodeNames(): array {
return ['type', 'prefix', 'uses'];
}
public function getType(): string {
return 'Stmt_GroupUse';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Stmt;
class HaltCompiler extends Stmt {
/** @var string Remaining text after halt compiler statement. */
public string $remaining;
/**
* Constructs a __halt_compiler node.
*
* @param string $remaining Remaining text after halt compiler statement.
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(string $remaining, array $attributes = []) {
$this->attributes = $attributes;
$this->remaining = $remaining;
}
public function getSubNodeNames(): array {
return ['remaining'];
}
public function getType(): string {
return 'Stmt_HaltCompiler';
}
}

View File

@ -0,0 +1,46 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class If_ extends Node\Stmt {
/** @var Node\Expr Condition expression */
public Node\Expr $cond;
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var ElseIf_[] Elseif clauses */
public array $elseifs;
/** @var null|Else_ Else clause */
public ?Else_ $else;
/**
* Constructs an if node.
*
* @param Node\Expr $cond Condition
* @param array{
* stmts?: Node\Stmt[],
* elseifs?: ElseIf_[],
* else?: Else_|null,
* } $subNodes Array of the following optional subnodes:
* 'stmts' => array(): Statements
* 'elseifs' => array(): Elseif clauses
* 'else' => null : Else clause
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $subNodes['stmts'] ?? [];
$this->elseifs = $subNodes['elseifs'] ?? [];
$this->else = $subNodes['else'] ?? null;
}
public function getSubNodeNames(): array {
return ['cond', 'stmts', 'elseifs', 'else'];
}
public function getType(): string {
return 'Stmt_If';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Stmt;
class InlineHTML extends Stmt {
/** @var string String */
public string $value;
/**
* Constructs an inline HTML node.
*
* @param string $value String
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(string $value, array $attributes = []) {
$this->attributes = $attributes;
$this->value = $value;
}
public function getSubNodeNames(): array {
return ['value'];
}
public function getType(): string {
return 'Stmt_InlineHTML';
}
}

View File

@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Interface_ extends ClassLike {
/** @var Node\Name[] Extended interfaces */
public array $extends;
/**
* Constructs a class node.
*
* @param string|Node\Identifier $name Name
* @param array{
* extends?: Node\Name[],
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'extends' => array(): Name of extended interfaces
* 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->extends = $subNodes['extends'] ?? [];
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'name', 'extends', 'stmts'];
}
public function getType(): string {
return 'Stmt_Interface';
}
}

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
class Label extends Stmt {
/** @var Identifier Name */
public Identifier $name;
/**
* Constructs a label node.
*
* @param string|Identifier $name Name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Identifier($name) : $name;
}
public function getSubNodeNames(): array {
return ['name'];
}
public function getType(): string {
return 'Stmt_Label';
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Namespace_ extends Node\Stmt {
/* For use in the "kind" attribute */
public const KIND_SEMICOLON = 1;
public const KIND_BRACED = 2;
/** @var null|Node\Name Name */
public ?Node\Name $name;
/** @var Node\Stmt[] Statements */
public $stmts;
/**
* Constructs a namespace node.
*
* @param null|Node\Name $name Name
* @param null|Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Name $name = null, ?array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = $name;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['name', 'stmts'];
}
public function getType(): string {
return 'Stmt_Namespace';
}
}

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
/** Nop/empty statement (;). */
class Nop extends Node\Stmt {
public function getSubNodeNames(): array {
return [];
}
public function getType(): string {
return 'Stmt_Nop';
}
}

View File

@ -0,0 +1,82 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\PropertyItem;
class Property extends Node\Stmt {
/** @var int Modifiers */
public int $flags;
/** @var PropertyItem[] Properties */
public array $props;
/** @var null|Identifier|Name|ComplexType Type declaration */
public ?Node $type;
/** @var Node\AttributeGroup[] PHP attribute groups */
public array $attrGroups;
/**
* Constructs a class property list node.
*
* @param int $flags Modifiers
* @param PropertyItem[] $props Properties
* @param array<string, mixed> $attributes Additional attributes
* @param null|Identifier|Name|ComplexType $type Type declaration
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
*/
public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = []) {
$this->attributes = $attributes;
$this->flags = $flags;
$this->props = $props;
$this->type = $type;
$this->attrGroups = $attrGroups;
}
public function getSubNodeNames(): array {
return ['attrGroups', 'flags', 'type', 'props'];
}
/**
* Whether the property is explicitly or implicitly public.
*/
public function isPublic(): bool {
return ($this->flags & Modifiers::PUBLIC) !== 0
|| ($this->flags & Modifiers::VISIBILITY_MASK) === 0;
}
/**
* Whether the property is protected.
*/
public function isProtected(): bool {
return (bool) ($this->flags & Modifiers::PROTECTED);
}
/**
* Whether the property is private.
*/
public function isPrivate(): bool {
return (bool) ($this->flags & Modifiers::PRIVATE);
}
/**
* Whether the property is static.
*/
public function isStatic(): bool {
return (bool) ($this->flags & Modifiers::STATIC);
}
/**
* Whether the property is readonly.
*/
public function isReadonly(): bool {
return (bool) ($this->flags & Modifiers::READONLY);
}
public function getType(): string {
return 'Stmt_Property';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../PropertyItem.php';

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Return_ extends Node\Stmt {
/** @var null|Node\Expr Expression */
public ?Node\Expr $expr;
/**
* Constructs a return node.
*
* @param null|Node\Expr $expr Expression
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Expr $expr = null, array $attributes = []) {
$this->attributes = $attributes;
$this->expr = $expr;
}
public function getSubNodeNames(): array {
return ['expr'];
}
public function getType(): string {
return 'Stmt_Return';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../StaticVar.php';

View File

@ -0,0 +1,30 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\StaticVar;
use PhpParser\Node\Stmt;
class Static_ extends Stmt {
/** @var StaticVar[] Variable definitions */
public array $vars;
/**
* Constructs a static variables list node.
*
* @param StaticVar[] $vars Variable definitions
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}
public function getSubNodeNames(): array {
return ['vars'];
}
public function getType(): string {
return 'Stmt_Static';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Switch_ extends Node\Stmt {
/** @var Node\Expr Condition */
public Node\Expr $cond;
/** @var Case_[] Case list */
public array $cases;
/**
* Constructs a case node.
*
* @param Node\Expr $cond Condition
* @param Case_[] $cases Case list
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $cases, array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->cases = $cases;
}
public function getSubNodeNames(): array {
return ['cond', 'cases'];
}
public function getType(): string {
return 'Stmt_Switch';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class TraitUse extends Node\Stmt {
/** @var Node\Name[] Traits */
public array $traits;
/** @var TraitUseAdaptation[] Adaptations */
public array $adaptations;
/**
* Constructs a trait use node.
*
* @param Node\Name[] $traits Traits
* @param TraitUseAdaptation[] $adaptations Adaptations
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $traits, array $adaptations = [], array $attributes = []) {
$this->attributes = $attributes;
$this->traits = $traits;
$this->adaptations = $adaptations;
}
public function getSubNodeNames(): array {
return ['traits', 'adaptations'];
}
public function getType(): string {
return 'Stmt_TraitUse';
}
}

View File

@ -0,0 +1,12 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
abstract class TraitUseAdaptation extends Node\Stmt {
/** @var Node\Name|null Trait name */
public ?Node\Name $trait;
/** @var Node\Identifier Method name */
public Node\Identifier $method;
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt\TraitUseAdaptation;
use PhpParser\Node;
class Alias extends Node\Stmt\TraitUseAdaptation {
/** @var null|int New modifier */
public ?int $newModifier;
/** @var null|Node\Identifier New name */
public ?Node\Identifier $newName;
/**
* Constructs a trait use precedence adaptation node.
*
* @param null|Node\Name $trait Trait name
* @param string|Node\Identifier $method Method name
* @param null|int $newModifier New modifier
* @param null|string|Node\Identifier $newName New name
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(?Node\Name $trait, $method, ?int $newModifier, $newName, array $attributes = []) {
$this->attributes = $attributes;
$this->trait = $trait;
$this->method = \is_string($method) ? new Node\Identifier($method) : $method;
$this->newModifier = $newModifier;
$this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName;
}
public function getSubNodeNames(): array {
return ['trait', 'method', 'newModifier', 'newName'];
}
public function getType(): string {
return 'Stmt_TraitUseAdaptation_Alias';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt\TraitUseAdaptation;
use PhpParser\Node;
class Precedence extends Node\Stmt\TraitUseAdaptation {
/** @var Node\Name[] Overwritten traits */
public array $insteadof;
/**
* Constructs a trait use precedence adaptation node.
*
* @param Node\Name $trait Trait name
* @param string|Node\Identifier $method Method name
* @param Node\Name[] $insteadof Overwritten traits
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Name $trait, $method, array $insteadof, array $attributes = []) {
$this->attributes = $attributes;
$this->trait = $trait;
$this->method = \is_string($method) ? new Node\Identifier($method) : $method;
$this->insteadof = $insteadof;
}
public function getSubNodeNames(): array {
return ['trait', 'method', 'insteadof'];
}
public function getType(): string {
return 'Stmt_TraitUseAdaptation_Precedence';
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Trait_ extends ClassLike {
/**
* Constructs a trait node.
*
* @param string|Node\Identifier $name Name
* @param array{
* stmts?: Node\Stmt[],
* attrGroups?: Node\AttributeGroup[],
* } $subNodes Array of the following optional subnodes:
* 'stmts' => array(): Statements
* 'attrGroups' => array(): PHP attribute groups
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct($name, array $subNodes = [], array $attributes = []) {
$this->attributes = $attributes;
$this->name = \is_string($name) ? new Node\Identifier($name) : $name;
$this->stmts = $subNodes['stmts'] ?? [];
$this->attrGroups = $subNodes['attrGroups'] ?? [];
}
public function getSubNodeNames(): array {
return ['attrGroups', 'name', 'stmts'];
}
public function getType(): string {
return 'Stmt_Trait';
}
}

View File

@ -0,0 +1,37 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class TryCatch extends Node\Stmt {
/** @var Node\Stmt[] Statements */
public array $stmts;
/** @var Catch_[] Catches */
public array $catches;
/** @var null|Finally_ Optional finally node */
public ?Finally_ $finally;
/**
* Constructs a try catch node.
*
* @param Node\Stmt[] $stmts Statements
* @param Catch_[] $catches Catches
* @param null|Finally_ $finally Optional finally node
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $stmts, array $catches, ?Finally_ $finally = null, array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
$this->catches = $catches;
$this->finally = $finally;
}
public function getSubNodeNames(): array {
return ['stmts', 'catches', 'finally'];
}
public function getType(): string {
return 'Stmt_TryCatch';
}
}

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class Unset_ extends Node\Stmt {
/** @var Node\Expr[] Variables to unset */
public array $vars;
/**
* Constructs an unset node.
*
* @param Node\Expr[] $vars Variables to unset
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $vars, array $attributes = []) {
$this->attributes = $attributes;
$this->vars = $vars;
}
public function getSubNodeNames(): array {
return ['vars'];
}
public function getType(): string {
return 'Stmt_Unset';
}
}

View File

@ -0,0 +1,3 @@
<?php declare(strict_types=1);
require __DIR__ . '/../UseItem.php';

View File

@ -0,0 +1,47 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node\Stmt;
use PhpParser\Node\UseItem;
class Use_ extends Stmt {
/**
* Unknown type. Both Stmt\Use_ / Stmt\GroupUse and Stmt\UseUse have a $type property, one of them will always be
* TYPE_UNKNOWN while the other has one of the three other possible types. For normal use statements the type on the
* Stmt\UseUse is unknown. It's only the other way around for mixed group use declarations.
*/
public const TYPE_UNKNOWN = 0;
/** Class or namespace import */
public const TYPE_NORMAL = 1;
/** Function import */
public const TYPE_FUNCTION = 2;
/** Constant import */
public const TYPE_CONSTANT = 3;
/** @var self::TYPE_* Type of alias */
public int $type;
/** @var UseItem[] Aliases */
public array $uses;
/**
* Constructs an alias (use) list node.
*
* @param UseItem[] $uses Aliases
* @param Stmt\Use_::TYPE_* $type Type of alias
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $uses, int $type = self::TYPE_NORMAL, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
$this->uses = $uses;
}
public function getSubNodeNames(): array {
return ['type', 'uses'];
}
public function getType(): string {
return 'Stmt_Use';
}
}

View File

@ -0,0 +1,33 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Stmt;
use PhpParser\Node;
class While_ extends Node\Stmt {
/** @var Node\Expr Condition */
public Node\Expr $cond;
/** @var Node\Stmt[] Statements */
public array $stmts;
/**
* Constructs a while node.
*
* @param Node\Expr $cond Condition
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) {
$this->attributes = $attributes;
$this->cond = $cond;
$this->stmts = $stmts;
}
public function getSubNodeNames(): array {
return ['cond', 'stmts'];
}
public function getType(): string {
return 'Stmt_While';
}
}

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
class UnionType extends ComplexType {
/** @var (Identifier|Name|IntersectionType)[] Types */
public array $types;
/**
* Constructs a union type.
*
* @param (Identifier|Name|IntersectionType)[] $types Types
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $types, array $attributes = []) {
$this->attributes = $attributes;
$this->types = $types;
}
public function getSubNodeNames(): array {
return ['types'];
}
public function getType(): string {
return 'UnionType';
}
}

View File

@ -0,0 +1,55 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\Node;
use PhpParser\NodeAbstract;
use PhpParser\Node\Stmt\Use_;
class UseItem extends NodeAbstract {
/**
* @var Use_::TYPE_* One of the Stmt\Use_::TYPE_* constants. Will only differ from TYPE_UNKNOWN for mixed group uses
*/
public int $type;
/** @var Node\Name Namespace, class, function or constant to alias */
public Name $name;
/** @var Identifier|null Alias */
public ?Identifier $alias;
/**
* Constructs an alias (use) item node.
*
* @param Node\Name $name Namespace/Class to alias
* @param null|string|Identifier $alias Alias
* @param Use_::TYPE_* $type Type of the use element (for mixed group use only)
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(Node\Name $name, $alias = null, int $type = Use_::TYPE_UNKNOWN, array $attributes = []) {
$this->attributes = $attributes;
$this->type = $type;
$this->name = $name;
$this->alias = \is_string($alias) ? new Identifier($alias) : $alias;
}
public function getSubNodeNames(): array {
return ['type', 'name', 'alias'];
}
/**
* Get alias. If not explicitly given this is the last component of the used name.
*/
public function getAlias(): Identifier {
if (null !== $this->alias) {
return $this->alias;
}
return new Identifier($this->name->getLast());
}
public function getType(): string {
return 'UseItem';
}
}
// @deprecated compatibility alias
class_alias(UseItem::class, Stmt\UseUse::class);

View File

@ -0,0 +1,16 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
/**
* Represents a name that is written in source code with a leading dollar,
* but is not a proper variable. The leading dollar is not stored as part of the name.
*
* Examples: Names in property declarations are formatted as variables. Names in static property
* lookups are also formatted as variables.
*/
class VarLikeIdentifier extends Identifier {
public function getType(): string {
return 'VarLikeIdentifier';
}
}

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node;
use PhpParser\NodeAbstract;
/**
* Represents the "..." in "foo(...)" of the first-class callable syntax.
*/
class VariadicPlaceholder extends NodeAbstract {
/**
* Create a variadic argument placeholder (first-class callable syntax).
*
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}
public function getType(): string {
return 'VariadicPlaceholder';
}
public function getSubNodeNames(): array {
return [];
}
}

View File

@ -0,0 +1,181 @@
<?php declare(strict_types=1);
namespace PhpParser;
abstract class NodeAbstract implements Node, \JsonSerializable {
/** @var array<string, mixed> Attributes */
protected array $attributes;
/**
* Creates a Node.
*
* @param array<string, mixed> $attributes Array of attributes
*/
public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}
/**
* Gets line the node started in (alias of getStartLine).
*
* @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getLine(): int {
return $this->attributes['startLine'] ?? -1;
}
/**
* Gets line the node started in.
*
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getStartLine(): int {
return $this->attributes['startLine'] ?? -1;
}
/**
* Gets the line the node ended in.
*
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int End line (or -1 if not available)
* @phpstan-return -1|positive-int
*/
public function getEndLine(): int {
return $this->attributes['endLine'] ?? -1;
}
/**
* Gets the token offset of the first token that is part of this node.
*
* The offset is an index into the array returned by Lexer::getTokens().
*
* Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int Token start position (or -1 if not available)
*/
public function getStartTokenPos(): int {
return $this->attributes['startTokenPos'] ?? -1;
}
/**
* Gets the token offset of the last token that is part of this node.
*
* The offset is an index into the array returned by Lexer::getTokens().
*
* Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int Token end position (or -1 if not available)
*/
public function getEndTokenPos(): int {
return $this->attributes['endTokenPos'] ?? -1;
}
/**
* Gets the file offset of the first character that is part of this node.
*
* Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int File start position (or -1 if not available)
*/
public function getStartFilePos(): int {
return $this->attributes['startFilePos'] ?? -1;
}
/**
* Gets the file offset of the last character that is part of this node.
*
* Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default).
*
* @return int File end position (or -1 if not available)
*/
public function getEndFilePos(): int {
return $this->attributes['endFilePos'] ?? -1;
}
/**
* Gets all comments directly preceding this node.
*
* The comments are also available through the "comments" attribute.
*
* @return Comment[]
*/
public function getComments(): array {
return $this->attributes['comments'] ?? [];
}
/**
* Gets the doc comment of the node.
*
* @return null|Comment\Doc Doc comment object or null
*/
public function getDocComment(): ?Comment\Doc {
$comments = $this->getComments();
for ($i = count($comments) - 1; $i >= 0; $i--) {
$comment = $comments[$i];
if ($comment instanceof Comment\Doc) {
return $comment;
}
}
return null;
}
/**
* Sets the doc comment of the node.
*
* This will either replace an existing doc comment or add it to the comments array.
*
* @param Comment\Doc $docComment Doc comment to set
*/
public function setDocComment(Comment\Doc $docComment): void {
$comments = $this->getComments();
for ($i = count($comments) - 1; $i >= 0; $i--) {
if ($comments[$i] instanceof Comment\Doc) {
// Replace existing doc comment.
$comments[$i] = $docComment;
$this->setAttribute('comments', $comments);
return;
}
}
// Append new doc comment.
$comments[] = $docComment;
$this->setAttribute('comments', $comments);
}
public function setAttribute(string $key, $value): void {
$this->attributes[$key] = $value;
}
public function hasAttribute(string $key): bool {
return array_key_exists($key, $this->attributes);
}
public function getAttribute(string $key, $default = null) {
if (array_key_exists($key, $this->attributes)) {
return $this->attributes[$key];
}
return $default;
}
public function getAttributes(): array {
return $this->attributes;
}
public function setAttributes(array $attributes): void {
$this->attributes = $attributes;
}
/**
* @return array<string, mixed>
*/
public function jsonSerialize(): array {
return ['nodeType' => $this->getType()] + get_object_vars($this);
}
}

View File

@ -0,0 +1,290 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\UseItem;
class NodeDumper {
private bool $dumpComments;
private bool $dumpPositions;
private bool $dumpOtherAttributes;
private ?string $code;
private string $res;
private string $nl;
private const IGNORE_ATTRIBUTES = [
'comments' => true,
'startLine' => true,
'endLine' => true,
'startFilePos' => true,
'endFilePos' => true,
'startTokenPos' => true,
'endTokenPos' => true,
];
/**
* Constructs a NodeDumper.
*
* Supported options:
* * bool dumpComments: Whether comments should be dumped.
* * bool dumpPositions: Whether line/offset information should be dumped. To dump offset
* information, the code needs to be passed to dump().
* * bool dumpOtherAttributes: Whether non-comment, non-position attributes should be dumped.
*
* @param array $options Options (see description)
*/
public function __construct(array $options = []) {
$this->dumpComments = !empty($options['dumpComments']);
$this->dumpPositions = !empty($options['dumpPositions']);
$this->dumpOtherAttributes = !empty($options['dumpOtherAttributes']);
}
/**
* Dumps a node or array.
*
* @param array|Node $node Node or array to dump
* @param string|null $code Code corresponding to dumped AST. This only needs to be passed if
* the dumpPositions option is enabled and the dumping of node offsets
* is desired.
*
* @return string Dumped value
*/
public function dump($node, ?string $code = null): string {
$this->code = $code;
$this->res = '';
$this->nl = "\n";
$this->dumpRecursive($node, false);
return $this->res;
}
/** @param mixed $node */
protected function dumpRecursive($node, bool $indent = true): void {
if ($indent) {
$this->nl .= " ";
}
if ($node instanceof Node) {
$this->res .= $node->getType();
if ($this->dumpPositions && null !== $p = $this->dumpPosition($node)) {
$this->res .= $p;
}
$this->res .= '(';
foreach ($node->getSubNodeNames() as $key) {
$this->res .= "$this->nl " . $key . ': ';
$value = $node->$key;
if (\is_int($value)) {
if ('flags' === $key || 'newModifier' === $key) {
$this->res .= $this->dumpFlags($value);
continue;
}
if ('type' === $key && $node instanceof Include_) {
$this->res .= $this->dumpIncludeType($value);
continue;
}
if ('type' === $key
&& ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) {
$this->res .= $this->dumpUseType($value);
continue;
}
}
$this->dumpRecursive($value);
}
if ($this->dumpComments && $comments = $node->getComments()) {
$this->res .= "$this->nl comments: ";
$this->dumpRecursive($comments);
}
if ($this->dumpOtherAttributes) {
foreach ($node->getAttributes() as $key => $value) {
if (isset(self::IGNORE_ATTRIBUTES[$key])) {
continue;
}
$this->res .= "$this->nl $key: ";
if (\is_int($value)) {
if ('kind' === $key) {
if ($node instanceof Int_) {
$this->res .= $this->dumpIntKind($value);
continue;
}
if ($node instanceof String_ || $node instanceof InterpolatedString) {
$this->res .= $this->dumpStringKind($value);
continue;
}
if ($node instanceof Array_) {
$this->res .= $this->dumpArrayKind($value);
continue;
}
if ($node instanceof List_) {
$this->res .= $this->dumpListKind($value);
continue;
}
}
}
$this->dumpRecursive($value);
}
}
$this->res .= "$this->nl)";
} elseif (\is_array($node)) {
$this->res .= 'array(';
foreach ($node as $key => $value) {
$this->res .= "$this->nl " . $key . ': ';
$this->dumpRecursive($value);
}
$this->res .= "$this->nl)";
} elseif ($node instanceof Comment) {
$this->res .= \str_replace("\n", $this->nl, $node->getReformattedText());
} elseif (\is_string($node)) {
$this->res .= \str_replace("\n", $this->nl, (string)$node);
} elseif (\is_int($node) || \is_float($node)) {
$this->res .= $node;
} elseif (null === $node) {
$this->res .= 'null';
} elseif (false === $node) {
$this->res .= 'false';
} elseif (true === $node) {
$this->res .= 'true';
} else {
throw new \InvalidArgumentException('Can only dump nodes and arrays.');
}
if ($indent) {
$this->nl = \substr($this->nl, 0, -4);
}
}
protected function dumpFlags(int $flags): string {
$strs = [];
if ($flags & Modifiers::PUBLIC) {
$strs[] = 'PUBLIC';
}
if ($flags & Modifiers::PROTECTED) {
$strs[] = 'PROTECTED';
}
if ($flags & Modifiers::PRIVATE) {
$strs[] = 'PRIVATE';
}
if ($flags & Modifiers::ABSTRACT) {
$strs[] = 'ABSTRACT';
}
if ($flags & Modifiers::STATIC) {
$strs[] = 'STATIC';
}
if ($flags & Modifiers::FINAL) {
$strs[] = 'FINAL';
}
if ($flags & Modifiers::READONLY) {
$strs[] = 'READONLY';
}
if ($strs) {
return implode(' | ', $strs) . ' (' . $flags . ')';
} else {
return (string) $flags;
}
}
/** @param array<int, string> $map */
private function dumpEnum(int $value, array $map): string {
if (!isset($map[$value])) {
return (string) $value;
}
return $map[$value] . ' (' . $value . ')';
}
private function dumpIncludeType(int $type): string {
return $this->dumpEnum($type, [
Include_::TYPE_INCLUDE => 'TYPE_INCLUDE',
Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE',
Include_::TYPE_REQUIRE => 'TYPE_REQUIRE',
Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE',
]);
}
private function dumpUseType(int $type): string {
return $this->dumpEnum($type, [
Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN',
Use_::TYPE_NORMAL => 'TYPE_NORMAL',
Use_::TYPE_FUNCTION => 'TYPE_FUNCTION',
Use_::TYPE_CONSTANT => 'TYPE_CONSTANT',
]);
}
private function dumpIntKind(int $kind): string {
return $this->dumpEnum($kind, [
Int_::KIND_BIN => 'KIND_BIN',
Int_::KIND_OCT => 'KIND_OCT',
Int_::KIND_DEC => 'KIND_DEC',
Int_::KIND_HEX => 'KIND_HEX',
]);
}
private function dumpStringKind(int $kind): string {
return $this->dumpEnum($kind, [
String_::KIND_SINGLE_QUOTED => 'KIND_SINGLE_QUOTED',
String_::KIND_DOUBLE_QUOTED => 'KIND_DOUBLE_QUOTED',
String_::KIND_HEREDOC => 'KIND_HEREDOC',
String_::KIND_NOWDOC => 'KIND_NOWDOC',
]);
}
private function dumpArrayKind(int $kind): string {
return $this->dumpEnum($kind, [
Array_::KIND_LONG => 'KIND_LONG',
Array_::KIND_SHORT => 'KIND_SHORT',
]);
}
private function dumpListKind(int $kind): string {
return $this->dumpEnum($kind, [
List_::KIND_LIST => 'KIND_LIST',
List_::KIND_ARRAY => 'KIND_ARRAY',
]);
}
/**
* Dump node position, if possible.
*
* @param Node $node Node for which to dump position
*
* @return string|null Dump of position, or null if position information not available
*/
protected function dumpPosition(Node $node): ?string {
if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) {
return null;
}
$start = $node->getStartLine();
$end = $node->getEndLine();
if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos')
&& null !== $this->code
) {
$start .= ':' . $this->toColumn($this->code, $node->getStartFilePos());
$end .= ':' . $this->toColumn($this->code, $node->getEndFilePos());
}
return "[$start - $end]";
}
// Copied from Error class
private function toColumn(string $code, int $pos): int {
if ($pos > strlen($code)) {
throw new \RuntimeException('Invalid position information');
}
$lineStartPos = strrpos($code, "\n", $pos - strlen($code));
if (false === $lineStartPos) {
$lineStartPos = -1;
}
return $pos - $lineStartPos;
}
}

View File

@ -0,0 +1,90 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\NodeVisitor\FindingVisitor;
use PhpParser\NodeVisitor\FirstFindingVisitor;
class NodeFinder {
/**
* Find all nodes satisfying a filter callback.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param callable $filter Filter callback: function(Node $node) : bool
*
* @return Node[] Found nodes satisfying the filter callback
*/
public function find($nodes, callable $filter): array {
if ($nodes === []) {
return [];
}
if (!is_array($nodes)) {
$nodes = [$nodes];
}
$visitor = new FindingVisitor($filter);
$traverser = new NodeTraverser($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNodes();
}
/**
* Find all nodes that are instances of a certain class.
* @template TNode as Node
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param class-string<TNode> $class Class name
*
* @return TNode[] Found nodes (all instances of $class)
*/
public function findInstanceOf($nodes, string $class): array {
return $this->find($nodes, function ($node) use ($class) {
return $node instanceof $class;
});
}
/**
* Find first node satisfying a filter callback.
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param callable $filter Filter callback: function(Node $node) : bool
*
* @return null|Node Found node (or null if none found)
*/
public function findFirst($nodes, callable $filter): ?Node {
if ($nodes === []) {
return null;
}
if (!is_array($nodes)) {
$nodes = [$nodes];
}
$visitor = new FirstFindingVisitor($filter);
$traverser = new NodeTraverser($visitor);
$traverser->traverse($nodes);
return $visitor->getFoundNode();
}
/**
* Find first node that is an instance of a certain class.
*
* @template TNode as Node
*
* @param Node|Node[] $nodes Single node or array of nodes to search in
* @param class-string<TNode> $class Class name
*
* @return null|TNode Found node, which is an instance of $class (or null if none found)
*/
public function findFirstInstanceOf($nodes, string $class): ?Node {
return $this->findFirst($nodes, function ($node) use ($class) {
return $node instanceof $class;
});
}
}

View File

@ -0,0 +1,278 @@
<?php declare(strict_types=1);
namespace PhpParser;
class NodeTraverser implements NodeTraverserInterface {
/**
* @deprecated Use NodeVisitor::DONT_TRAVERSE_CHILDREN instead.
*/
public const DONT_TRAVERSE_CHILDREN = NodeVisitor::DONT_TRAVERSE_CHILDREN;
/**
* @deprecated Use NodeVisitor::STOP_TRAVERSAL instead.
*/
public const STOP_TRAVERSAL = NodeVisitor::STOP_TRAVERSAL;
/**
* @deprecated Use NodeVisitor::REMOVE_NODE instead.
*/
public const REMOVE_NODE = NodeVisitor::REMOVE_NODE;
/**
* @deprecated Use NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN instead.
*/
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
/** @var list<NodeVisitor> Visitors */
protected array $visitors = [];
/** @var bool Whether traversal should be stopped */
protected bool $stopTraversal;
/**
* Create a traverser with the given visitors.
*
* @param NodeVisitor ...$visitors Node visitors
*/
public function __construct(NodeVisitor ...$visitors) {
$this->visitors = $visitors;
}
/**
* Adds a visitor.
*
* @param NodeVisitor $visitor Visitor to add
*/
public function addVisitor(NodeVisitor $visitor): void {
$this->visitors[] = $visitor;
}
/**
* Removes an added visitor.
*/
public function removeVisitor(NodeVisitor $visitor): void {
$index = array_search($visitor, $this->visitors);
if ($index !== false) {
array_splice($this->visitors, $index, 1, []);
}
}
/**
* Traverses an array of nodes using the registered visitors.
*
* @param Node[] $nodes Array of nodes
*
* @return Node[] Traversed array of nodes
*/
public function traverse(array $nodes): array {
$this->stopTraversal = false;
foreach ($this->visitors as $visitor) {
if (null !== $return = $visitor->beforeTraverse($nodes)) {
$nodes = $return;
}
}
$nodes = $this->traverseArray($nodes);
for ($i = \count($this->visitors) - 1; $i >= 0; --$i) {
$visitor = $this->visitors[$i];
if (null !== $return = $visitor->afterTraverse($nodes)) {
$nodes = $return;
}
}
return $nodes;
}
/**
* Recursively traverse a node.
*
* @param Node $node Node to traverse.
*/
protected function traverseNode(Node $node): void {
foreach ($node->getSubNodeNames() as $name) {
$subNode = $node->$name;
if (\is_array($subNode)) {
$node->$name = $this->traverseArray($subNode);
if ($this->stopTraversal) {
break;
}
} elseif ($subNode instanceof Node) {
$traverseChildren = true;
$visitorIndex = -1;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
continue 2;
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
}
if ($traverseChildren) {
$this->traverseNode($subNode);
if ($this->stopTraversal) {
break;
}
}
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($subNode);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($subNode, $return);
$subNode = $node->$name = $return;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
$node->$name = null;
break;
} elseif (\is_array($return)) {
throw new \LogicException(
'leaveNode() may only return an array ' .
'if the parent structure is an array'
);
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
}
}
}
}
/**
* Recursively traverse array (usually of nodes).
*
* @param array $nodes Array to traverse
*
* @return array Result of traversal (may be original array or changed one)
*/
protected function traverseArray(array $nodes): array {
$doNodes = [];
foreach ($nodes as $i => $node) {
if ($node instanceof Node) {
$traverseChildren = true;
$visitorIndex = -1;
foreach ($this->visitors as $visitorIndex => $visitor) {
$return = $visitor->enterNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
continue 2;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
continue 2;
} elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) {
$traverseChildren = false;
} elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) {
$traverseChildren = false;
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'enterNode() returned invalid value of type ' . gettype($return)
);
}
}
}
if ($traverseChildren) {
$this->traverseNode($node);
if ($this->stopTraversal) {
break;
}
}
for (; $visitorIndex >= 0; --$visitorIndex) {
$visitor = $this->visitors[$visitorIndex];
$return = $visitor->leaveNode($node);
if (null !== $return) {
if ($return instanceof Node) {
$this->ensureReplacementReasonable($node, $return);
$nodes[$i] = $node = $return;
} elseif (\is_array($return)) {
$doNodes[] = [$i, $return];
break;
} elseif (NodeVisitor::REMOVE_NODE === $return) {
$doNodes[] = [$i, []];
break;
} elseif (NodeVisitor::STOP_TRAVERSAL === $return) {
$this->stopTraversal = true;
break 2;
} elseif (NodeVisitor::REPLACE_WITH_NULL === $return) {
throw new \LogicException(
'REPLACE_WITH_NULL can not be used if the parent structure is an array');
} else {
throw new \LogicException(
'leaveNode() returned invalid value of type ' . gettype($return)
);
}
}
}
} elseif (\is_array($node)) {
throw new \LogicException('Invalid node structure: Contains nested arrays');
}
}
if (!empty($doNodes)) {
while (list($i, $replace) = array_pop($doNodes)) {
array_splice($nodes, $i, 1, $replace);
}
}
return $nodes;
}
private function ensureReplacementReasonable(Node $old, Node $new): void {
if ($old instanceof Node\Stmt && $new instanceof Node\Expr) {
throw new \LogicException(
"Trying to replace statement ({$old->getType()}) " .
"with expression ({$new->getType()}). Are you missing a " .
"Stmt_Expression wrapper?"
);
}
if ($old instanceof Node\Expr && $new instanceof Node\Stmt) {
throw new \LogicException(
"Trying to replace expression ({$old->getType()}) " .
"with statement ({$new->getType()})"
);
}
}
}

View File

@ -0,0 +1,26 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface NodeTraverserInterface {
/**
* Adds a visitor.
*
* @param NodeVisitor $visitor Visitor to add
*/
public function addVisitor(NodeVisitor $visitor): void;
/**
* Removes an added visitor.
*/
public function removeVisitor(NodeVisitor $visitor): void;
/**
* Traverses an array of nodes using the registered visitors.
*
* @param Node[] $nodes Array of nodes
*
* @return Node[] Traversed array of nodes
*/
public function traverse(array $nodes): array;
}

View File

@ -0,0 +1,124 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface NodeVisitor {
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will still be called on the current
* node and leaveNode() will also be invoked for the current node.
*/
public const DONT_TRAVERSE_CHILDREN = 1;
/**
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
* STOP_TRAVERSAL, traversal is aborted.
*
* The afterTraverse() method will still be invoked.
*/
public const STOP_TRAVERSAL = 2;
/**
* If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
* in an array, it will be removed from the array.
*
* For subsequent visitors leaveNode() will still be invoked for the
* removed node.
*/
public const REMOVE_NODE = 3;
/**
* If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
* of the current node will not be traversed for any visitors.
*
* For subsequent visitors enterNode() will not be called as well.
* leaveNode() will be invoked for visitors that has enterNode() method invoked.
*/
public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
/**
* If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns REPLACE_WITH_NULL,
* the node will be replaced with null. This is not a legal return value if the node is part
* of an array, rather than another node.
*/
public const REPLACE_WITH_NULL = 5;
/**
* Called once before traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return null|Node[] Array of nodes
*/
public function beforeTraverse(array $nodes);
/**
* Called when entering a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * NodeVisitor::REMOVE_NODE
* => $node is removed from the parent array
* * NodeVisitor::REPLACE_WITH_NULL
* => $node is replaced with null
* * NodeVisitor::DONT_TRAVERSE_CHILDREN
* => Children of $node are not traversed. $node stays as-is
* * NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN
* => Further visitors for the current node are skipped, and its children are not
* traversed. $node stays as-is.
* * NodeVisitor::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return null|int|Node|Node[] Replacement node (or special return value)
*/
public function enterNode(Node $node);
/**
* Called when leaving a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * NodeVisitor::REMOVE_NODE
* => $node is removed from the parent array
* * NodeVisitor::REPLACE_WITH_NULL
* => $node is replaced with null
* * NodeVisitor::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return null|int|Node|Node[] Replacement node (or special return value)
*/
public function leaveNode(Node $node);
/**
* Called once after traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return null|Node[] Array of nodes
*/
public function afterTraverse(array $nodes);
}

View File

@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Visitor cloning all nodes and linking to the original nodes using an attribute.
*
* This visitor is required to perform format-preserving pretty prints.
*/
class CloningVisitor extends NodeVisitorAbstract {
public function enterNode(Node $origNode) {
$node = clone $origNode;
$node->setAttribute('origNode', $origNode);
return $node;
}
}

View File

@ -0,0 +1,82 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Token;
class CommentAnnotatingVisitor extends NodeVisitorAbstract {
/** @var int Last seen token start position */
private int $pos = 0;
/** @var Token[] Token array */
private array $tokens;
/** @var list<int> Token positions of comments */
private array $commentPositions = [];
/**
* Create a comment annotation visitor.
*
* @param Token[] $tokens Token array
*/
public function __construct(array $tokens) {
$this->tokens = $tokens;
// Collect positions of comments. We use this to avoid traversing parts of the AST where
// there are no comments.
foreach ($tokens as $i => $token) {
if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) {
$this->commentPositions[] = $i;
}
}
}
public function enterNode(Node $node) {
$nextCommentPos = current($this->commentPositions);
if ($nextCommentPos === false) {
// No more comments.
return self::STOP_TRAVERSAL;
}
$oldPos = $this->pos;
$this->pos = $pos = $node->getStartTokenPos();
if ($nextCommentPos > $oldPos && $nextCommentPos < $pos) {
$comments = [];
while (--$pos >= $oldPos) {
$token = $this->tokens[$pos];
if ($token->id === \T_DOC_COMMENT) {
$comments[] = new Comment\Doc(
$token->text, $token->line, $token->pos, $pos,
$token->getEndLine(), $token->getEndPos() - 1, $pos);
continue;
}
if ($token->id === \T_COMMENT) {
$comments[] = new Comment(
$token->text, $token->line, $token->pos, $pos,
$token->getEndLine(), $token->getEndPos() - 1, $pos);
continue;
}
if ($token->id !== \T_WHITESPACE) {
break;
}
}
if (!empty($comments)) {
$node->setAttribute('comments', array_reverse($comments));
}
do {
$nextCommentPos = next($this->commentPositions);
} while ($nextCommentPos !== false && $nextCommentPos < $this->pos);
}
$endPos = $node->getEndTokenPos();
if ($nextCommentPos > $endPos) {
// Skip children if there are no comments located inside this node.
$this->pos = $endPos;
return self::DONT_TRAVERSE_CHILDREN;
}
return null;
}
}

View File

@ -0,0 +1,47 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* This visitor can be used to find and collect all nodes satisfying some criterion determined by
* a filter callback.
*/
class FindingVisitor extends NodeVisitorAbstract {
/** @var callable Filter callback */
protected $filterCallback;
/** @var Node[] Found nodes */
protected array $foundNodes;
public function __construct(callable $filterCallback) {
$this->filterCallback = $filterCallback;
}
/**
* Get found nodes satisfying the filter callback.
*
* Nodes are returned in pre-order.
*
* @return Node[] Found nodes
*/
public function getFoundNodes(): array {
return $this->foundNodes;
}
public function beforeTraverse(array $nodes): ?array {
$this->foundNodes = [];
return null;
}
public function enterNode(Node $node) {
$filterCallback = $this->filterCallback;
if ($filterCallback($node)) {
$this->foundNodes[] = $node;
}
return null;
}
}

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use PhpParser\NodeVisitorAbstract;
/**
* This visitor can be used to find the first node satisfying some criterion determined by
* a filter callback.
*/
class FirstFindingVisitor extends NodeVisitorAbstract {
/** @var callable Filter callback */
protected $filterCallback;
/** @var null|Node Found node */
protected ?Node $foundNode;
public function __construct(callable $filterCallback) {
$this->filterCallback = $filterCallback;
}
/**
* Get found node satisfying the filter callback.
*
* Returns null if no node satisfies the filter callback.
*
* @return null|Node Found node (or null if not found)
*/
public function getFoundNode(): ?Node {
return $this->foundNode;
}
public function beforeTraverse(array $nodes): ?array {
$this->foundNode = null;
return null;
}
public function enterNode(Node $node) {
$filterCallback = $this->filterCallback;
if ($filterCallback($node)) {
$this->foundNode = $node;
return NodeVisitor::STOP_TRAVERSAL;
}
return null;
}
}

View File

@ -0,0 +1,262 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\ErrorHandler;
use PhpParser\NameContext;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;
class NameResolver extends NodeVisitorAbstract {
/** @var NameContext Naming context */
protected NameContext $nameContext;
/** @var bool Whether to preserve original names */
protected bool $preserveOriginalNames;
/** @var bool Whether to replace resolved nodes in place, or to add resolvedNode attributes */
protected bool $replaceNodes;
/**
* Constructs a name resolution visitor.
*
* Options:
* * preserveOriginalNames (default false): An "originalName" attribute will be added to
* all name nodes that underwent resolution.
* * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a
* resolvedName attribute is added. (Names that cannot be statically resolved receive a
* namespacedName attribute, as usual.)
*
* @param ErrorHandler|null $errorHandler Error handler
* @param array{preserveOriginalNames?: bool, replaceNodes?: bool} $options Options
*/
public function __construct(?ErrorHandler $errorHandler = null, array $options = []) {
$this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing());
$this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false;
$this->replaceNodes = $options['replaceNodes'] ?? true;
}
/**
* Get name resolution context.
*/
public function getNameContext(): NameContext {
return $this->nameContext;
}
public function beforeTraverse(array $nodes): ?array {
$this->nameContext->startNamespace();
return null;
}
public function enterNode(Node $node) {
if ($node instanceof Stmt\Namespace_) {
$this->nameContext->startNamespace($node->name);
} elseif ($node instanceof Stmt\Use_) {
foreach ($node->uses as $use) {
$this->addAlias($use, $node->type, null);
}
} elseif ($node instanceof Stmt\GroupUse) {
foreach ($node->uses as $use) {
$this->addAlias($use, $node->type, $node->prefix);
}
} elseif ($node instanceof Stmt\Class_) {
if (null !== $node->extends) {
$node->extends = $this->resolveClassName($node->extends);
}
foreach ($node->implements as &$interface) {
$interface = $this->resolveClassName($interface);
}
$this->resolveAttrGroups($node);
if (null !== $node->name) {
$this->addNamespacedName($node);
} else {
$node->namespacedName = null;
}
} elseif ($node instanceof Stmt\Interface_) {
foreach ($node->extends as &$interface) {
$interface = $this->resolveClassName($interface);
}
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\Enum_) {
foreach ($node->implements as &$interface) {
$interface = $this->resolveClassName($interface);
}
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\Trait_) {
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\Function_) {
$this->resolveSignature($node);
$this->resolveAttrGroups($node);
$this->addNamespacedName($node);
} elseif ($node instanceof Stmt\ClassMethod
|| $node instanceof Expr\Closure
|| $node instanceof Expr\ArrowFunction
) {
$this->resolveSignature($node);
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\Property) {
if (null !== $node->type) {
$node->type = $this->resolveType($node->type);
}
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\Const_) {
foreach ($node->consts as $const) {
$this->addNamespacedName($const);
}
} elseif ($node instanceof Stmt\ClassConst) {
if (null !== $node->type) {
$node->type = $this->resolveType($node->type);
}
$this->resolveAttrGroups($node);
} elseif ($node instanceof Stmt\EnumCase) {
$this->resolveAttrGroups($node);
} elseif ($node instanceof Expr\StaticCall
|| $node instanceof Expr\StaticPropertyFetch
|| $node instanceof Expr\ClassConstFetch
|| $node instanceof Expr\New_
|| $node instanceof Expr\Instanceof_
) {
if ($node->class instanceof Name) {
$node->class = $this->resolveClassName($node->class);
}
} elseif ($node instanceof Stmt\Catch_) {
foreach ($node->types as &$type) {
$type = $this->resolveClassName($type);
}
} elseif ($node instanceof Expr\FuncCall) {
if ($node->name instanceof Name) {
$node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
}
} elseif ($node instanceof Expr\ConstFetch) {
$node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
} elseif ($node instanceof Stmt\TraitUse) {
foreach ($node->traits as &$trait) {
$trait = $this->resolveClassName($trait);
}
foreach ($node->adaptations as $adaptation) {
if (null !== $adaptation->trait) {
$adaptation->trait = $this->resolveClassName($adaptation->trait);
}
if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
foreach ($adaptation->insteadof as &$insteadof) {
$insteadof = $this->resolveClassName($insteadof);
}
}
}
}
return null;
}
/** @param Stmt\Use_::TYPE_* $type */
private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void {
// Add prefix for group uses
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
// Type is determined either by individual element or whole use declaration
$type |= $use->type;
$this->nameContext->addAlias(
$name, (string) $use->getAlias(), $type, $use->getAttributes()
);
}
/** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure|Expr\ArrowFunction $node */
private function resolveSignature($node): void {
foreach ($node->params as $param) {
$param->type = $this->resolveType($param->type);
$this->resolveAttrGroups($param);
}
$node->returnType = $this->resolveType($node->returnType);
}
/**
* @template T of Node\Identifier|Name|Node\ComplexType|null
* @param T $node
* @return T
*/
private function resolveType(?Node $node): ?Node {
if ($node instanceof Name) {
return $this->resolveClassName($node);
}
if ($node instanceof Node\NullableType) {
$node->type = $this->resolveType($node->type);
return $node;
}
if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) {
foreach ($node->types as &$type) {
$type = $this->resolveType($type);
}
return $node;
}
return $node;
}
/**
* Resolve name, according to name resolver options.
*
* @param Name $name Function or constant name to resolve
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
*
* @return Name Resolved name, or original name with attribute
*/
protected function resolveName(Name $name, int $type): Name {
if (!$this->replaceNodes) {
$resolvedName = $this->nameContext->getResolvedName($name, $type);
if (null !== $resolvedName) {
$name->setAttribute('resolvedName', $resolvedName);
} else {
$name->setAttribute('namespacedName', FullyQualified::concat(
$this->nameContext->getNamespace(), $name, $name->getAttributes()));
}
return $name;
}
if ($this->preserveOriginalNames) {
// Save the original name
$originalName = $name;
$name = clone $originalName;
$name->setAttribute('originalName', $originalName);
}
$resolvedName = $this->nameContext->getResolvedName($name, $type);
if (null !== $resolvedName) {
return $resolvedName;
}
// unqualified names inside a namespace cannot be resolved at compile-time
// add the namespaced version of the name as an attribute
$name->setAttribute('namespacedName', FullyQualified::concat(
$this->nameContext->getNamespace(), $name, $name->getAttributes()));
return $name;
}
protected function resolveClassName(Name $name): Name {
return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
}
protected function addNamespacedName(Node $node): void {
$node->namespacedName = Name::concat(
$this->nameContext->getNamespace(), (string) $node->name);
}
protected function resolveAttrGroups(Node $node): void {
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
$attr->name = $this->resolveClassName($attr->name);
}
}
}
}

View File

@ -0,0 +1,51 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* Visitor that connects a child node to its parent node
* as well as its sibling nodes.
*
* On the child node, the parent node can be accessed through
* <code>$node->getAttribute('parent')</code>, the previous
* node can be accessed through <code>$node->getAttribute('previous')</code>,
* and the next node can be accessed through <code>$node->getAttribute('next')</code>.
*/
final class NodeConnectingVisitor extends NodeVisitorAbstract {
/**
* @var Node[]
*/
private array $stack = [];
/**
* @var ?Node
*/
private $previous;
public function beforeTraverse(array $nodes) {
$this->stack = [];
$this->previous = null;
}
public function enterNode(Node $node) {
if (!empty($this->stack)) {
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
}
if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
$node->setAttribute('previous', $this->previous);
$this->previous->setAttribute('next', $node);
}
$this->stack[] = $node;
}
public function leaveNode(Node $node) {
$this->previous = $node;
array_pop($this->stack);
}
}

View File

@ -0,0 +1,38 @@
<?php declare(strict_types=1);
namespace PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use function array_pop;
use function count;
/**
* Visitor that connects a child node to its parent node.
*
* On the child node, the parent node can be accessed through
* <code>$node->getAttribute('parent')</code>.
*/
final class ParentConnectingVisitor extends NodeVisitorAbstract {
/**
* @var Node[]
*/
private array $stack = [];
public function beforeTraverse(array $nodes) {
$this->stack = [];
}
public function enterNode(Node $node) {
if (!empty($this->stack)) {
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
}
$this->stack[] = $node;
}
public function leaveNode(Node $node) {
array_pop($this->stack);
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace PhpParser;
/**
* @codeCoverageIgnore
*/
abstract class NodeVisitorAbstract implements NodeVisitor {
public function beforeTraverse(array $nodes) {
return null;
}
public function enterNode(Node $node) {
return null;
}
public function leaveNode(Node $node) {
return null;
}
public function afterTraverse(array $nodes) {
return null;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace PhpParser;
interface Parser {
/**
* Parses PHP code into a node tree.
*
* @param string $code The source code to parse
* @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
* to ErrorHandler\Throwing.
*
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
* the parser was unable to recover from an error).
*/
public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array;
/**
* Return tokens for the last parse.
*
* @return Token[]
*/
public function getTokens(): array;
}

View File

@ -0,0 +1,2743 @@
<?php declare(strict_types=1);
namespace PhpParser\Parser;
use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
/* This is an automatically GENERATED file, which should not be manually edited.
* Instead edit one of the following:
* * the grammar file grammar/php.y
* * the skeleton file grammar/parser.template
* * the preprocessing script grammar/rebuildParsers.php
*/
class Php7 extends \PhpParser\ParserAbstract
{
public const YYERRTOK = 256;
public const T_THROW = 257;
public const T_INCLUDE = 258;
public const T_INCLUDE_ONCE = 259;
public const T_EVAL = 260;
public const T_REQUIRE = 261;
public const T_REQUIRE_ONCE = 262;
public const T_LOGICAL_OR = 263;
public const T_LOGICAL_XOR = 264;
public const T_LOGICAL_AND = 265;
public const T_PRINT = 266;
public const T_YIELD = 267;
public const T_DOUBLE_ARROW = 268;
public const T_YIELD_FROM = 269;
public const T_PLUS_EQUAL = 270;
public const T_MINUS_EQUAL = 271;
public const T_MUL_EQUAL = 272;
public const T_DIV_EQUAL = 273;
public const T_CONCAT_EQUAL = 274;
public const T_MOD_EQUAL = 275;
public const T_AND_EQUAL = 276;
public const T_OR_EQUAL = 277;
public const T_XOR_EQUAL = 278;
public const T_SL_EQUAL = 279;
public const T_SR_EQUAL = 280;
public const T_POW_EQUAL = 281;
public const T_COALESCE_EQUAL = 282;
public const T_COALESCE = 283;
public const T_BOOLEAN_OR = 284;
public const T_BOOLEAN_AND = 285;
public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286;
public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287;
public const T_IS_EQUAL = 288;
public const T_IS_NOT_EQUAL = 289;
public const T_IS_IDENTICAL = 290;
public const T_IS_NOT_IDENTICAL = 291;
public const T_SPACESHIP = 292;
public const T_IS_SMALLER_OR_EQUAL = 293;
public const T_IS_GREATER_OR_EQUAL = 294;
public const T_SL = 295;
public const T_SR = 296;
public const T_INSTANCEOF = 297;
public const T_INC = 298;
public const T_DEC = 299;
public const T_INT_CAST = 300;
public const T_DOUBLE_CAST = 301;
public const T_STRING_CAST = 302;
public const T_ARRAY_CAST = 303;
public const T_OBJECT_CAST = 304;
public const T_BOOL_CAST = 305;
public const T_UNSET_CAST = 306;
public const T_POW = 307;
public const T_NEW = 308;
public const T_CLONE = 309;
public const T_EXIT = 310;
public const T_IF = 311;
public const T_ELSEIF = 312;
public const T_ELSE = 313;
public const T_ENDIF = 314;
public const T_LNUMBER = 315;
public const T_DNUMBER = 316;
public const T_STRING = 317;
public const T_STRING_VARNAME = 318;
public const T_VARIABLE = 319;
public const T_NUM_STRING = 320;
public const T_INLINE_HTML = 321;
public const T_ENCAPSED_AND_WHITESPACE = 322;
public const T_CONSTANT_ENCAPSED_STRING = 323;
public const T_ECHO = 324;
public const T_DO = 325;
public const T_WHILE = 326;
public const T_ENDWHILE = 327;
public const T_FOR = 328;
public const T_ENDFOR = 329;
public const T_FOREACH = 330;
public const T_ENDFOREACH = 331;
public const T_DECLARE = 332;
public const T_ENDDECLARE = 333;
public const T_AS = 334;
public const T_SWITCH = 335;
public const T_MATCH = 336;
public const T_ENDSWITCH = 337;
public const T_CASE = 338;
public const T_DEFAULT = 339;
public const T_BREAK = 340;
public const T_CONTINUE = 341;
public const T_GOTO = 342;
public const T_FUNCTION = 343;
public const T_FN = 344;
public const T_CONST = 345;
public const T_RETURN = 346;
public const T_TRY = 347;
public const T_CATCH = 348;
public const T_FINALLY = 349;
public const T_USE = 350;
public const T_INSTEADOF = 351;
public const T_GLOBAL = 352;
public const T_STATIC = 353;
public const T_ABSTRACT = 354;
public const T_FINAL = 355;
public const T_PRIVATE = 356;
public const T_PROTECTED = 357;
public const T_PUBLIC = 358;
public const T_READONLY = 359;
public const T_VAR = 360;
public const T_UNSET = 361;
public const T_ISSET = 362;
public const T_EMPTY = 363;
public const T_HALT_COMPILER = 364;
public const T_CLASS = 365;
public const T_TRAIT = 366;
public const T_INTERFACE = 367;
public const T_ENUM = 368;
public const T_EXTENDS = 369;
public const T_IMPLEMENTS = 370;
public const T_OBJECT_OPERATOR = 371;
public const T_NULLSAFE_OBJECT_OPERATOR = 372;
public const T_LIST = 373;
public const T_ARRAY = 374;
public const T_CALLABLE = 375;
public const T_CLASS_C = 376;
public const T_TRAIT_C = 377;
public const T_METHOD_C = 378;
public const T_FUNC_C = 379;
public const T_LINE = 380;
public const T_FILE = 381;
public const T_START_HEREDOC = 382;
public const T_END_HEREDOC = 383;
public const T_DOLLAR_OPEN_CURLY_BRACES = 384;
public const T_CURLY_OPEN = 385;
public const T_PAAMAYIM_NEKUDOTAYIM = 386;
public const T_NAMESPACE = 387;
public const T_NS_C = 388;
public const T_DIR = 389;
public const T_NS_SEPARATOR = 390;
public const T_ELLIPSIS = 391;
public const T_NAME_FULLY_QUALIFIED = 392;
public const T_NAME_QUALIFIED = 393;
public const T_NAME_RELATIVE = 394;
public const T_ATTRIBUTE = 395;
protected int $tokenToSymbolMapSize = 396;
protected int $actionTableSize = 1268;
protected int $gotoTableSize = 730;
protected int $invalidSymbol = 168;
protected int $errorSymbol = 1;
protected int $defaultAction = -32766;
protected int $unexpectedTokenRule = 32767;
protected int $YY2TBLSTATE = 437;
protected int $numNonLeafStates = 743;
protected array $symbolToName = array(
"EOF",
"error",
"T_THROW",
"T_INCLUDE",
"T_INCLUDE_ONCE",
"T_EVAL",
"T_REQUIRE",
"T_REQUIRE_ONCE",
"','",
"T_LOGICAL_OR",
"T_LOGICAL_XOR",
"T_LOGICAL_AND",
"T_PRINT",
"T_YIELD",
"T_DOUBLE_ARROW",
"T_YIELD_FROM",
"'='",
"T_PLUS_EQUAL",
"T_MINUS_EQUAL",
"T_MUL_EQUAL",
"T_DIV_EQUAL",
"T_CONCAT_EQUAL",
"T_MOD_EQUAL",
"T_AND_EQUAL",
"T_OR_EQUAL",
"T_XOR_EQUAL",
"T_SL_EQUAL",
"T_SR_EQUAL",
"T_POW_EQUAL",
"T_COALESCE_EQUAL",
"'?'",
"':'",
"T_COALESCE",
"T_BOOLEAN_OR",
"T_BOOLEAN_AND",
"'|'",
"'^'",
"T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
"T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
"T_IS_EQUAL",
"T_IS_NOT_EQUAL",
"T_IS_IDENTICAL",
"T_IS_NOT_IDENTICAL",
"T_SPACESHIP",
"'<'",
"T_IS_SMALLER_OR_EQUAL",
"'>'",
"T_IS_GREATER_OR_EQUAL",
"T_SL",
"T_SR",
"'+'",
"'-'",
"'.'",
"'*'",
"'/'",
"'%'",
"'!'",
"T_INSTANCEOF",
"'~'",
"T_INC",
"T_DEC",
"T_INT_CAST",
"T_DOUBLE_CAST",
"T_STRING_CAST",
"T_ARRAY_CAST",
"T_OBJECT_CAST",
"T_BOOL_CAST",
"T_UNSET_CAST",
"'@'",
"T_POW",
"'['",
"T_NEW",
"T_CLONE",
"T_EXIT",
"T_IF",
"T_ELSEIF",
"T_ELSE",
"T_ENDIF",
"T_LNUMBER",
"T_DNUMBER",
"T_STRING",
"T_STRING_VARNAME",
"T_VARIABLE",
"T_NUM_STRING",
"T_INLINE_HTML",
"T_ENCAPSED_AND_WHITESPACE",
"T_CONSTANT_ENCAPSED_STRING",
"T_ECHO",
"T_DO",
"T_WHILE",
"T_ENDWHILE",
"T_FOR",
"T_ENDFOR",
"T_FOREACH",
"T_ENDFOREACH",
"T_DECLARE",
"T_ENDDECLARE",
"T_AS",
"T_SWITCH",
"T_MATCH",
"T_ENDSWITCH",
"T_CASE",
"T_DEFAULT",
"T_BREAK",
"T_CONTINUE",
"T_GOTO",
"T_FUNCTION",
"T_FN",
"T_CONST",
"T_RETURN",
"T_TRY",
"T_CATCH",
"T_FINALLY",
"T_USE",
"T_INSTEADOF",
"T_GLOBAL",
"T_STATIC",
"T_ABSTRACT",
"T_FINAL",
"T_PRIVATE",
"T_PROTECTED",
"T_PUBLIC",
"T_READONLY",
"T_VAR",
"T_UNSET",
"T_ISSET",
"T_EMPTY",
"T_HALT_COMPILER",
"T_CLASS",
"T_TRAIT",
"T_INTERFACE",
"T_ENUM",
"T_EXTENDS",
"T_IMPLEMENTS",
"T_OBJECT_OPERATOR",
"T_NULLSAFE_OBJECT_OPERATOR",
"T_LIST",
"T_ARRAY",
"T_CALLABLE",
"T_CLASS_C",
"T_TRAIT_C",
"T_METHOD_C",
"T_FUNC_C",
"T_LINE",
"T_FILE",
"T_START_HEREDOC",
"T_END_HEREDOC",
"T_DOLLAR_OPEN_CURLY_BRACES",
"T_CURLY_OPEN",
"T_PAAMAYIM_NEKUDOTAYIM",
"T_NAMESPACE",
"T_NS_C",
"T_DIR",
"T_NS_SEPARATOR",
"T_ELLIPSIS",
"T_NAME_FULLY_QUALIFIED",
"T_NAME_QUALIFIED",
"T_NAME_RELATIVE",
"T_ATTRIBUTE",
"';'",
"']'",
"'('",
"')'",
"'{'",
"'}'",
"'`'",
"'\"'",
"'$'"
);
protected array $tokenToSymbol = array(
0, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 56, 166, 168, 167, 55, 168, 168,
161, 162, 53, 50, 8, 51, 52, 54, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 31, 159,
44, 16, 46, 30, 68, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 70, 168, 160, 36, 168, 165, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 163, 35, 164, 58, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 1, 2, 3, 4,
5, 6, 7, 9, 10, 11, 12, 13, 14, 15,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 32, 33, 34, 37, 38, 39, 40,
41, 42, 43, 45, 47, 48, 49, 57, 59, 60,
61, 62, 63, 64, 65, 66, 67, 69, 71, 72,
73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
153, 154, 155, 156, 157, 158
);
protected array $action = array(
133, 134, 135, 586, 136, 137, 0, 755, 756, 757,
138, 38,-32766,-32766,-32766, 151,-32766,-32766,-32766,-32767,
-32767,-32767,-32767, 102, 103, 104, 105, 106, 1116, 1117,
1118, 1115, 1114, 1113, 1119, 749, 748,-32766,-32766,-32766,
-32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,
-32767, 1252, 841,-32766, 1331, 758,-32766,-32766,-32766,-32766,
-599,-32766,-32766,-32766, 104, 105, 106, -599, 1315, 265,
139, 406, 762, 763, 764, 765, 994,-32766, 431,-32766,
-32766, -16,-32766, 242, 1031, 819, 766, 767, 768, 769,
770, 771, 772, 773, 774, 775, 795, 587, 796, 797,
798, 799, 787, 788, 347, 348, 790, 791, 776, 777,
778, 780, 781, 782, 358, 822, 823, 824, 825, 826,
588, 783, 784, 589, 590,-32766, 807, 805, 806, 818,
802, 803, 839, 830, 591, 592, 801, 593, 594, 595,
596, 597, 598, 830, 461, 462, 463, 1040, 804, 599,
600, 945, 140, 2, 133, 134, 135, 586, 136, 137,
1064, 755, 756, 757, 138, 38, -328, -110, -110, 1335,
291, 23, -110,-32766,-32766,-32766, 1334, 35, -110, 1116,
1117, 1118, 1115, 1114, 1113, 1119, 616,-32766, 129, 749,
748, 107, 108, 109,-32766, 275,-32766,-32766,-32766,-32766,
-32766,-32766,-32766, 832, 995, -194, 145, 110, 300, 758,
840, 75,-32766,-32766,-32766, 1360, 142, 328, 1361, -599,
328, -599, 253, 265, 139, 406, 762, 763, 764, 765,
82, -272, 431,-32766, 328,-32766,-32766,-32766,-32766, 819,
766, 767, 768, 769, 770, 771, 772, 773, 774, 775,
795, 587, 796, 797, 798, 799, 787, 788, 347, 348,
790, 791, 776, 777, 778, 780, 781, 782, 358, 822,
823, 824, 825, 826, 588, 783, 784, 589, 590, 834,
807, 805, 806, 818, 802, 803, 716, 311, 591, 592,
801, 593, 594, 595, 596, 597, 598, -78, 83, 84,
85, -85, 804, 599, 600, 313, 149, 779, 750, 751,
752, 753, 754, 729, 755, 756, 757, 792, 793, 37,
-328, 86, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 325, 275, 485,-32766,-32766,
-32766, -58,-32766,-32766,-32766, 963, 964, 127, 110, -194,
965, 341, 758,-32766,-32766,-32766, 959, -85, 292,-32766,
1092,-32766,-32766,-32766,-32766,-32766, 759, 760, 761, 762,
763, 764, 765, -193,-32766, 828,-32766,-32766,-32766, -367,
431, -367, 819, 766, 767, 768, 769, 770, 771, 772,
773, 774, 775, 795, 817, 796, 797, 798, 799, 787,
788, 789, 816, 790, 791, 776, 777, 778, 780, 781,
782, 821, 822, 823, 824, 825, 826, 827, 783, 784,
785, 786, -552, 807, 805, 806, 818, 802, 803, 342,
329, 794, 800, 801, 808, 809, 811, 810, 812, 813,
1037, 866, 610, 867,-32766, 804, 815, 814, 50, 51,
52, 516, 53, 54, 835, 1247, 1246, 1248, 55, 56,
-110, 57, 1040, 924, 1094, -110, 1040, -110, 292, 486,
749, 748, 307, 384, 383, -110, -110, -110, -110, -110,
-110, -110, -110, 425, 924, 284, -552, -552, 372, 291,
838, 924, 1252, 719, 470, 471, 58, 59,-32766,-32766,
21, -550, 60, 560, 61, 247, 248, 62, 63, 64,
65, 66, 67, 68, 69, -552, 28, 267, 70, 446,
517, 720, 1108, -342, 1279, 1280, 518, -193, 839, 376,
836, -548, 1277, 42, 25, 519, 391, 520, 241, 521,
924, 522, 947, 1245, 523, 524, 914, 660, 26, 44,
45, 447, 379, 378,-32766, 46, 525, 1027, 1026, 1025,
1028, 370, 340, 442, 1285, -550, -550, 914, 1238, 947,
527, 528, 529, 839, 914, 839, 1040, 443, 1350, 1243,
-550, 359, 531, 532, 444, 1266, 1267, 1268, 1269, 1263,
1264, 299, -556, 445, -550, -548, -548, 1270, 1265, 291,
1039, 1247, 1246, 1248, 300, 749, 748, 71, 364, 845,
-548, 323, 324, 328, -153, -153, -153, 152, 1247, 1246,
1248, 926, -555, 914, -548, 714, 1063, 154,-32766, -153,
1093, -153, 155, -153, 741, -153, 156, -596, 28, 268,
36, 250, 926,-32766, -596, 377, 714, 679, 680, 926,
839, 1273, 75, 714, 1277, 288, 963, 964, 328, -547,
393, 526, 7, 1037, -57, 1040, 900, 959, -110, -110,
-110, 32, 111, 112, 113, 114, 115, 116, 117, 118,
119, 120, 121, 122, 123, 1040, 158, 382, 383, 866,
1238, 867, 924, 749, 748, 1252, 33, 425, 926, 150,
409, 924, 714, -153, 531, 532, -87, 1266, 1267, 1268,
1269, 1263, 1264, 124, 1154, 1156, -84, -4, 924, 1270,
1265, 125, 721, -547, -547, -546, 130, 749, 748, 73,
-32766, 724, 839, -78, 324, 328, 1245, 131, -547, 300,
-590, 1037, -590,-32766,-32766,-32766, 144,-32766, 159,-32766,
-554,-32766, -547, 160,-32766, 380, 381, 924, 161,-32766,
-32766,-32766, 162, 1040,-32766,-32766,-32766, 385, 386, 163,
1245,-32766, 422, 651, 652, 914, 839,-32766,-32766,-32766,
-32766,-32766, -73,-32766, 914,-32766, 284, 731,-32766, -546,
-546, -72, 48,-32766,-32766,-32766, -596, -71, -596,-32766,
-32766, 914, -70, -69, -546,-32766, 422, -68, -67, -66,
74, -110, -110, 141,-32766, -50, -110, 328, -546, -65,
-46, -18, -110, 377, 148, 438, 274, 285, 730, 733,
298,-32766, 923, 147, 963, 964, 289, 290, -549, 526,
914, -302, -298, 280, 530, 959, -110, -110, -110, 132,
980, 281, 300, 941, 714, 75, 301, 302,-32766, 926,
286, 328, 287, 714, 1245, 334, 293, 10, 294, 275,
1362,-32766,-32766,-32766, 110,-32766, 926,-32766, 707,-32766,
714, -4,-32766, 146, 830, 126, 689,-32766,-32766,-32766,
705, 20,-32766,-32766,-32766, 924, 839, 682, 1245,-32766,
422, 1123, -549, -549, 649,-32766,-32766,-32766,-32766,-32766,
565,-32766, 661,-32766, 467, 926,-32766, -549,-32766, 714,
666,-32766,-32766,-32766,-32766, 496, 667,-32766,-32766,-32766,
1245, -549, 683,-32766, 422, 924, 571,-32766,-32766,-32766,
838,-32766,-32766,-32766, 306,-32766, 735, 1278,-32766, 308,
0, 960, 491,-32766,-32766,-32766,-32766, 0, 0,-32766,
-32766, 0, 1245, 578, 0,-32766, 422, -546, 305,-32766,
-32766,-32766, 312,-32766,-32766,-32766, 0,-32766, 914, 40,
-32766, 0, 0, 1284, 1286,-32766,-32766,-32766, -511, 0,
-501,-32766,-32766, 8, -250, -250, -250,-32766, 422, 614,
377, 24, 49, 28, 267, 374,-32766, 943, 41, 300,
-275, 963, 964, 738, 739, 839, 526, 858, 914, 1277,
905, 900, 959, -110, -110, -110, 1004, 981, 988, 978,
989, -546, -546, 903, -249, -249, -249, 976, 28, 268,
377, 1274, 288, 1097, 1100, 1101, -546, 1098, 1099, 1105,
839, 963, 964, 926, 1277, 1238, 526, 714, -250, 850,
-546, 900, 959, -110, -110, -110, 303, 304, 1301, 1319,
532, 1353, 1266, 1267, 1268, 1269, 1263, 1264, 654, -273,
-584, 375, -583, -582, 1270, 1265, -556, -555, -554, -553,
1238, -495, 694, 926, 73, 128, 1, 714, -249, 324,
328, 29, 30, 39, 43, 532, 47, 1266, 1267, 1268,
1269, 1263, 1264, 72, 76, 77, 78, 79, 80, 1270,
1265, 81, 143, 153,-32766, 157, 245, 330, 695, 73,
1245, 359, 360, 361, 324, 328, 362,-32766,-32766,-32766,
363,-32766, 364,-32766, 365,-32766, 366, 367,-32766, 696,
697, 368, 369,-32766,-32766,-32766, 371, 439, 559,-32766,
-32766, -272, 13, 14, 15,-32766, 422, 1247, 1246, 1248,
16, 18, 408, 284,-32766, 487, 488, 495, 498, 499,
500, 501, 505, 506, 507, 514, 576, 700, 1256, 1194,
1275, 1066, 1065, 1046, 1233, 1042, -277, -102, 12, 17,
27, 297, 407, 607, 611, 640, 706, 1198, 1251, 1195,
1332, 0, 34, 0, 322, 373, 715, 718, 722, 723,
725, 726, 727, 728, 732, 717, 0, 901, 1357, 1359,
861, 860, 869, 953, 996, 868, 1358, 952, 950, 951,
954, 1226, 934, 944, 932, 986, 987, 638, 1356, 1313,
1302, 1320, 1329, 0, 1211, 0, 0, 328
);
protected array $actionCheck = array(
2, 3, 4, 5, 6, 7, 0, 9, 10, 11,
12, 13, 9, 10, 11, 14, 9, 10, 11, 44,
45, 46, 47, 48, 49, 50, 51, 52, 116, 117,
118, 119, 120, 121, 122, 37, 38, 30, 116, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 1, 1, 9, 1, 57, 9, 10, 11, 137,
1, 9, 10, 11, 50, 51, 52, 8, 1, 71,
72, 73, 74, 75, 76, 77, 31, 30, 80, 32,
33, 31, 30, 14, 1, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
122, 123, 124, 125, 126, 116, 128, 129, 130, 131,
132, 133, 82, 80, 136, 137, 138, 139, 140, 141,
142, 143, 144, 80, 129, 130, 131, 138, 150, 151,
152, 1, 154, 8, 2, 3, 4, 5, 6, 7,
162, 9, 10, 11, 12, 13, 8, 117, 118, 1,
161, 8, 122, 9, 10, 11, 8, 8, 128, 116,
117, 118, 119, 120, 121, 122, 51, 137, 8, 37,
38, 53, 54, 55, 30, 57, 32, 33, 34, 35,
36, 37, 38, 80, 159, 8, 8, 69, 158, 57,
159, 161, 9, 10, 11, 80, 163, 167, 83, 160,
167, 162, 8, 71, 72, 73, 74, 75, 76, 77,
163, 162, 80, 30, 167, 32, 33, 34, 35, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 156,
128, 129, 130, 131, 132, 133, 163, 8, 136, 137,
138, 139, 140, 141, 142, 143, 144, 16, 9, 10,
11, 31, 150, 151, 152, 8, 154, 2, 3, 4,
5, 6, 7, 163, 9, 10, 11, 12, 13, 30,
162, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 8, 57, 31, 9, 10,
11, 16, 9, 10, 11, 117, 118, 14, 69, 162,
122, 8, 57, 9, 10, 11, 128, 97, 30, 30,
1, 32, 33, 34, 35, 36, 71, 72, 73, 74,
75, 76, 77, 8, 30, 80, 32, 33, 34, 106,
80, 108, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
125, 126, 70, 128, 129, 130, 131, 132, 133, 8,
70, 136, 137, 138, 139, 140, 141, 142, 143, 144,
116, 106, 1, 108, 116, 150, 151, 152, 2, 3,
4, 5, 6, 7, 80, 155, 156, 157, 12, 13,
101, 15, 138, 1, 164, 106, 138, 108, 30, 163,
37, 38, 113, 106, 107, 116, 117, 118, 119, 120,
121, 122, 123, 116, 1, 161, 134, 135, 8, 161,
155, 1, 1, 31, 134, 135, 50, 51, 9, 10,
101, 70, 56, 85, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 163, 70, 71, 72, 73,
74, 31, 123, 164, 78, 79, 80, 162, 82, 8,
156, 70, 86, 87, 88, 89, 8, 91, 97, 93,
1, 95, 122, 80, 98, 99, 84, 75, 76, 103,
104, 105, 106, 107, 116, 109, 110, 119, 120, 121,
122, 115, 116, 8, 146, 134, 135, 84, 122, 122,
124, 125, 126, 82, 84, 82, 138, 8, 85, 116,
149, 161, 136, 137, 8, 139, 140, 141, 142, 143,
144, 145, 161, 8, 163, 134, 135, 151, 152, 161,
137, 155, 156, 157, 158, 37, 38, 161, 161, 8,
149, 165, 166, 167, 75, 76, 77, 14, 155, 156,
157, 159, 161, 84, 163, 163, 1, 14, 137, 90,
159, 92, 14, 94, 163, 96, 14, 1, 70, 71,
147, 148, 159, 116, 8, 106, 163, 75, 76, 159,
82, 1, 161, 163, 86, 30, 117, 118, 167, 70,
106, 122, 108, 116, 16, 138, 127, 128, 129, 130,
131, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 138, 14, 106, 107, 106,
122, 108, 1, 37, 38, 1, 14, 116, 159, 101,
102, 1, 163, 164, 136, 137, 31, 139, 140, 141,
142, 143, 144, 16, 59, 60, 31, 0, 1, 151,
152, 16, 31, 134, 135, 70, 16, 37, 38, 161,
74, 31, 82, 31, 166, 167, 80, 16, 149, 158,
160, 116, 162, 87, 88, 89, 16, 91, 16, 93,
161, 95, 163, 16, 98, 106, 107, 1, 16, 103,
104, 105, 16, 138, 74, 109, 110, 106, 107, 16,
80, 115, 116, 111, 112, 84, 82, 87, 88, 89,
124, 91, 31, 93, 84, 95, 161, 31, 98, 134,
135, 31, 70, 103, 104, 105, 160, 31, 162, 109,
110, 84, 31, 31, 149, 115, 116, 31, 31, 31,
154, 117, 118, 163, 124, 31, 122, 167, 163, 31,
31, 31, 128, 106, 31, 108, 31, 31, 31, 31,
113, 137, 31, 31, 117, 118, 37, 37, 70, 122,
84, 35, 35, 35, 127, 128, 129, 130, 131, 31,
159, 35, 158, 38, 163, 161, 134, 135, 74, 159,
35, 167, 35, 163, 80, 35, 37, 150, 37, 57,
83, 87, 88, 89, 69, 91, 159, 93, 92, 95,
163, 164, 98, 70, 80, 163, 77, 103, 104, 105,
80, 97, 74, 109, 110, 1, 82, 94, 80, 115,
116, 82, 134, 135, 113, 87, 88, 89, 124, 91,
89, 93, 90, 95, 97, 159, 98, 149, 85, 163,
96, 103, 104, 105, 74, 97, 100, 109, 110, 137,
80, 163, 100, 115, 116, 1, 153, 87, 88, 89,
155, 91, 124, 93, 133, 95, 164, 166, 98, 114,
-1, 128, 102, 103, 104, 105, 74, -1, -1, 109,
110, -1, 80, 81, -1, 115, 116, 70, 132, 87,
88, 89, 132, 91, 124, 93, -1, 95, 84, 159,
98, -1, -1, 146, 146, 103, 104, 105, 149, -1,
149, 109, 110, 149, 100, 101, 102, 115, 116, 153,
106, 149, 70, 70, 71, 149, 124, 154, 159, 158,
162, 117, 118, 159, 159, 82, 122, 159, 84, 86,
159, 127, 128, 129, 130, 131, 159, 159, 159, 159,
159, 134, 135, 159, 100, 101, 102, 159, 70, 71,
106, 160, 30, 159, 159, 159, 149, 159, 159, 159,
82, 117, 118, 159, 86, 122, 122, 163, 164, 160,
163, 127, 128, 129, 130, 131, 134, 135, 160, 160,
137, 160, 139, 140, 141, 142, 143, 144, 160, 162,
161, 149, 161, 161, 151, 152, 161, 161, 161, 161,
122, 161, 80, 159, 161, 163, 161, 163, 164, 166,
167, 161, 161, 161, 161, 137, 161, 139, 140, 141,
142, 143, 144, 161, 161, 161, 161, 161, 161, 151,
152, 161, 161, 161, 74, 161, 161, 161, 116, 161,
80, 161, 161, 161, 166, 167, 161, 87, 88, 89,
161, 91, 161, 93, 161, 95, 161, 161, 98, 137,
138, 161, 161, 103, 104, 105, 161, 161, 161, 109,
110, 162, 162, 162, 162, 115, 116, 155, 156, 157,
162, 162, 162, 161, 124, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, -1, 163, -1, 163, 163, 163, 163, 163, 163,
163, 163, 163, 163, 163, 163, -1, 164, 164, 164,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
164, 164, 164, -1, 165, -1, -1, 167
);
protected array $actionBase = array(
0, -2, 152, 549, 727, 904, 944, 1022, 660, 310,
123, 899, 500, 710, 710, 766, 710, 472, 701, 820,
63, 305, 305, 820, 305, 493, 493, 493, 666, 666,
666, 666, 700, 700, 860, 860, 892, 828, 794, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 51, 45, 451, 692, 1049, 1055,
1051, 1056, 1047, 1046, 1050, 1052, 1057, 1094, 1095, 812,
1096, 1097, 1093, 1098, 1053, 928, 1048, 1054, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 44, 343, 499, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 52, 52,
52, 578, 578, 47, 354, 978, 943, 978, 978, 978,
978, 978, 978, 978, 978, 203, 665, 339, 164, 164,
7, 7, 7, 7, 7, 50, 369, 704, 704, -25,
-25, -25, -25, 448, 635, 501, 409, 283, 338, 591,
334, 334, 14, 14, 557, 557, 9, 9, 557, 557,
557, 537, 537, 537, 537, 441, 471, 599, 345, 428,
802, 53, 53, 53, 53, 802, 802, 802, 802, 848,
791, 802, 802, 802, 778, 907, 907, 942, 138, 138,
138, 907, 593, 503, 503, 593, 238, 503, 67, 135,
-78, 833, 377, 590, -78, 362, 732, 646, 59, 795,
659, 795, 1045, 430, 843, 843, 457, 799, 761, 900,
1072, 1058, 836, 1091, 842, 1092, 15, 370, 712, 1044,
1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044, 1044,
1100, 443, 1045, 384, 1100, 1100, 1100, 443, 443, 443,
443, 443, 443, 443, 443, 443, 443, 672, 384, 482,
582, 384, 840, 443, 51, 851, 51, 51, 51, 51,
51, 51, 51, 51, 51, 51, 800, 316, 51, 45,
150, 150, 481, 83, 150, 150, 150, 150, 51, 51,
51, 51, 659, 822, 793, 671, 856, 375, 822, 822,
822, 270, 158, 69, 197, 816, 817, 564, 814, 814,
829, 945, 814, 824, 814, 829, 955, 814, 814, 945,
945, 861, 945, 180, 565, 353, 531, 579, 945, 279,
814, 814, 814, 814, 850, 945, 586, 814, 214, 198,
814, 814, 850, 846, 806, 145, 821, 945, 945, 945,
850, 490, 821, 821, 821, 864, 865, 801, 805, 337,
297, 611, 169, 825, 805, 805, 814, 538, 801, 805,
801, 805, 863, 805, 805, 805, 801, 805, 824, 431,
805, 742, 595, 163, 805, 6, 962, 963, 685, 964,
952, 965, 1006, 966, 967, 1063, 940, 975, 953, 970,
1007, 951, 950, 811, 707, 715, 854, 849, 938, 815,
815, 815, 935, 936, 815, 815, 815, 815, 815, 815,
815, 815, 707, 891, 866, 831, 981, 720, 731, 1034,
847, 1073, 1099, 980, 1036, 971, 830, 740, 1019, 982,
792, 1061, 985, 989, 1020, 1037, 868, 1038, 1074, 823,
1075, 1076, 909, 993, 1064, 815, 962, 967, 695, 953,
970, 951, 950, 798, 788, 786, 787, 782, 781, 770,
776, 803, 1039, 932, 929, 918, 991, 937, 707, 919,
1010, 1059, 1023, 1024, 1062, 827, 797, 921, 1077, 995,
996, 1000, 1065, 1040, 1066, 859, 1011, 858, 1025, 838,
1078, 1026, 1027, 1028, 1029, 1067, 1079, 1068, 931, 1069,
871, 832, 927, 834, 1080, 1, 835, 837, 841, 1005,
613, 976, 1070, 1081, 1082, 1030, 1031, 1032, 1083, 1084,
972, 877, 1012, 813, 1018, 1009, 878, 879, 623, 839,
1041, 818, 826, 810, 628, 632, 1085, 1086, 1087, 974,
807, 819, 880, 881, 1042, 809, 1043, 1088, 682, 884,
747, 1089, 1035, 752, 756, 281, 658, 335, 763, 796,
1071, 862, 845, 804, 1001, 756, 808, 888, 1090, 894,
895, 896, 1033, 898, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 456, 456, 456, 456, 456, 456,
305, 305, 305, 305, 305, 456, 456, 456, 456, 456,
456, 456, 305, 305, 0, 0, 305, 0, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 473, 473, 289, 289, 473, 473,
473, 473, 473, 473, 473, 473, 473, 473, 289, 0,
289, 289, 289, 289, 289, 289, 289, 289, 473, 861,
473, 473, 138, 138, 138, 138, 473, 473, 473, -88,
-88, 473, 238, 473, 473, 138, 138, 473, 473, 473,
473, 473, 473, 473, 473, 473, 473, 473, 0, 0,
0, 384, 503, 473, 824, 824, 824, 824, 473, 473,
473, 473, 503, 503, 473, 473, 473, 0, 0, 0,
0, 0, 0, 0, 0, 384, 0, 0, 384, 0,
0, 824, 824, 473, 238, 861, 168, 473, 0, 0,
0, 0, 384, 824, 384, 443, 814, 503, 503, 814,
443, 443, 150, 51, 168, 608, 608, 608, 608, 0,
0, 659, 861, 861, 861, 861, 861, 861, 861, 861,
861, 861, 861, 824, 0, 861, 0, 824, 824, 824,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 824, 0, 0, 945, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 955,
0, 0, 0, 0, 0, 0, 824, 0, 0, 0,
0, 0, 0, 0, 0, 0, 815, 827, 0, 827,
0, 815, 815, 815, 0, 0, 0, 0, 839, 809
);
protected array $actionDefault = array(
3,32767, 102,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767, 100,32767,32767,32767,32767, 602, 602,
602, 602,32767,32767, 254, 102,32767,32767, 470, 387,
387, 387,32767,32767, 544, 544, 544, 544, 544, 544,
32767,32767,32767,32767,32767,32767, 470,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767, 100,
32767,32767,32767, 36, 7, 8, 10, 11, 49, 17,
324,32767,32767,32767,32767, 102,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767, 595,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767, 474, 453,
454, 456, 457, 386, 545, 601, 327, 598, 385, 145,
339, 329, 242, 330, 258, 475, 259, 476, 479, 480,
215, 287, 382, 149, 150, 417, 471, 419, 469, 473,
418, 392, 398, 399, 400, 401, 402, 403, 404, 405,
406, 407, 408, 409, 410, 390, 391, 472, 450, 449,
448,32767,32767, 415, 416,32767,32767,32767,32767,32767,
32767,32767,32767, 102,32767, 420, 389, 423, 421, 422,
439, 440, 437, 438, 441,32767,32767,32767,32767, 442,
443, 444, 445, 316,32767,32767, 366, 364, 316, 111,
32767,32767, 430, 431,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767, 487, 538, 447,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767, 102,32767, 100, 540, 412, 414, 507, 425, 426,
424, 393,32767, 514,32767, 102,32767, 516,32767,32767,
32767,32767,32767,32767,32767, 539,32767, 546, 546,32767,
500, 100, 195,32767,32767, 515,32767, 195, 195,32767,
32767,32767,32767,32767,32767,32767,32767, 609, 500, 110,
110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
32767, 195, 110,32767,32767,32767, 100, 195, 195, 195,
195, 195, 195, 195, 195, 195, 195, 190,32767, 268,
270, 102, 563, 195,32767, 519,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767, 512,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767, 500, 435, 138,32767, 138, 546, 427, 428,
429, 502, 546, 546, 546, 312, 289,32767,32767,32767,
32767, 517, 100, 100, 100, 100, 512,32767,32767,32767,
32767, 111, 486, 99, 99, 99, 99, 99, 103, 101,
32767,32767,32767,32767, 223,32767, 99,32767, 101, 101,
32767,32767, 223, 225, 212, 101, 227,32767, 567, 568,
223, 101, 227, 227, 227, 247, 247, 489, 318, 101,
99, 101, 101, 197, 318, 318,32767, 101, 489, 318,
489, 318, 199, 318, 318, 318, 489, 318,32767, 101,
318, 214, 99, 99, 318,32767,32767,32767, 502,32767,
32767,32767,32767,32767,32767,32767, 222,32767,32767,32767,
32767,32767,32767,32767,32767, 533,32767, 551, 565, 433,
434, 436, 550, 548, 458, 459, 460, 461, 462, 463,
464, 466, 597,32767, 506,32767,32767,32767, 338,32767,
607,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767, 608,32767,
546,32767,32767,32767,32767, 432, 9, 74, 495, 42,
43, 51, 57, 523, 524, 525, 526, 520, 521, 527,
522,32767,32767, 528, 573,32767,32767, 547, 600,32767,
32767,32767,32767,32767,32767, 138,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767, 533,32767, 136,
32767,32767,32767,32767,32767,32767,32767,32767, 529,32767,
32767,32767, 546,32767,32767,32767,32767, 314, 311,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767, 546,32767,32767,32767,32767,
32767, 291,32767, 308,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
286,32767,32767, 381, 502, 294, 296, 297,32767,32767,
32767,32767, 360,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767, 152, 152, 3, 3, 341, 152,
152, 152, 341, 341, 152, 341, 341, 341, 152, 152,
152, 152, 152, 152, 280, 185, 262, 265, 247, 247,
152, 352, 152
);
protected array $goto = array(
196, 196, 1038, 1069, 701, 353, 433, 665, 856, 710,
427, 321, 315, 316, 337, 580, 432, 338, 434, 642,
658, 659, 857, 676, 677, 678, 979, 167, 167, 167,
167, 221, 197, 193, 193, 177, 179, 216, 193, 193,
193, 193, 193, 194, 194, 194, 194, 194, 194, 188,
189, 190, 191, 192, 218, 216, 219, 539, 540, 423,
541, 544, 545, 546, 547, 548, 549, 550, 551, 1140,
168, 169, 170, 195, 171, 172, 173, 166, 174, 175,
176, 178, 215, 217, 220, 238, 243, 244, 255, 257,
258, 259, 260, 261, 262, 263, 264, 269, 270, 271,
272, 282, 283, 318, 319, 320, 428, 429, 430, 585,
222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 180, 237, 181, 198, 199,
200, 239, 188, 189, 190, 191, 192, 218, 1140, 201,
182, 183, 184, 202, 198, 185, 240, 203, 201, 165,
204, 205, 186, 206, 207, 208, 187, 209, 210, 211,
212, 213, 214, 859, 421, 1041, 1041, 625, 662, 685,
956, 251, 251, 1033, 1049, 1050, 279, 279, 279, 279,
344, 831, 852, 627, 627, 890, 604, 1276, 1276, 1276,
1276, 1276, 1276, 1276, 1276, 1276, 1276, 351, 249, 249,
249, 249, 246, 252, 345, 344, 577, 864, 460, 913,
908, 909, 922, 865, 910, 862, 911, 912, 863, 469,
469, 916, 897, 855, 897, 897, 357, 917, 469, 918,
1336, 1091, 1086, 1087, 1088, 852, 357, 357, 613, 628,
631, 632, 633, 634, 655, 656, 657, 712, 396, 698,
357, 357, 833, 1000, 357, 441, 1363, 354, 355, 872,
1244, 698, 1244, 1244, 426, 698, 615, 558, 1038, 1038,
1244, 357, 357, 1038, 884, 1038, 1038, 871, 575, 1038,
1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038,
1328, 1328, 1328, 1328, 1137, 1244, 356, 356, 356, 356,
1244, 1244, 1244, 1244, 1111, 1112, 1244, 1244, 1244, 1220,
948, 563, 556, 1221, 1224, 949, 1225, 1062, 554, 1307,
554, 554, 482, 603, 1104, 930, 713, 465, 554, 931,
484, 5, 946, 6, 1189, 946, 511, 704, 664, 1102,
690, 343, 556, 563, 572, 573, 346, 583, 606, 620,
621, 1044, 1043, 458, 852, 1047, 1048, 22, 973, 973,
973, 973, 327, 310, 458, 967, 974, 1295, 1295, 440,
558, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295,
1295, 1292, 1292, 837, 686, 1292, 1292, 1292, 1292, 1292,
1292, 1292, 1292, 1292, 1292, 543, 543, 1323, 1324, 543,
543, 543, 543, 543, 543, 543, 543, 543, 543, 542,
542, 254, 254, 542, 670, 542, 542, 542, 542, 542,
542, 542, 542, 339, 837, 962, 837, 557, 567, 581,
618, 557, 849, 567, 877, 1237, 399, 464, 451, 451,
451, 451, 405, 1318, 619, 1318, 1318, 1239, 874, 472,
584, 473, 474, 1318, 1235, 1075, 882, 570, 1022, 1354,
1355, 737, 641, 643, 740, 1079, 663, 479, 1321, 1322,
687, 691, 1014, 699, 708, 1010, 503, 886, 504, 1330,
1330, 1330, 1330, 1122, 510, 880, 984, 410, 411, 0,
1346, 1346, 674, 1261, 675, 0, 414, 415, 416, 0,
688, 1240, 1241, 417, 0, 0, 1314, 349, 1346, 0,
847, 885, 873, 1074, 1078, 552, 552, 552, 552, 0,
608, 0, 0, 982, 0, 1349, 1349, 0, 0, 1242,
1304, 1305, 451, 451, 451, 451, 451, 451, 451, 451,
451, 451, 451, 935, 1127, 451, 0, 972, 1077, 0,
623, 0, 1316, 1316, 1077, 0, 1019, 0, 326, 276,
326, 326, 0, 0, 876, 0, 668, 998, 435, 1120,
889, 0, 870, 435, 398, 401, 564, 605, 609, 0,
1003, 1045, 1045, 975, 1234, 736, 669, 1056, 1052, 1053,
971, 412, 709, 555, 1012, 1007, 635, 637, 639, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1017, 1017
);
protected array $gotoCheck = array(
42, 42, 73, 127, 73, 97, 66, 66, 26, 9,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
86, 86, 27, 86, 86, 86, 49, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 15, 43, 89, 89, 56, 56, 89,
89, 5, 5, 89, 89, 89, 23, 23, 23, 23,
170, 6, 22, 108, 108, 45, 130, 108, 108, 108,
108, 108, 108, 108, 108, 108, 108, 181, 5, 5,
5, 5, 5, 5, 170, 170, 174, 15, 83, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 149,
149, 15, 25, 25, 25, 25, 14, 65, 149, 65,
183, 15, 15, 15, 15, 22, 14, 14, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81, 62, 7,
14, 14, 7, 103, 14, 83, 14, 97, 97, 35,
73, 7, 73, 73, 13, 7, 13, 14, 73, 73,
73, 14, 14, 73, 35, 73, 73, 35, 104, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
9, 9, 9, 9, 150, 73, 24, 24, 24, 24,
73, 73, 73, 73, 144, 144, 73, 73, 73, 79,
79, 76, 76, 79, 79, 79, 79, 114, 19, 14,
19, 19, 84, 8, 8, 73, 8, 151, 19, 73,
84, 46, 9, 46, 151, 9, 8, 8, 64, 8,
14, 76, 76, 76, 76, 76, 76, 76, 76, 76,
76, 118, 118, 19, 22, 119, 119, 76, 19, 19,
19, 19, 171, 171, 19, 19, 19, 172, 172, 113,
14, 172, 172, 172, 172, 172, 172, 172, 172, 172,
172, 173, 173, 12, 116, 173, 173, 173, 173, 173,
173, 173, 173, 173, 173, 175, 175, 180, 180, 175,
175, 175, 175, 175, 175, 175, 175, 175, 175, 158,
158, 5, 5, 158, 120, 158, 158, 158, 158, 158,
158, 158, 158, 29, 12, 92, 12, 9, 9, 2,
2, 9, 18, 9, 39, 14, 9, 9, 23, 23,
23, 23, 28, 130, 80, 130, 130, 20, 37, 9,
9, 9, 9, 130, 162, 129, 9, 48, 110, 9,
9, 48, 48, 48, 99, 132, 48, 178, 178, 178,
48, 48, 48, 48, 48, 48, 155, 41, 155, 130,
130, 130, 130, 147, 155, 9, 96, 82, 82, -1,
184, 184, 82, 20, 82, -1, 82, 82, 82, -1,
82, 20, 20, 82, -1, -1, 130, 82, 184, -1,
20, 16, 16, 16, 16, 107, 107, 107, 107, -1,
107, -1, -1, 16, -1, 184, 184, -1, -1, 20,
20, 20, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 17, 17, 23, -1, 16, 130, -1,
17, -1, 130, 130, 130, -1, 17, -1, 24, 24,
24, 24, -1, -1, 17, -1, 17, 17, 117, 16,
16, -1, 17, 117, 59, 59, 59, 59, 59, -1,
50, 117, 117, 50, 17, 50, 117, 117, 117, 117,
93, 93, 93, 50, 50, 50, 85, 85, 85, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, 107, 107
);
protected array $gotoBase = array(
0, 0, -287, 0, 0, 170, 161, 242, 315, -11,
0, 0, 85, -75, -73, -187, 57, 75, 121, 53,
52, 0, -97, 173, 293, 219, 4, 18, 103, 125,
0, 0, 0, 0, 0, -114, 0, 107, 0, 109,
0, 35, -1, 145, 0, 162, -409, 0, -258, 8,
568, 0, 0, 0, 0, 0, 127, 0, 0, 529,
0, 0, 206, 0, 96, 213, -235, 0, 0, 0,
0, 0, 0, -5, 0, 0, -36, 0, 0, -101,
98, -122, -7, -71, -150, 114, -702, 0, 0, -115,
0, 0, 94, 284, 0, 0, 42, -481, 0, 55,
0, 0, 0, 218, 235, 0, 0, 487, -58, 0,
86, 0, 0, 91, 43, 0, 100, 295, 71, 69,
123, 0, 0, 0, 0, 0, 0, 1, 0, 79,
178, 0, 22, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 28, 0, 0, 38, 0, 185,
48, 59, 0, 0, 0, -22, 0, 0, 168, 0,
0, 0, 83, 0, 0, 0, 0, 0, 0, 0,
-119, 39, 126, 140, 177, 154, 0, 0, 165, 0,
23, 167, 0, 199, 181, 0, 0
);
protected array $gotoDefault = array(
-32768, 515, 744, 4, 745, 939, 820, 829, 601, 533,
711, 350, 629, 424, 1312, 915, 1126, 582, 848, 1253,
1227, 459, 851, 332, 734, 927, 898, 899, 402, 388,
394, 400, 653, 630, 497, 883, 455, 875, 489, 878,
454, 887, 164, 420, 513, 891, 3, 894, 561, 925,
977, 389, 902, 390, 681, 904, 566, 906, 907, 397,
403, 404, 1131, 574, 626, 919, 256, 568, 920, 387,
921, 929, 392, 395, 692, 468, 508, 502, 413, 1106,
569, 612, 650, 448, 476, 624, 636, 622, 483, 436,
418, 331, 961, 969, 490, 466, 983, 352, 991, 742,
1139, 644, 492, 999, 645, 1006, 1009, 534, 535, 481,
1021, 273, 1024, 493, 19, 671, 1035, 1036, 672, 646,
1058, 647, 673, 648, 1060, 475, 602, 1068, 456, 1076,
1300, 457, 1080, 266, 1083, 278, 419, 437, 1089, 1090,
9, 1096, 702, 703, 11, 277, 512, 1121, 693, 453,
1138, 452, 1208, 1210, 562, 494, 1228, 480, 295, 1231,
684, 509, 1236, 449, 1303, 450, 536, 477, 317, 537,
1347, 309, 335, 314, 553, 296, 336, 538, 478, 1309,
1317, 333, 31, 1337, 1348, 579, 617
);
protected array $ruleToNonTerminal = array(
0, 1, 3, 3, 2, 5, 5, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 7, 7, 7,
7, 7, 7, 7, 7, 8, 8, 9, 10, 11,
11, 11, 12, 12, 13, 13, 14, 15, 15, 16,
16, 17, 17, 18, 18, 21, 21, 22, 23, 23,
24, 24, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 29, 29, 30, 30, 32, 34, 34,
28, 36, 36, 33, 38, 38, 35, 35, 37, 37,
39, 39, 31, 40, 40, 41, 43, 44, 44, 45,
45, 46, 46, 48, 47, 47, 47, 47, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 25, 25, 50, 69, 69, 72, 72, 71,
70, 70, 63, 75, 75, 76, 76, 77, 77, 78,
78, 79, 79, 80, 80, 26, 26, 27, 27, 27,
27, 27, 88, 88, 90, 90, 83, 83, 91, 91,
92, 92, 92, 84, 84, 87, 87, 85, 85, 93,
94, 94, 57, 57, 65, 65, 68, 68, 68, 67,
95, 95, 96, 58, 58, 58, 58, 97, 97, 98,
98, 99, 99, 100, 101, 101, 102, 102, 103, 103,
55, 55, 51, 51, 105, 53, 53, 106, 52, 52,
54, 54, 64, 64, 64, 64, 81, 81, 109, 109,
111, 111, 112, 112, 112, 112, 110, 110, 110, 114,
114, 114, 114, 89, 89, 117, 117, 117, 118, 118,
115, 115, 119, 119, 121, 121, 122, 122, 116, 123,
123, 120, 124, 124, 124, 124, 113, 113, 82, 82,
82, 20, 20, 20, 126, 125, 125, 127, 127, 127,
127, 60, 128, 128, 129, 61, 131, 131, 132, 132,
133, 133, 86, 134, 134, 134, 134, 134, 134, 134,
139, 139, 140, 140, 141, 141, 141, 141, 141, 142,
143, 143, 138, 138, 135, 135, 137, 137, 145, 145,
144, 144, 144, 144, 144, 144, 144, 136, 146, 146,
148, 147, 147, 62, 104, 149, 149, 56, 56, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 156, 158, 158, 159, 150, 150, 155,
155, 160, 161, 161, 162, 163, 164, 164, 164, 164,
19, 19, 73, 73, 73, 73, 151, 151, 151, 151,
166, 166, 152, 152, 154, 154, 154, 157, 157, 172,
172, 172, 172, 172, 172, 172, 172, 172, 173, 173,
173, 108, 175, 175, 175, 175, 153, 153, 153, 153,
153, 153, 153, 153, 59, 59, 169, 169, 169, 169,
169, 176, 176, 165, 165, 165, 165, 177, 177, 177,
177, 177, 177, 74, 74, 66, 66, 66, 66, 130,
130, 130, 130, 180, 179, 168, 168, 168, 168, 168,
168, 168, 167, 167, 167, 178, 178, 178, 178, 107,
174, 182, 182, 181, 181, 183, 183, 183, 183, 183,
183, 183, 183, 171, 171, 171, 171, 170, 185, 184,
184, 184, 184, 184, 184, 184, 184, 186, 186, 186,
186
);
protected array $ruleToLength = array(
1, 1, 2, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 1, 1, 2, 1, 3, 4, 1, 2,
0, 1, 1, 1, 1, 4, 3, 5, 4, 3,
4, 2, 3, 1, 1, 7, 6, 2, 3, 1,
2, 3, 1, 2, 3, 1, 1, 3, 1, 3,
1, 2, 2, 3, 1, 3, 2, 3, 1, 3,
3, 2, 0, 1, 1, 1, 1, 1, 3, 7,
10, 5, 7, 9, 5, 3, 3, 3, 3, 3,
3, 1, 2, 5, 7, 9, 6, 5, 6, 3,
2, 1, 1, 1, 1, 0, 2, 1, 3, 8,
0, 4, 2, 1, 3, 0, 1, 0, 1, 0,
1, 3, 1, 1, 1, 8, 9, 7, 8, 7,
6, 8, 0, 2, 0, 2, 1, 2, 1, 2,
1, 1, 1, 0, 2, 0, 2, 0, 2, 2,
1, 3, 1, 4, 1, 4, 1, 1, 4, 2,
1, 3, 3, 3, 4, 4, 5, 0, 2, 4,
3, 1, 1, 7, 0, 2, 1, 3, 3, 4,
1, 4, 0, 2, 5, 0, 2, 6, 0, 2,
0, 3, 1, 2, 1, 1, 2, 0, 1, 3,
0, 2, 1, 1, 1, 1, 6, 8, 6, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 3,
3, 3, 1, 3, 3, 3, 3, 3, 1, 3,
3, 1, 1, 2, 1, 1, 0, 1, 0, 2,
2, 2, 4, 3, 1, 1, 3, 1, 2, 2,
3, 2, 3, 1, 1, 2, 3, 1, 1, 3,
2, 0, 1, 5, 5, 6, 10, 3, 5, 1,
1, 3, 0, 2, 4, 5, 4, 4, 4, 3,
1, 1, 1, 1, 1, 1, 0, 1, 1, 2,
1, 1, 1, 1, 1, 1, 1, 2, 1, 3,
1, 1, 3, 2, 2, 3, 1, 0, 1, 1,
3, 3, 3, 4, 4, 1, 1, 2, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 2, 2, 2, 2, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 2, 2, 2, 2, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 5, 4,
3, 4, 4, 2, 2, 4, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 1, 3, 2,
1, 2, 4, 2, 2, 8, 9, 8, 9, 9,
10, 9, 10, 8, 3, 2, 2, 1, 1, 0,
4, 2, 1, 3, 2, 1, 2, 2, 2, 4,
1, 1, 1, 1, 1, 1, 1, 1, 3, 1,
1, 1, 0, 3, 0, 1, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 3, 5,
3, 3, 4, 1, 1, 3, 1, 1, 1, 1,
1, 3, 2, 3, 0, 1, 1, 3, 1, 1,
1, 1, 1, 1, 3, 1, 1, 1, 4, 4,
1, 4, 4, 0, 1, 1, 1, 3, 3, 1,
4, 2, 2, 1, 3, 1, 4, 4, 3, 3,
3, 3, 1, 3, 1, 1, 3, 1, 1, 4,
1, 1, 1, 3, 1, 1, 2, 1, 3, 4,
3, 2, 0, 2, 2, 1, 2, 1, 1, 1,
4, 3, 3, 3, 3, 6, 3, 1, 1, 2,
1
);
protected function initReduceCallbacks(): void {
$this->reduceCallbacks = [
0 => null,
1 => static function ($self, $stackPos) {
$self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]);
},
2 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
},
3 => static function ($self, $stackPos) {
$self->semValue = array();
},
4 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
5 => null,
6 => null,
7 => null,
8 => null,
9 => null,
10 => null,
11 => null,
12 => null,
13 => null,
14 => null,
15 => null,
16 => null,
17 => null,
18 => null,
19 => null,
20 => null,
21 => null,
22 => null,
23 => null,
24 => null,
25 => null,
26 => null,
27 => null,
28 => null,
29 => null,
30 => null,
31 => null,
32 => null,
33 => null,
34 => null,
35 => null,
36 => null,
37 => null,
38 => null,
39 => null,
40 => null,
41 => null,
42 => null,
43 => null,
44 => null,
45 => null,
46 => null,
47 => null,
48 => null,
49 => null,
50 => null,
51 => null,
52 => null,
53 => null,
54 => null,
55 => null,
56 => null,
57 => null,
58 => null,
59 => null,
60 => null,
61 => null,
62 => null,
63 => null,
64 => null,
65 => null,
66 => null,
67 => null,
68 => null,
69 => null,
70 => null,
71 => null,
72 => null,
73 => null,
74 => null,
75 => null,
76 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
},
77 => null,
78 => null,
79 => null,
80 => null,
81 => null,
82 => null,
83 => null,
84 => null,
85 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
86 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
87 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
88 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
89 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
90 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
91 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
92 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
93 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
94 => null,
95 => static function ($self, $stackPos) {
$self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
96 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
97 => static function ($self, $stackPos) {
/* nothing */
},
98 => static function ($self, $stackPos) {
/* nothing */
},
99 => static function ($self, $stackPos) {
/* nothing */
},
100 => static function ($self, $stackPos) {
$self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
},
101 => null,
102 => null,
103 => static function ($self, $stackPos) {
$self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
104 => static function ($self, $stackPos) {
$self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
105 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
106 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
107 => static function ($self, $stackPos) {
$self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
108 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
109 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
110 => static function ($self, $stackPos) {
$self->semValue = [];
},
111 => null,
112 => null,
113 => null,
114 => null,
115 => static function ($self, $stackPos) {
$self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
116 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
$self->checkNamespace($self->semValue);
},
117 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
$self->checkNamespace($self->semValue);
},
118 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
$self->checkNamespace($self->semValue);
},
119 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
120 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
121 => null,
122 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
123 => static function ($self, $stackPos) {
$self->semValue = Stmt\Use_::TYPE_FUNCTION;
},
124 => static function ($self, $stackPos) {
$self->semValue = Stmt\Use_::TYPE_CONSTANT;
},
125 => static function ($self, $stackPos) {
$self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
126 => static function ($self, $stackPos) {
$self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
127 => null,
128 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
129 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
130 => null,
131 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
132 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
133 => null,
134 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
135 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
136 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
},
137 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
},
138 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
},
139 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
},
140 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL;
},
141 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)];
},
142 => null,
143 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
144 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
145 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
146 => null,
147 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
148 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
149 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
150 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
151 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
},
152 => static function ($self, $stackPos) {
$self->semValue = array();
},
153 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
154 => null,
155 => null,
156 => null,
157 => static function ($self, $stackPos) {
throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
158 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
159 => static function ($self, $stackPos) {
$self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
160 => static function ($self, $stackPos) {
$self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
161 => static function ($self, $stackPos) {
$self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
162 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
163 => static function ($self, $stackPos) {
$self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
164 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
165 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
166 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
167 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
168 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
169 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
170 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
171 => static function ($self, $stackPos) {
$self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1)));
},
172 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
173 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
174 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
175 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
176 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)], $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
177 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
178 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue);
},
179 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
180 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
181 => static function ($self, $stackPos) {
$self->semValue = null; /* means: no statement */
},
182 => null,
183 => static function ($self, $stackPos) {
$self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]);
},
184 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
},
185 => static function ($self, $stackPos) {
$self->semValue = array();
},
186 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
187 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
188 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
189 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
190 => static function ($self, $stackPos) {
$self->semValue = null;
},
191 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
192 => null,
193 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
194 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
195 => static function ($self, $stackPos) {
$self->semValue = false;
},
196 => static function ($self, $stackPos) {
$self->semValue = true;
},
197 => static function ($self, $stackPos) {
$self->semValue = false;
},
198 => static function ($self, $stackPos) {
$self->semValue = true;
},
199 => static function ($self, $stackPos) {
$self->semValue = false;
},
200 => static function ($self, $stackPos) {
$self->semValue = true;
},
201 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
202 => static function ($self, $stackPos) {
$self->semValue = [];
},
203 => null,
204 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
205 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
206 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
207 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
$self->checkClass($self->semValue, $stackPos-(7-2));
},
208 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
$self->checkClass($self->semValue, $stackPos-(8-3));
},
209 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
$self->checkInterface($self->semValue, $stackPos-(7-3));
},
210 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
211 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
$self->checkEnum($self->semValue, $stackPos-(8-3));
},
212 => static function ($self, $stackPos) {
$self->semValue = null;
},
213 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
214 => static function ($self, $stackPos) {
$self->semValue = null;
},
215 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
216 => static function ($self, $stackPos) {
$self->semValue = 0;
},
217 => null,
218 => null,
219 => static function ($self, $stackPos) {
$self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
220 => static function ($self, $stackPos) {
$self->semValue = Modifiers::ABSTRACT;
},
221 => static function ($self, $stackPos) {
$self->semValue = Modifiers::FINAL;
},
222 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
223 => static function ($self, $stackPos) {
$self->semValue = null;
},
224 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
225 => static function ($self, $stackPos) {
$self->semValue = array();
},
226 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
227 => static function ($self, $stackPos) {
$self->semValue = array();
},
228 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
229 => null,
230 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
231 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
232 => null,
233 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
234 => null,
235 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
236 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
},
237 => static function ($self, $stackPos) {
$self->semValue = null;
},
238 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
239 => null,
240 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
241 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
242 => static function ($self, $stackPos) {
$self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
243 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
244 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-3)];
},
245 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
246 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(5-3)];
},
247 => static function ($self, $stackPos) {
$self->semValue = array();
},
248 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
249 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
250 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
251 => null,
252 => null,
253 => static function ($self, $stackPos) {
$self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
254 => static function ($self, $stackPos) {
$self->semValue = [];
},
255 => null,
256 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
257 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
258 => static function ($self, $stackPos) {
$self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
259 => static function ($self, $stackPos) {
$self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
260 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)];
},
261 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
262 => static function ($self, $stackPos) {
$self->semValue = array();
},
263 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
264 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
265 => static function ($self, $stackPos) {
$self->semValue = array();
},
266 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
267 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
},
268 => static function ($self, $stackPos) {
$self->semValue = null;
},
269 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
270 => static function ($self, $stackPos) {
$self->semValue = null;
},
271 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
},
272 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)], false);
},
273 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(2-2)], true);
},
274 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)], false);
},
275 => static function ($self, $stackPos) {
$self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false);
},
276 => null,
277 => static function ($self, $stackPos) {
$self->semValue = array();
},
278 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
279 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
280 => static function ($self, $stackPos) {
$self->semValue = 0;
},
281 => static function ($self, $stackPos) {
$self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
282 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PUBLIC;
},
283 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PROTECTED;
},
284 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PRIVATE;
},
285 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
286 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(6-6)], null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
$self->checkParam($self->semValue);
},
287 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-8)], $self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(8-2)], $self->semStack[$stackPos-(8-1)]);
$self->checkParam($self->semValue);
},
288 => static function ($self, $stackPos) {
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
},
289 => null,
290 => static function ($self, $stackPos) {
$self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
291 => static function ($self, $stackPos) {
$self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
292 => null,
293 => null,
294 => static function ($self, $stackPos) {
$self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
295 => static function ($self, $stackPos) {
$self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]);
},
296 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
297 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
298 => null,
299 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
300 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
301 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
302 => null,
303 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
304 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
305 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
306 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
307 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
308 => static function ($self, $stackPos) {
$self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
309 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
310 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
311 => static function ($self, $stackPos) {
$self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
312 => null,
313 => static function ($self, $stackPos) {
$self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
314 => static function ($self, $stackPos) {
$self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
315 => null,
316 => static function ($self, $stackPos) {
$self->semValue = null;
},
317 => null,
318 => static function ($self, $stackPos) {
$self->semValue = null;
},
319 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
320 => static function ($self, $stackPos) {
$self->semValue = null;
},
321 => static function ($self, $stackPos) {
$self->semValue = array();
},
322 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
323 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-2)]);
},
324 => static function ($self, $stackPos) {
$self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
325 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
326 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
327 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
328 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
329 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
330 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]);
},
331 => null,
332 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
333 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
334 => null,
335 => null,
336 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
337 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
338 => static function ($self, $stackPos) {
$self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
339 => static function ($self, $stackPos) {
$self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
340 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; }
},
341 => static function ($self, $stackPos) {
$self->semValue = array();
},
342 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
343 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]);
$self->checkProperty($self->semValue, $stackPos-(5-2));
},
344 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
$self->checkClassConst($self->semValue, $stackPos-(5-2));
},
345 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]);
$self->checkClassConst($self->semValue, $stackPos-(6-2));
},
346 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
$self->checkClassMethod($self->semValue, $stackPos-(10-2));
},
347 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
348 => static function ($self, $stackPos) {
$self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
349 => static function ($self, $stackPos) {
$self->semValue = null; /* will be skipped */
},
350 => static function ($self, $stackPos) {
$self->semValue = array();
},
351 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
352 => static function ($self, $stackPos) {
$self->semValue = array();
},
353 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
354 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
355 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
356 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
357 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
358 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
359 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
360 => null,
361 => static function ($self, $stackPos) {
$self->semValue = array(null, $self->semStack[$stackPos-(1-1)]);
},
362 => static function ($self, $stackPos) {
$self->semValue = null;
},
363 => null,
364 => null,
365 => static function ($self, $stackPos) {
$self->semValue = 0;
},
366 => static function ($self, $stackPos) {
$self->semValue = 0;
},
367 => null,
368 => null,
369 => static function ($self, $stackPos) {
$self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
370 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PUBLIC;
},
371 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PROTECTED;
},
372 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PRIVATE;
},
373 => static function ($self, $stackPos) {
$self->semValue = Modifiers::STATIC;
},
374 => static function ($self, $stackPos) {
$self->semValue = Modifiers::ABSTRACT;
},
375 => static function ($self, $stackPos) {
$self->semValue = Modifiers::FINAL;
},
376 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
377 => null,
378 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
379 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
380 => static function ($self, $stackPos) {
$self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
381 => static function ($self, $stackPos) {
$self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
382 => static function ($self, $stackPos) {
$self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
383 => null,
384 => null,
385 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
386 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
387 => static function ($self, $stackPos) {
$self->semValue = array();
},
388 => null,
389 => null,
390 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
391 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
392 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
393 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
394 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
if (!$self->phpVersion->allowsAssignNewByReference()) {
$self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
}
},
395 => null,
396 => null,
397 => static function ($self, $stackPos) {
$self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
398 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
399 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
400 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
401 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
402 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
403 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
404 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
405 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
406 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
407 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
408 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
409 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
410 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
411 => static function ($self, $stackPos) {
$self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
412 => static function ($self, $stackPos) {
$self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
413 => static function ($self, $stackPos) {
$self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
414 => static function ($self, $stackPos) {
$self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
415 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
416 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
417 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
418 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
419 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
420 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
421 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
422 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
423 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
424 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
425 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
426 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
427 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
428 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
429 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
430 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
431 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
432 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
433 => static function ($self, $stackPos) {
$self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
434 => static function ($self, $stackPos) {
$self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
435 => static function ($self, $stackPos) {
$self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
436 => static function ($self, $stackPos) {
$self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
437 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
438 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
439 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
440 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
441 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
442 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
443 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
444 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
445 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
446 => static function ($self, $stackPos) {
$self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
447 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
448 => static function ($self, $stackPos) {
$self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
449 => static function ($self, $stackPos) {
$self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
450 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
451 => static function ($self, $stackPos) {
$self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
452 => static function ($self, $stackPos) {
$self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
453 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
454 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
455 => static function ($self, $stackPos) {
$self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
456 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
457 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
458 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
459 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
$attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]);
$self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs);
},
460 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
461 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
462 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
463 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
464 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
465 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
$attrs['kind'] = strtolower($self->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
$self->semValue = new Expr\Exit_($self->semStack[$stackPos-(2-2)], $attrs);
},
466 => static function ($self, $stackPos) {
$self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
467 => null,
468 => static function ($self, $stackPos) {
$self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
469 => static function ($self, $stackPos) {
$self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
470 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
471 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
472 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
473 => static function ($self, $stackPos) {
$self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
474 => static function ($self, $stackPos) {
$self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
475 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
476 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
477 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
478 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
479 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
480 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
481 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
482 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
483 => static function ($self, $stackPos) {
$self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]);
$self->checkClass($self->semValue[0], -1);
},
484 => static function ($self, $stackPos) {
$self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
485 => static function ($self, $stackPos) {
list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
486 => static function ($self, $stackPos) {
$self->semValue = new Expr\New_($self->semStack[$stackPos-(2-2)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
487 => null,
488 => null,
489 => static function ($self, $stackPos) {
$self->semValue = array();
},
490 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-3)];
},
491 => null,
492 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
493 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
494 => static function ($self, $stackPos) {
$self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
495 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
496 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
497 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
498 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
499 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
500 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
501 => null,
502 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
503 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
504 => static function ($self, $stackPos) {
$self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
505 => static function ($self, $stackPos) {
$self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
506 => null,
507 => null,
508 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
509 => static function ($self, $stackPos) {
$self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
510 => null,
511 => null,
512 => static function ($self, $stackPos) {
$self->semValue = null;
},
513 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
514 => static function ($self, $stackPos) {
$self->semValue = array();
},
515 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } };
},
516 => static function ($self, $stackPos) {
foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)];
},
517 => static function ($self, $stackPos) {
$self->semValue = array();
},
518 => null,
519 => static function ($self, $stackPos) {
$self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
520 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
521 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
522 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
523 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
524 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
525 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
526 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
527 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
528 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
529 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
530 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
531 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT;
$self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs);
},
532 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG;
$self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs);
$self->createdArrays->attach($self->semValue);
},
533 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->attach($self->semValue);
},
534 => static function ($self, $stackPos) {
$self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes());
},
535 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs);
},
536 => static function ($self, $stackPos) {
$self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals());
},
537 => static function ($self, $stackPos) {
$self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
538 => null,
539 => null,
540 => null,
541 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true);
},
542 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)], $self->tokenEndStack[$stackPos-(2-2)]), true);
},
543 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true);
},
544 => static function ($self, $stackPos) {
$self->semValue = null;
},
545 => null,
546 => null,
547 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
548 => null,
549 => null,
550 => null,
551 => null,
552 => null,
553 => null,
554 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
555 => null,
556 => null,
557 => null,
558 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
559 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
560 => null,
561 => static function ($self, $stackPos) {
$self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
562 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
563 => static function ($self, $stackPos) {
$self->semValue = null;
},
564 => null,
565 => null,
566 => null,
567 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
568 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
569 => null,
570 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
571 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
572 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
573 => static function ($self, $stackPos) {
$var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var;
},
574 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
575 => null,
576 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
577 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
578 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
579 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
580 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
581 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
582 => null,
583 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
584 => null,
585 => null,
586 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
587 => null,
588 => static function ($self, $stackPos) {
$self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
589 => static function ($self, $stackPos) {
$self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST);
$self->postprocessList($self->semValue);
},
590 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue);
},
591 => null,
592 => static function ($self, $stackPos) {
/* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */
},
593 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
594 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
595 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
596 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
597 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
598 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
599 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
600 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
601 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true);
},
602 => static function ($self, $stackPos) {
/* Create an Error node now to remember the position. We'll later either report an error,
or convert this into a null element, depending on whether this is a creation or destructuring context. */
$attrs = $self->createEmptyElemAttributes($self->tokenPos);
$self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs);
},
603 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
604 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
605 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
606 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]);
},
607 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs);
},
608 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
609 => null,
610 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
611 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
612 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
613 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
614 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
615 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
616 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
617 => static function ($self, $stackPos) {
$self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
618 => static function ($self, $stackPos) {
$self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
619 => static function ($self, $stackPos) {
$self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
620 => null,
];
}
}

View File

@ -0,0 +1,2737 @@
<?php declare(strict_types=1);
namespace PhpParser\Parser;
use PhpParser\Error;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
/* This is an automatically GENERATED file, which should not be manually edited.
* Instead edit one of the following:
* * the grammar file grammar/php.y
* * the skeleton file grammar/parser.template
* * the preprocessing script grammar/rebuildParsers.php
*/
class Php8 extends \PhpParser\ParserAbstract
{
public const YYERRTOK = 256;
public const T_THROW = 257;
public const T_INCLUDE = 258;
public const T_INCLUDE_ONCE = 259;
public const T_EVAL = 260;
public const T_REQUIRE = 261;
public const T_REQUIRE_ONCE = 262;
public const T_LOGICAL_OR = 263;
public const T_LOGICAL_XOR = 264;
public const T_LOGICAL_AND = 265;
public const T_PRINT = 266;
public const T_YIELD = 267;
public const T_DOUBLE_ARROW = 268;
public const T_YIELD_FROM = 269;
public const T_PLUS_EQUAL = 270;
public const T_MINUS_EQUAL = 271;
public const T_MUL_EQUAL = 272;
public const T_DIV_EQUAL = 273;
public const T_CONCAT_EQUAL = 274;
public const T_MOD_EQUAL = 275;
public const T_AND_EQUAL = 276;
public const T_OR_EQUAL = 277;
public const T_XOR_EQUAL = 278;
public const T_SL_EQUAL = 279;
public const T_SR_EQUAL = 280;
public const T_POW_EQUAL = 281;
public const T_COALESCE_EQUAL = 282;
public const T_COALESCE = 283;
public const T_BOOLEAN_OR = 284;
public const T_BOOLEAN_AND = 285;
public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG = 286;
public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG = 287;
public const T_IS_EQUAL = 288;
public const T_IS_NOT_EQUAL = 289;
public const T_IS_IDENTICAL = 290;
public const T_IS_NOT_IDENTICAL = 291;
public const T_SPACESHIP = 292;
public const T_IS_SMALLER_OR_EQUAL = 293;
public const T_IS_GREATER_OR_EQUAL = 294;
public const T_SL = 295;
public const T_SR = 296;
public const T_INSTANCEOF = 297;
public const T_INC = 298;
public const T_DEC = 299;
public const T_INT_CAST = 300;
public const T_DOUBLE_CAST = 301;
public const T_STRING_CAST = 302;
public const T_ARRAY_CAST = 303;
public const T_OBJECT_CAST = 304;
public const T_BOOL_CAST = 305;
public const T_UNSET_CAST = 306;
public const T_POW = 307;
public const T_NEW = 308;
public const T_CLONE = 309;
public const T_EXIT = 310;
public const T_IF = 311;
public const T_ELSEIF = 312;
public const T_ELSE = 313;
public const T_ENDIF = 314;
public const T_LNUMBER = 315;
public const T_DNUMBER = 316;
public const T_STRING = 317;
public const T_STRING_VARNAME = 318;
public const T_VARIABLE = 319;
public const T_NUM_STRING = 320;
public const T_INLINE_HTML = 321;
public const T_ENCAPSED_AND_WHITESPACE = 322;
public const T_CONSTANT_ENCAPSED_STRING = 323;
public const T_ECHO = 324;
public const T_DO = 325;
public const T_WHILE = 326;
public const T_ENDWHILE = 327;
public const T_FOR = 328;
public const T_ENDFOR = 329;
public const T_FOREACH = 330;
public const T_ENDFOREACH = 331;
public const T_DECLARE = 332;
public const T_ENDDECLARE = 333;
public const T_AS = 334;
public const T_SWITCH = 335;
public const T_MATCH = 336;
public const T_ENDSWITCH = 337;
public const T_CASE = 338;
public const T_DEFAULT = 339;
public const T_BREAK = 340;
public const T_CONTINUE = 341;
public const T_GOTO = 342;
public const T_FUNCTION = 343;
public const T_FN = 344;
public const T_CONST = 345;
public const T_RETURN = 346;
public const T_TRY = 347;
public const T_CATCH = 348;
public const T_FINALLY = 349;
public const T_USE = 350;
public const T_INSTEADOF = 351;
public const T_GLOBAL = 352;
public const T_STATIC = 353;
public const T_ABSTRACT = 354;
public const T_FINAL = 355;
public const T_PRIVATE = 356;
public const T_PROTECTED = 357;
public const T_PUBLIC = 358;
public const T_READONLY = 359;
public const T_VAR = 360;
public const T_UNSET = 361;
public const T_ISSET = 362;
public const T_EMPTY = 363;
public const T_HALT_COMPILER = 364;
public const T_CLASS = 365;
public const T_TRAIT = 366;
public const T_INTERFACE = 367;
public const T_ENUM = 368;
public const T_EXTENDS = 369;
public const T_IMPLEMENTS = 370;
public const T_OBJECT_OPERATOR = 371;
public const T_NULLSAFE_OBJECT_OPERATOR = 372;
public const T_LIST = 373;
public const T_ARRAY = 374;
public const T_CALLABLE = 375;
public const T_CLASS_C = 376;
public const T_TRAIT_C = 377;
public const T_METHOD_C = 378;
public const T_FUNC_C = 379;
public const T_LINE = 380;
public const T_FILE = 381;
public const T_START_HEREDOC = 382;
public const T_END_HEREDOC = 383;
public const T_DOLLAR_OPEN_CURLY_BRACES = 384;
public const T_CURLY_OPEN = 385;
public const T_PAAMAYIM_NEKUDOTAYIM = 386;
public const T_NAMESPACE = 387;
public const T_NS_C = 388;
public const T_DIR = 389;
public const T_NS_SEPARATOR = 390;
public const T_ELLIPSIS = 391;
public const T_NAME_FULLY_QUALIFIED = 392;
public const T_NAME_QUALIFIED = 393;
public const T_NAME_RELATIVE = 394;
public const T_ATTRIBUTE = 395;
protected int $tokenToSymbolMapSize = 396;
protected int $actionTableSize = 1272;
protected int $gotoTableSize = 689;
protected int $invalidSymbol = 168;
protected int $errorSymbol = 1;
protected int $defaultAction = -32766;
protected int $unexpectedTokenRule = 32767;
protected int $YY2TBLSTATE = 437;
protected int $numNonLeafStates = 743;
protected array $symbolToName = array(
"EOF",
"error",
"T_THROW",
"T_INCLUDE",
"T_INCLUDE_ONCE",
"T_EVAL",
"T_REQUIRE",
"T_REQUIRE_ONCE",
"','",
"T_LOGICAL_OR",
"T_LOGICAL_XOR",
"T_LOGICAL_AND",
"T_PRINT",
"T_YIELD",
"T_DOUBLE_ARROW",
"T_YIELD_FROM",
"'='",
"T_PLUS_EQUAL",
"T_MINUS_EQUAL",
"T_MUL_EQUAL",
"T_DIV_EQUAL",
"T_CONCAT_EQUAL",
"T_MOD_EQUAL",
"T_AND_EQUAL",
"T_OR_EQUAL",
"T_XOR_EQUAL",
"T_SL_EQUAL",
"T_SR_EQUAL",
"T_POW_EQUAL",
"T_COALESCE_EQUAL",
"'?'",
"':'",
"T_COALESCE",
"T_BOOLEAN_OR",
"T_BOOLEAN_AND",
"'|'",
"'^'",
"T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG",
"T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG",
"T_IS_EQUAL",
"T_IS_NOT_EQUAL",
"T_IS_IDENTICAL",
"T_IS_NOT_IDENTICAL",
"T_SPACESHIP",
"'<'",
"T_IS_SMALLER_OR_EQUAL",
"'>'",
"T_IS_GREATER_OR_EQUAL",
"'.'",
"T_SL",
"T_SR",
"'+'",
"'-'",
"'*'",
"'/'",
"'%'",
"'!'",
"T_INSTANCEOF",
"'~'",
"T_INC",
"T_DEC",
"T_INT_CAST",
"T_DOUBLE_CAST",
"T_STRING_CAST",
"T_ARRAY_CAST",
"T_OBJECT_CAST",
"T_BOOL_CAST",
"T_UNSET_CAST",
"'@'",
"T_POW",
"'['",
"T_NEW",
"T_CLONE",
"T_EXIT",
"T_IF",
"T_ELSEIF",
"T_ELSE",
"T_ENDIF",
"T_LNUMBER",
"T_DNUMBER",
"T_STRING",
"T_STRING_VARNAME",
"T_VARIABLE",
"T_NUM_STRING",
"T_INLINE_HTML",
"T_ENCAPSED_AND_WHITESPACE",
"T_CONSTANT_ENCAPSED_STRING",
"T_ECHO",
"T_DO",
"T_WHILE",
"T_ENDWHILE",
"T_FOR",
"T_ENDFOR",
"T_FOREACH",
"T_ENDFOREACH",
"T_DECLARE",
"T_ENDDECLARE",
"T_AS",
"T_SWITCH",
"T_MATCH",
"T_ENDSWITCH",
"T_CASE",
"T_DEFAULT",
"T_BREAK",
"T_CONTINUE",
"T_GOTO",
"T_FUNCTION",
"T_FN",
"T_CONST",
"T_RETURN",
"T_TRY",
"T_CATCH",
"T_FINALLY",
"T_USE",
"T_INSTEADOF",
"T_GLOBAL",
"T_STATIC",
"T_ABSTRACT",
"T_FINAL",
"T_PRIVATE",
"T_PROTECTED",
"T_PUBLIC",
"T_READONLY",
"T_VAR",
"T_UNSET",
"T_ISSET",
"T_EMPTY",
"T_HALT_COMPILER",
"T_CLASS",
"T_TRAIT",
"T_INTERFACE",
"T_ENUM",
"T_EXTENDS",
"T_IMPLEMENTS",
"T_OBJECT_OPERATOR",
"T_NULLSAFE_OBJECT_OPERATOR",
"T_LIST",
"T_ARRAY",
"T_CALLABLE",
"T_CLASS_C",
"T_TRAIT_C",
"T_METHOD_C",
"T_FUNC_C",
"T_LINE",
"T_FILE",
"T_START_HEREDOC",
"T_END_HEREDOC",
"T_DOLLAR_OPEN_CURLY_BRACES",
"T_CURLY_OPEN",
"T_PAAMAYIM_NEKUDOTAYIM",
"T_NAMESPACE",
"T_NS_C",
"T_DIR",
"T_NS_SEPARATOR",
"T_ELLIPSIS",
"T_NAME_FULLY_QUALIFIED",
"T_NAME_QUALIFIED",
"T_NAME_RELATIVE",
"T_ATTRIBUTE",
"';'",
"']'",
"'('",
"')'",
"'{'",
"'}'",
"'`'",
"'\"'",
"'$'"
);
protected array $tokenToSymbol = array(
0, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 56, 166, 168, 167, 55, 168, 168,
161, 162, 53, 51, 8, 52, 48, 54, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 31, 159,
44, 16, 46, 30, 68, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 70, 168, 160, 36, 168, 165, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 163, 35, 164, 58, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 168, 168, 168, 168,
168, 168, 168, 168, 168, 168, 1, 2, 3, 4,
5, 6, 7, 9, 10, 11, 12, 13, 14, 15,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 32, 33, 34, 37, 38, 39, 40,
41, 42, 43, 45, 47, 49, 50, 57, 59, 60,
61, 62, 63, 64, 65, 66, 67, 69, 71, 72,
73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
153, 154, 155, 156, 157, 158
);
protected array $action = array(
133, 134, 135, 586, 136, 137, 0, 755, 756, 757,
138, 38, 329,-32766,-32766,-32766,-32766,-32766,-32766, 841,
830,-32767,-32767,-32767,-32767, 102, 103, 104, 1116, 1117,
1118, 1115, 1114, 1113, 1119, 749, 748,-32766, 1031,-32766,
-32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,
-32767, 1252,-32766,-32766, 1331, 758, 1116, 1117, 1118, 1115,
1114, 1113, 1119, 461, 462, 463, 2, 994, 1315, 265,
139, 406, 762, 763, 764, 765, 470, 471, 431, 839,
610, -16, 1350, 23, 293, 819, 766, 767, 768, 769,
770, 771, 772, 773, 774, 775, 795, 587, 796, 797,
798, 799, 787, 788, 347, 348, 790, 791, 776, 777,
778, 780, 781, 782, 358, 822, 823, 824, 825, 826,
588, 783, 784, 589, 590, 945, 807, 805, 806, 818,
802, 803, 839, 830, 591, 592, 801, 593, 594, 595,
596, 597, 598, -328, 36, 250, 35, -194, 804, 599,
600, -193, 140, -85, 133, 134, 135, 586, 136, 137,
1064, 755, 756, 757, 138, 38, 129, -110, -110, -590,
-32766, -590, -110,-32766,-32766,-32766, 241, 840, -110, 145,
963, 964,-32766,-32766,-32766, 965, -599,-32766, 485, 749,
748, 959, 1040, -599,-32766, 995,-32766,-32766,-32766,-32766,
-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766, 301, 758,
835, 75,-32766,-32766,-32766, 292, 142, 328, 242, -85,
328, 384, 383, 265, 139, 406, 762, 763, 764, 765,
82, 425, 431,-32766, 328,-32766,-32766,-32766,-32766, 819,
766, 767, 768, 769, 770, 771, 772, 773, 774, 775,
795, 587, 796, 797, 798, 799, 787, 788, 347, 348,
790, 791, 776, 777, 778, 780, 781, 782, 358, 822,
823, 824, 825, 826, 588, 783, 784, 589, 590, 253,
807, 805, 806, 818, 802, 803, 836, 729, 591, 592,
801, 593, 594, 595, 596, 597, 598, -328, 83, 84,
85, -194, 804, 599, 600, -193, 149, 779, 750, 751,
752, 753, 754, 151, 755, 756, 757, 792, 793, 37,
486, 86, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, -599, 275, -599,-32766,-32766,
-32766,-32766,-32766,-32766, 312, 1093, 127, 314, 110, 741,
1335, 21, 758,-32766,-32766,-32766, -272, 1334,-32766,-32766,
1092,-32766,-32766,-32766,-32766,-32766, 759, 760, 761, 762,
763, 764, 765, 1108,-32766, 828,-32766,-32766, -550, 560,
1040, 1273, 819, 766, 767, 768, 769, 770, 771, 772,
773, 774, 775, 795, 817, 796, 797, 798, 799, 787,
788, 789, 816, 790, 791, 776, 777, 778, 780, 781,
782, 821, 822, 823, 824, 825, 826, 827, 783, 784,
785, 786, 1037, 807, 805, 806, 818, 802, 803, 749,
748, 794, 800, 801, 808, 809, 811, 810, 812, 813,
1285, 325, -550, -550, 1040, 804, 815, 814, 50, 51,
52, 516, 53, 54, 866, 341, 867, -550, 55, 56,
-110, 57, 839, 924, -367, -110, -367, -110, 293, -556,
152, -550, 308, 103, 104, -110, -110, -110, -110, -110,
-110, -110, -110, 105, 106, 107, 108, 109, 947, 275,
342, 924, 1252, 719,-32766,-32766,-32766, 58, 59, -549,
372, 110, 60, 838, 61, 247, 248, 62, 63, 64,
65, 66, 67, 68, 69,-32766, 28, 267, 70, 446,
517, 720, 376, -342, 1279, 1280, 518, 359, 839, -548,
391, -546, 1277, 42, 25, 519, 947, 520, 616, 521,
924, 522, 442, 141, 523, 524, 914, 328, 443, 44,
45, 447, 379, 378,-32766, 46, 525, 1027, 1026, 1025,
1028, 370, 340, -549, -549, 444, 1360, 431, 1238, 1361,
527, 528, 529, 839, 914, 364, 1040, 445, -549,-32766,
-32766,-32766, 531, 532, 845, 1266, 1267, 1268, 1269, 1263,
1264, 300, -549, -548, -548, -546, -546, 1270, 1265, 292,
-32766, 1247, 1246, 1248, 301, 749, 748, 71, -548, -78,
-546, 323, 324, 328, -153, -153, -153, 393,-32766, 7,
-555, 926, -548, 914, -546, 714, 660, 26,-32766, -153,
832, -153, 866, -153, 867, -153, 382, 383, 28, 268,
1040, 154, 1247, 1246, 1248, 377, 425, 155, -596, 926,
839, 1094, 75, 714, 1277, -596, 963, 964, 328, -547,
156, 526, 158, 292, 1245, 33, 900, 959, -110, -110,
-110, 32, 111, 112, 113, 114, 115, 116, 117, 118,
119, 120, 121, 122, 123, 679, 680, -58, 301, -57,
1238, 124, 924, 749, 748, 1252, 150, 409, 926, 125,
1243, 924, 714, -153, 531, 532, 834, 1266, 1267, 1268,
1269, 1263, 1264, 716, 1154, 1156, -87, -4, 924, 1270,
1265, 1039, 721, -547, -547, -546, 130, 749, 748, 73,
-32766, 724, 131, -552, 324, 328, 1245, 144, -547, 1247,
1246, 1248, 159,-32766,-32766,-32766, 1037,-32766, 160,-32766,
-554,-32766, -547, 161,-32766, 380, 381, 924, 162,-32766,
-32766,-32766, 163, 49,-32766,-32766,-32766, -84, 1040, -78,
1245,-32766, 422, 48, 924, 914, 839,-32766,-32766,-32766,
-32766,-32766, -73,-32766, 914,-32766, -72, 731,-32766, -546,
-546, 283, -71,-32766,-32766,-32766, -70, -552, -552,-32766,
-32766, 914, 385, 386, -546,-32766, 422, -596, -69, -596,
74, -110, -110, -68,-32766, -50, -110, -67, -546, 651,
652, -66, -110, 377, -65, 438, -552, 304, 305, -46,
299,-32766, -18, 148, 963, 964, 274, 302, 303, 526,
914, 284, 375, 730, 530, 959, -110, -110, -110, 132,
980, 733, 301, 923, 714, 75, 128, 914,-32766, 926,
147, 328, -302, 714, 1245, -298, 126, 10, 1063, 281,
282,-32766,-32766,-32766, 285,-32766, 926,-32766, 286,-32766,
714, -4,-32766, 334, 288, 275, 289,-32766,-32766,-32766,
294, 295,-32766,-32766,-32766, 924, 941, 287, 1245,-32766,
422, 110, 689, 146, 830,-32766,-32766,-32766,-32766,-32766,
565,-32766, 666,-32766, 1362, 926,-32766, 705, 839, 714,
1123,-32766,-32766,-32766,-32766,-32766, 667,-32766,-32766, 309,
1245, 661, 926,-32766, 422, 924, 714,-32766,-32766,-32766,
682,-32766,-32766,-32766, 707,-32766, 306, 960,-32766, 313,
-32766, 683, 491,-32766,-32766,-32766,-32766, 20, 467,-32766,
-32766, 496, 1245, 578, 571,-32766, 422, 301, 649,-32766,
-32766,-32766, -511,-32766,-32766,-32766, 0,-32766, 914, 0,
-32766, 0, 0, 1037, 0,-32766,-32766,-32766, 1284, 307,
1286,-32766,-32766, 0, -250, -250, -250,-32766, 422, 943,
377, 0, 0, 28, 267, 1040,-32766, 0, -501, 0,
614, 963, 964, 0, 8, 839, 526, 24, 914, 1277,
374, 900, 959, -110, -110, -110, 1274, 838, 283, 40,
-584, 0, 41, 738, -249, -249, -249, 739, 28, 268,
377, 850, 287, 858, 905, 1004, 981, 988, 978, 989,
839, 963, 964, 926, 1277, 1238, 526, 714, -250, 903,
976, 900, 959, -110, -110, -110, 1097, 1100, 1101, 1098,
532, 1099, 1266, 1267, 1268, 1269, 1263, 1264, 1105, -583,
1301, 1319, 1353, 654, 1270, 1265, -582, -556, -555, -554,
1238, -553, 694, 926, 73, 34, -495, 714, -249, 324,
328, 1, 29, 30, 39, 532, 43, 1266, 1267, 1268,
1269, 1263, 1264, 47, 72, 76, 77, 78, 79, 1270,
1265, 80, 81, 143,-32766, 153, 157, 245, 695, 73,
1245, 330, 359, 360, 324, 328, 361,-32766,-32766,-32766,
362,-32766, 363,-32766, 364,-32766, 365, 366,-32766, 696,
697, 367, 368,-32766,-32766,-32766, 369, 371, 439,-32766,
-32766, 559, 322, -275, -273,-32766, 422, 1247, 1246, 1248,
-272, 13, 14, 283,-32766, 15, 16, 18, 408, 487,
488, 495, 498, 499, 500, 501, 505, 506, 507, 514,
576, 700, 1256, 1194, 1275, 1066, 1065, 1046, 1233, 1042,
-277, -102, 12, 17, 27, 298, 407, 607, 611, 640,
706, 1198, 0, 1251, 1195, 1332, 0, 373, 715, 718,
722, 723, 725, 726, 727, 728, 732, 717, 0, 735,
901, 1357, 1359, 861, 860, 869, 953, 996, 868, 1358,
952, 950, 951, 954, 1226, 934, 944, 932, 986, 987,
638, 1356, 1313, 1302, 1320, 1329, 0, 1211, 0, 1278,
0, 328
);
protected array $actionCheck = array(
2, 3, 4, 5, 6, 7, 0, 9, 10, 11,
12, 13, 70, 9, 10, 11, 9, 10, 11, 1,
80, 44, 45, 46, 47, 48, 49, 50, 116, 117,
118, 119, 120, 121, 122, 37, 38, 30, 1, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 1, 9, 10, 1, 57, 116, 117, 118, 119,
120, 121, 122, 129, 130, 131, 8, 31, 1, 71,
72, 73, 74, 75, 76, 77, 134, 135, 80, 82,
1, 31, 85, 8, 30, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
122, 123, 124, 125, 126, 1, 128, 129, 130, 131,
132, 133, 82, 80, 136, 137, 138, 139, 140, 141,
142, 143, 144, 8, 147, 148, 8, 8, 150, 151,
152, 8, 154, 31, 2, 3, 4, 5, 6, 7,
162, 9, 10, 11, 12, 13, 8, 117, 118, 160,
116, 162, 122, 9, 10, 11, 97, 159, 128, 8,
117, 118, 9, 10, 11, 122, 1, 137, 31, 37,
38, 128, 138, 8, 30, 159, 32, 33, 34, 35,
36, 37, 38, 30, 9, 32, 33, 34, 158, 57,
80, 161, 9, 10, 11, 161, 163, 167, 14, 97,
167, 106, 107, 71, 72, 73, 74, 75, 76, 77,
163, 116, 80, 30, 167, 32, 33, 34, 35, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 8,
128, 129, 130, 131, 132, 133, 156, 163, 136, 137,
138, 139, 140, 141, 142, 143, 144, 162, 9, 10,
11, 162, 150, 151, 152, 162, 154, 2, 3, 4,
5, 6, 7, 14, 9, 10, 11, 12, 13, 30,
163, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 54, 55, 160, 57, 162, 9, 10,
11, 9, 10, 11, 8, 159, 14, 8, 69, 163,
1, 101, 57, 9, 10, 11, 162, 8, 116, 30,
1, 32, 33, 34, 35, 36, 71, 72, 73, 74,
75, 76, 77, 123, 30, 80, 32, 33, 70, 85,
138, 1, 87, 88, 89, 90, 91, 92, 93, 94,
95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
125, 126, 116, 128, 129, 130, 131, 132, 133, 37,
38, 136, 137, 138, 139, 140, 141, 142, 143, 144,
146, 8, 134, 135, 138, 150, 151, 152, 2, 3,
4, 5, 6, 7, 106, 8, 108, 149, 12, 13,
101, 15, 82, 1, 106, 106, 108, 108, 30, 161,
14, 163, 113, 49, 50, 116, 117, 118, 119, 120,
121, 122, 123, 51, 52, 53, 54, 55, 122, 57,
8, 1, 1, 31, 9, 10, 11, 51, 52, 70,
8, 69, 56, 155, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 30, 70, 71, 72, 73,
74, 31, 8, 164, 78, 79, 80, 161, 82, 70,
8, 70, 86, 87, 88, 89, 122, 91, 52, 93,
1, 95, 8, 163, 98, 99, 84, 167, 8, 103,
104, 105, 106, 107, 116, 109, 110, 119, 120, 121,
122, 115, 116, 134, 135, 8, 80, 80, 122, 83,
124, 125, 126, 82, 84, 161, 138, 8, 149, 116,
51, 52, 136, 137, 8, 139, 140, 141, 142, 143,
144, 145, 163, 134, 135, 134, 135, 151, 152, 161,
137, 155, 156, 157, 158, 37, 38, 161, 149, 16,
149, 165, 166, 167, 75, 76, 77, 106, 116, 108,
161, 159, 163, 84, 163, 163, 75, 76, 137, 90,
80, 92, 106, 94, 108, 96, 106, 107, 70, 71,
138, 14, 155, 156, 157, 106, 116, 14, 1, 159,
82, 164, 161, 163, 86, 8, 117, 118, 167, 70,
14, 122, 14, 161, 80, 14, 127, 128, 129, 130,
131, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 75, 76, 16, 158, 16,
122, 16, 1, 37, 38, 1, 101, 102, 159, 16,
116, 1, 163, 164, 136, 137, 156, 139, 140, 141,
142, 143, 144, 163, 59, 60, 31, 0, 1, 151,
152, 137, 31, 134, 135, 70, 16, 37, 38, 161,
74, 31, 16, 70, 166, 167, 80, 16, 149, 155,
156, 157, 16, 87, 88, 89, 116, 91, 16, 93,
161, 95, 163, 16, 98, 106, 107, 1, 16, 103,
104, 105, 16, 70, 74, 109, 110, 31, 138, 31,
80, 115, 116, 70, 1, 84, 82, 87, 88, 89,
124, 91, 31, 93, 84, 95, 31, 31, 98, 134,
135, 161, 31, 103, 104, 105, 31, 134, 135, 109,
110, 84, 106, 107, 149, 115, 116, 160, 31, 162,
154, 117, 118, 31, 124, 31, 122, 31, 163, 111,
112, 31, 128, 106, 31, 108, 163, 134, 135, 31,
113, 137, 31, 31, 117, 118, 31, 134, 135, 122,
84, 31, 149, 31, 127, 128, 129, 130, 131, 31,
159, 31, 158, 31, 163, 161, 163, 84, 74, 159,
31, 167, 35, 163, 80, 35, 163, 150, 1, 35,
35, 87, 88, 89, 35, 91, 159, 93, 35, 95,
163, 164, 98, 35, 37, 57, 37, 103, 104, 105,
37, 37, 74, 109, 110, 1, 38, 30, 80, 115,
116, 69, 77, 70, 80, 87, 88, 89, 124, 91,
89, 93, 96, 95, 83, 159, 98, 80, 82, 163,
82, 103, 104, 105, 74, 85, 100, 109, 110, 114,
80, 90, 159, 115, 116, 1, 163, 87, 88, 89,
94, 91, 124, 93, 92, 95, 132, 128, 98, 132,
137, 100, 102, 103, 104, 105, 74, 97, 97, 109,
110, 97, 80, 81, 153, 115, 116, 158, 113, 87,
88, 89, 149, 91, 124, 93, -1, 95, 84, -1,
98, -1, -1, 116, -1, 103, 104, 105, 146, 133,
146, 109, 110, -1, 100, 101, 102, 115, 116, 154,
106, -1, -1, 70, 71, 138, 124, -1, 149, -1,
153, 117, 118, -1, 149, 82, 122, 149, 84, 86,
149, 127, 128, 129, 130, 131, 160, 155, 161, 159,
161, -1, 159, 159, 100, 101, 102, 159, 70, 71,
106, 160, 30, 159, 159, 159, 159, 159, 159, 159,
82, 117, 118, 159, 86, 122, 122, 163, 164, 159,
159, 127, 128, 129, 130, 131, 159, 159, 159, 159,
137, 159, 139, 140, 141, 142, 143, 144, 159, 161,
160, 160, 160, 160, 151, 152, 161, 161, 161, 161,
122, 161, 80, 159, 161, 163, 161, 163, 164, 166,
167, 161, 161, 161, 161, 137, 161, 139, 140, 141,
142, 143, 144, 161, 161, 161, 161, 161, 161, 151,
152, 161, 161, 161, 74, 161, 161, 161, 116, 161,
80, 161, 161, 161, 166, 167, 161, 87, 88, 89,
161, 91, 161, 93, 161, 95, 161, 161, 98, 137,
138, 161, 161, 103, 104, 105, 161, 161, 161, 109,
110, 161, 163, 162, 162, 115, 116, 155, 156, 157,
162, 162, 162, 161, 124, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
162, 162, -1, 162, 162, 162, -1, 163, 163, 163,
163, 163, 163, 163, 163, 163, 163, 163, -1, 164,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
164, 164, 164, 164, 164, 164, 164, 164, 164, 164,
164, 164, 164, 164, 164, 164, -1, 165, -1, 166,
-1, 167
);
protected array $actionBase = array(
0, -2, 152, 549, 727, 904, 944, 1022, 390, 497,
560, 922, 500, 710, 710, 766, 710, 472, 701, 847,
-60, 305, 305, 847, 305, 783, 783, 783, 666, 666,
666, 666, 700, 700, 860, 860, 892, 828, 794, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060,
1060, 1060, 1060, 1060, 18, 36, 79, 661, 1053, 1059,
1055, 1061, 1051, 1050, 1054, 1056, 1062, 1097, 1098, 839,
1099, 1100, 1096, 1101, 1057, 933, 1052, 1058, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 195, 342, 43, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 495, 495,
495, 578, 578, 354, 173, 978, 943, 978, 978, 978,
978, 978, 978, 978, 978, 203, 665, 339, 164, 164,
7, 7, 7, 7, 7, 50, 369, 704, 704, -23,
-23, -23, -23, 448, 877, 501, 260, 368, 434, 54,
540, 640, 640, 316, 316, 512, 512, 316, 316, 316,
442, 442, 252, 252, 252, 252, 318, 469, 599, 358,
304, 823, 53, 53, 53, 53, 823, 823, 823, 823,
854, 1103, 823, 823, 823, 439, 471, 471, 703, 539,
539, 471, 536, -3, -3, 536, 63, -3, 67, 496,
473, 829, 115, 9, 473, 673, 713, 657, 185, 882,
659, 882, 1049, 376, 850, 850, 424, 808, 761, 929,
1074, 1063, 836, 1094, 861, 1095, -66, -58, 748, 1048,
1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048,
1104, 402, 1049, 130, 1104, 1104, 1104, 402, 402, 402,
402, 402, 402, 402, 402, 402, 402, 718, 130, 561,
620, 130, 858, 402, 18, 869, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 811, 157, 18, 36,
124, 124, 196, 37, 124, 124, 124, 124, 18, 18,
18, 18, 659, 838, 821, 706, 867, 143, 838, 838,
838, 122, 135, 204, 139, 837, 840, 521, 834, 834,
848, 950, 834, 846, 834, 848, 962, 834, 834, 950,
950, 819, 950, 158, 544, 457, 524, 550, 950, 346,
834, 834, 834, 834, 827, 950, 567, 834, 271, 171,
834, 834, 827, 824, 820, 58, 866, 950, 950, 950,
827, 502, 866, 866, 866, 884, 888, 865, 815, 443,
349, 586, 138, 868, 815, 815, 834, 532, 865, 815,
865, 815, 855, 815, 815, 815, 865, 815, 846, 492,
815, 736, 579, 75, 815, 6, 963, 964, 695, 965,
953, 966, 1007, 967, 970, 1065, 945, 976, 955, 971,
1010, 952, 951, 832, 685, 693, 875, 833, 940, 842,
842, 842, 936, 937, 842, 842, 842, 842, 842, 842,
842, 842, 685, 876, 881, 831, 982, 720, 726, 1038,
852, 1076, 1102, 981, 1040, 972, 880, 731, 1025, 985,
1075, 1009, 989, 991, 1026, 1041, 894, 1042, 1077, 843,
1078, 1079, 891, 995, 1066, 842, 963, 970, 746, 955,
971, 952, 951, 803, 800, 792, 796, 787, 775, 765,
771, 812, 1043, 935, 879, 930, 993, 938, 685, 931,
1019, 942, 1027, 1028, 1064, 871, 841, 932, 1080, 996,
1000, 1001, 1067, 1044, 1068, 883, 1020, 1011, 1029, 874,
1081, 1030, 1031, 1032, 1033, 1069, 1082, 1070, 928, 1071,
895, 851, 1012, 826, 1083, 299, 849, 853, 864, 1006,
466, 980, 1072, 1084, 1085, 1034, 1035, 1036, 1086, 1087,
974, 896, 1023, 856, 1024, 1018, 897, 898, 637, 863,
1045, 844, 845, 859, 643, 656, 1088, 1089, 1090, 975,
822, 835, 899, 900, 1046, 857, 1047, 1091, 658, 910,
742, 1092, 1039, 747, 752, 603, 683, 681, 756, 862,
1073, 878, 825, 870, 1005, 752, 830, 911, 1093, 917,
918, 919, 1037, 920, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 456, 456, 456, 456, 456, 456,
305, 305, 305, 305, 305, 456, 456, 456, 456, 456,
456, 456, 305, 305, 0, 0, 305, 0, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 456, 456, 456,
456, 456, 456, 456, 456, 456, 456, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 289, 289, 289, 289, 289, 289,
289, 289, 289, 289, 594, 594, 289, 289, 594, 594,
594, 594, 594, 594, 594, 594, 594, 594, 289, 0,
289, 289, 289, 289, 289, 289, 289, 289, 594, 819,
594, 594, 442, 442, 442, 442, 594, 594, 594, -88,
-88, 442, 594, 63, 594, 594, 594, 594, 594, 594,
594, 594, 594, 0, 0, 594, 594, 594, 594, 0,
0, 0, 130, -3, 594, 846, 846, 846, 846, 594,
594, 594, 594, -3, -3, 594, 594, 594, 0, 0,
0, 0, 442, 442, 0, 130, 0, 0, 130, 0,
0, 846, 846, 594, 63, 819, 359, 594, 0, 0,
0, 0, 130, 846, 130, 402, 834, -3, -3, 834,
402, 402, 124, 18, 359, 605, 605, 605, 605, 0,
0, 659, 819, 819, 819, 819, 819, 819, 819, 819,
819, 819, 819, 846, 0, 819, 0, 846, 846, 846,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 846, 0, 0, 950, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 962,
0, 0, 0, 0, 0, 0, 846, 0, 0, 0,
0, 0, 0, 0, 0, 0, 842, 871, 0, 871,
0, 842, 842, 842, 0, 0, 0, 0, 863, 857
);
protected array $actionDefault = array(
3,32767, 102,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767, 100,32767,32767,32767,32767, 602, 602,
602, 602,32767,32767, 254, 102,32767,32767, 470, 387,
387, 387,32767,32767, 544, 544, 544, 544, 544, 544,
32767,32767,32767,32767,32767,32767, 470,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767, 100,
32767,32767,32767, 36, 7, 8, 10, 11, 49, 17,
324,32767,32767,32767,32767, 102,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767, 595,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767, 474, 453,
454, 456, 457, 386, 545, 601, 327, 598, 385, 145,
339, 329, 242, 330, 258, 475, 259, 476, 479, 480,
215, 287, 382, 149, 150, 417, 471, 419, 469, 473,
418, 392, 398, 399, 400, 401, 402, 403, 404, 405,
406, 407, 408, 409, 410, 390, 391, 472, 450, 449,
448,32767,32767, 415, 416,32767,32767,32767,32767,32767,
32767,32767,32767, 102,32767, 420, 389, 423, 421, 422,
439, 440, 437, 438, 441,32767,32767,32767,32767, 442,
443, 444, 445, 316,32767,32767, 366, 364, 424, 316,
111,32767,32767,32767,32767,32767,32767,32767,32767,32767,
430, 431,32767,32767,32767,32767, 487, 538, 447,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767, 102,32767, 100, 540, 412, 414, 507, 425,
426, 393,32767, 514,32767, 102,32767, 516,32767,32767,
32767,32767,32767,32767,32767, 539,32767, 546, 546,32767,
500, 100, 195,32767,32767, 515,32767, 195, 195,32767,
32767,32767,32767,32767,32767,32767,32767, 609, 500, 110,
110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
32767, 195, 110,32767,32767,32767, 100, 195, 195, 195,
195, 195, 195, 195, 195, 195, 195, 190,32767, 268,
270, 102, 563, 195,32767, 519,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767, 512,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767, 500, 435, 138,32767, 138, 546, 427, 428,
429, 502, 546, 546, 546, 312, 289,32767,32767,32767,
32767, 517, 100, 100, 100, 100, 512,32767,32767,32767,
32767, 111, 486, 99, 99, 99, 99, 99, 103, 101,
32767,32767,32767,32767, 223,32767, 99,32767, 101, 101,
32767,32767, 223, 225, 212, 101, 227,32767, 567, 568,
223, 101, 227, 227, 227, 247, 247, 489, 318, 101,
99, 101, 101, 197, 318, 318,32767, 101, 489, 318,
489, 318, 199, 318, 318, 318, 489, 318,32767, 101,
318, 214, 99, 99, 318,32767,32767,32767, 502,32767,
32767,32767,32767,32767,32767,32767, 222,32767,32767,32767,
32767,32767,32767,32767,32767, 533,32767, 551, 565, 433,
434, 436, 550, 548, 458, 459, 460, 461, 462, 463,
464, 466, 597,32767, 506,32767,32767,32767, 338,32767,
607,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767, 608,32767,
546,32767,32767,32767,32767, 432, 9, 74, 495, 42,
43, 51, 57, 523, 524, 525, 526, 520, 521, 527,
522,32767,32767, 528, 573,32767,32767, 547, 600,32767,
32767,32767,32767,32767,32767, 138,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767, 533,32767, 136,
32767,32767,32767,32767,32767,32767,32767,32767, 529,32767,
32767,32767, 546,32767,32767,32767,32767, 314, 311,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767, 546,32767,32767,32767,32767,
32767, 291,32767, 308,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
286,32767,32767, 381, 502, 294, 296, 297,32767,32767,
32767,32767, 360,32767,32767,32767,32767,32767,32767,32767,
32767,32767,32767,32767, 152, 152, 3, 3, 341, 152,
152, 152, 341, 341, 152, 341, 341, 341, 152, 152,
152, 152, 152, 152, 280, 185, 262, 265, 247, 247,
152, 352, 152
);
protected array $goto = array(
196, 196, 1038, 1069, 701, 353, 433, 665, 856, 710,
427, 321, 316, 317, 337, 580, 432, 338, 434, 642,
658, 659, 421, 676, 677, 678, 857, 167, 167, 167,
167, 221, 197, 193, 193, 177, 179, 216, 193, 193,
193, 193, 193, 194, 194, 194, 194, 194, 194, 188,
189, 190, 191, 192, 218, 216, 219, 539, 540, 423,
541, 544, 545, 546, 547, 548, 549, 550, 551, 1140,
168, 169, 170, 195, 171, 172, 173, 166, 174, 175,
176, 178, 215, 217, 220, 238, 243, 244, 255, 257,
258, 259, 260, 261, 262, 263, 264, 269, 270, 271,
272, 278, 290, 291, 319, 320, 428, 429, 430, 585,
222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 180, 237, 181, 198, 199,
200, 239, 188, 189, 190, 191, 192, 218, 1140, 201,
182, 183, 184, 202, 198, 185, 240, 203, 201, 165,
204, 205, 186, 206, 207, 208, 187, 209, 210, 211,
212, 213, 214, 859, 613, 628, 631, 632, 633, 634,
655, 656, 657, 712, 460, 979, 280, 280, 280, 280,
479, 1321, 1322, 627, 627, 831, 604, 1276, 1276, 1276,
1276, 1276, 1276, 1276, 1276, 1276, 1276, 398, 401, 564,
605, 609, 890, 552, 552, 552, 552, 864, 608, 913,
908, 909, 922, 865, 910, 862, 911, 912, 863, 465,
441, 916, 1041, 1041, 685, 956, 1189, 357, 1033, 1049,
1050, 1091, 1086, 1087, 1088, 1295, 1295, 357, 357, 1295,
1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 698,
357, 357, 833, 917, 357, 918, 1363, 354, 355, 577,
1244, 698, 1244, 1244, 426, 698, 615, 558, 1038, 1038,
1244, 357, 357, 5, 1038, 6, 1038, 1038, 1038, 1038,
1038, 1038, 1038, 1038, 1038, 625, 662, 1038, 1038, 1038,
1038, 1328, 1328, 1328, 1328, 351, 1244, 356, 356, 356,
356, 1244, 1244, 1244, 1244, 1111, 1112, 1244, 1244, 1244,
344, 563, 556, 897, 855, 897, 897, 1336, 554, 1307,
554, 554, 482, 603, 1104, 930, 713, 1000, 554, 931,
484, 396, 946, 345, 344, 946, 511, 704, 872, 1102,
690, 343, 556, 563, 572, 573, 346, 583, 606, 620,
621, 575, 852, 884, 458, 664, 871, 22, 1137, 973,
973, 973, 973, 1044, 1043, 458, 967, 974, 1292, 1292,
558, 1062, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292,
1292, 1292, 543, 543, 1047, 1048, 543, 543, 543, 543,
543, 543, 543, 543, 543, 543, 570, 469, 469, 440,
737, 641, 643, 670, 852, 663, 469, 327, 311, 687,
691, 1014, 699, 708, 1010, 686, 1017, 1017, 1220, 948,
1323, 1324, 1221, 1224, 949, 1225, 849, 557, 567, 581,
618, 557, 339, 567, 877, 1237, 399, 464, 451, 451,
451, 451, 405, 1318, 837, 1318, 1318, 251, 251, 472,
584, 473, 474, 1318, 962, 1022, 882, 542, 542, 1354,
1355, 542, 874, 542, 542, 542, 542, 542, 542, 542,
542, 971, 412, 709, 249, 249, 249, 249, 246, 252,
1330, 1330, 1330, 1330, 837, 880, 837, 410, 411, 635,
637, 639, 674, 619, 675, 1075, 414, 415, 416, 1235,
688, 740, 886, 417, 1079, 0, 1314, 349, 435, 984,
885, 873, 1074, 1078, 435, 1122, 503, 0, 504, 1239,
1045, 1045, 982, 852, 510, 0, 0, 669, 1056, 1052,
1053, 0, 451, 451, 451, 451, 451, 451, 451, 451,
451, 451, 451, 935, 1127, 451, 972, 0, 1077, 0,
623, 0, 1316, 1316, 1077, 0, 1019, 0, 0, 326,
276, 326, 326, 0, 876, 1261, 668, 998, 1120, 889,
1346, 1346, 870, 1240, 1241, 1003, 0, 0, 975, 0,
736, 0, 847, 0, 1234, 0, 0, 1346, 555, 1012,
1007, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1242, 1304, 1305, 1349, 1349, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 254, 254
);
protected array $gotoCheck = array(
42, 42, 73, 127, 73, 97, 66, 66, 26, 9,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
86, 86, 43, 86, 86, 86, 27, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 15, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 83, 49, 23, 23, 23, 23,
178, 178, 178, 108, 108, 6, 130, 108, 108, 108,
108, 108, 108, 108, 108, 108, 108, 59, 59, 59,
59, 59, 45, 107, 107, 107, 107, 15, 107, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 151,
83, 15, 89, 89, 89, 89, 151, 14, 89, 89,
89, 15, 15, 15, 15, 172, 172, 14, 14, 172,
172, 172, 172, 172, 172, 172, 172, 172, 172, 7,
14, 14, 7, 65, 14, 65, 14, 97, 97, 174,
73, 7, 73, 73, 13, 7, 13, 14, 73, 73,
73, 14, 14, 46, 73, 46, 73, 73, 73, 73,
73, 73, 73, 73, 73, 56, 56, 73, 73, 73,
73, 9, 9, 9, 9, 181, 73, 24, 24, 24,
24, 73, 73, 73, 73, 144, 144, 73, 73, 73,
170, 76, 76, 25, 25, 25, 25, 183, 19, 14,
19, 19, 84, 8, 8, 73, 8, 103, 19, 73,
84, 62, 9, 170, 170, 9, 8, 8, 35, 8,
14, 76, 76, 76, 76, 76, 76, 76, 76, 76,
76, 104, 22, 35, 19, 64, 35, 76, 150, 19,
19, 19, 19, 118, 118, 19, 19, 19, 173, 173,
14, 114, 173, 173, 173, 173, 173, 173, 173, 173,
173, 173, 175, 175, 119, 119, 175, 175, 175, 175,
175, 175, 175, 175, 175, 175, 48, 149, 149, 113,
48, 48, 48, 120, 22, 48, 149, 171, 171, 48,
48, 48, 48, 48, 48, 116, 107, 107, 79, 79,
180, 180, 79, 79, 79, 79, 18, 9, 9, 2,
2, 9, 29, 9, 39, 14, 9, 9, 23, 23,
23, 23, 28, 130, 12, 130, 130, 5, 5, 9,
9, 9, 9, 130, 92, 110, 9, 158, 158, 9,
9, 158, 37, 158, 158, 158, 158, 158, 158, 158,
158, 93, 93, 93, 5, 5, 5, 5, 5, 5,
130, 130, 130, 130, 12, 9, 12, 82, 82, 85,
85, 85, 82, 80, 82, 129, 82, 82, 82, 162,
82, 99, 41, 82, 132, -1, 130, 82, 117, 96,
16, 16, 16, 16, 117, 147, 155, -1, 155, 20,
117, 117, 16, 22, 155, -1, -1, 117, 117, 117,
117, -1, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 17, 17, 23, 16, -1, 130, -1,
17, -1, 130, 130, 130, -1, 17, -1, -1, 24,
24, 24, 24, -1, 17, 20, 17, 17, 16, 16,
184, 184, 17, 20, 20, 50, -1, -1, 50, -1,
50, -1, 20, -1, 17, -1, -1, 184, 50, 50,
50, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 20, 20, 20, 184, 184, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, 5, 5
);
protected array $gotoBase = array(
0, 0, -287, 0, 0, 446, 165, 242, 315, -11,
0, 0, 145, -75, -73, -187, 56, 75, 114, 53,
124, 0, 72, 173, 294, 310, 4, 22, 103, 133,
0, 0, 0, 0, 0, -35, 0, 121, 0, 109,
0, 60, -1, 3, 0, 179, -467, 0, -319, 157,
563, 0, 0, 0, 0, 0, 245, 0, 0, 152,
0, 0, 289, 0, 113, 239, -235, 0, 0, 0,
0, 0, 0, -5, 0, 0, -36, 0, 0, 8,
147, -196, -7, -106, -150, 7, -702, 0, 0, -59,
0, 0, 123, 164, 0, 0, 65, -481, 0, 92,
0, 0, 0, 292, 308, 0, 0, 175, -58, 0,
83, 0, 0, 120, 97, 0, 132, 235, 82, 99,
111, 0, 0, 0, 0, 0, 0, 1, 0, 119,
178, 0, 61, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 29, 0, 0, 70, 0, 363,
112, -49, 0, 0, 0, 18, 0, 0, 216, 0,
0, 0, 128, 0, 0, 0, 0, 0, 0, 0,
10, 84, -6, 127, 230, 141, 0, 0, -123, 0,
46, 265, 0, 286, 260, 0, 0
);
protected array $gotoDefault = array(
-32768, 515, 744, 4, 745, 939, 820, 829, 601, 533,
711, 350, 629, 424, 1312, 915, 1126, 582, 848, 1253,
1227, 459, 851, 332, 734, 927, 898, 899, 402, 388,
394, 400, 653, 630, 497, 883, 455, 875, 489, 878,
454, 887, 164, 420, 513, 891, 3, 894, 561, 925,
977, 389, 902, 390, 681, 904, 566, 906, 907, 397,
403, 404, 1131, 574, 626, 919, 256, 568, 920, 387,
921, 929, 392, 395, 692, 468, 508, 502, 413, 1106,
569, 612, 650, 448, 476, 624, 636, 622, 483, 436,
418, 331, 961, 969, 490, 466, 983, 352, 991, 742,
1139, 644, 492, 999, 645, 1006, 1009, 534, 535, 481,
1021, 273, 1024, 493, 19, 671, 1035, 1036, 672, 646,
1058, 647, 673, 648, 1060, 475, 602, 1068, 456, 1076,
1300, 457, 1080, 266, 1083, 279, 419, 437, 1089, 1090,
9, 1096, 702, 703, 11, 277, 512, 1121, 693, 453,
1138, 452, 1208, 1210, 562, 494, 1228, 480, 296, 1231,
684, 509, 1236, 449, 1303, 450, 536, 477, 318, 537,
1347, 310, 335, 315, 553, 297, 336, 538, 478, 1309,
1317, 333, 31, 1337, 1348, 579, 617
);
protected array $ruleToNonTerminal = array(
0, 1, 3, 3, 2, 5, 5, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 7, 7, 7,
7, 7, 7, 7, 7, 8, 8, 9, 10, 11,
11, 11, 12, 12, 13, 13, 14, 15, 15, 16,
16, 17, 17, 18, 18, 21, 21, 22, 23, 23,
24, 24, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 29, 29, 30, 30, 32, 34, 34,
28, 36, 36, 33, 38, 38, 35, 35, 37, 37,
39, 39, 31, 40, 40, 41, 43, 44, 44, 45,
45, 46, 46, 48, 47, 47, 47, 47, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
49, 49, 25, 25, 50, 69, 69, 72, 72, 71,
70, 70, 63, 75, 75, 76, 76, 77, 77, 78,
78, 79, 79, 80, 80, 26, 26, 27, 27, 27,
27, 27, 88, 88, 90, 90, 83, 83, 91, 91,
92, 92, 92, 84, 84, 87, 87, 85, 85, 93,
94, 94, 57, 57, 65, 65, 68, 68, 68, 67,
95, 95, 96, 58, 58, 58, 58, 97, 97, 98,
98, 99, 99, 100, 101, 101, 102, 102, 103, 103,
55, 55, 51, 51, 105, 53, 53, 106, 52, 52,
54, 54, 64, 64, 64, 64, 81, 81, 109, 109,
111, 111, 112, 112, 112, 112, 110, 110, 110, 114,
114, 114, 114, 89, 89, 117, 117, 117, 118, 118,
115, 115, 119, 119, 121, 121, 122, 122, 116, 123,
123, 120, 124, 124, 124, 124, 113, 113, 82, 82,
82, 20, 20, 20, 126, 125, 125, 127, 127, 127,
127, 60, 128, 128, 129, 61, 131, 131, 132, 132,
133, 133, 86, 134, 134, 134, 134, 134, 134, 134,
139, 139, 140, 140, 141, 141, 141, 141, 141, 142,
143, 143, 138, 138, 135, 135, 137, 137, 145, 145,
144, 144, 144, 144, 144, 144, 144, 136, 146, 146,
148, 147, 147, 62, 104, 149, 149, 56, 56, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
42, 42, 42, 156, 158, 158, 159, 150, 150, 155,
155, 160, 161, 161, 162, 163, 164, 164, 164, 164,
19, 19, 73, 73, 73, 73, 151, 151, 151, 151,
166, 166, 152, 152, 154, 154, 154, 157, 157, 172,
172, 172, 172, 172, 172, 172, 172, 172, 173, 173,
173, 108, 175, 175, 175, 175, 153, 153, 153, 153,
153, 153, 153, 153, 59, 59, 169, 169, 169, 169,
169, 176, 176, 165, 165, 165, 165, 177, 177, 177,
177, 177, 177, 74, 74, 66, 66, 66, 66, 130,
130, 130, 130, 180, 179, 168, 168, 168, 168, 168,
168, 168, 167, 167, 167, 178, 178, 178, 178, 107,
174, 182, 182, 181, 181, 183, 183, 183, 183, 183,
183, 183, 183, 171, 171, 171, 171, 170, 185, 184,
184, 184, 184, 184, 184, 184, 184, 186, 186, 186,
186
);
protected array $ruleToLength = array(
1, 1, 2, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 1, 1, 2, 1, 3, 4, 1, 2,
0, 1, 1, 1, 1, 4, 3, 5, 4, 3,
4, 2, 3, 1, 1, 7, 6, 2, 3, 1,
2, 3, 1, 2, 3, 1, 1, 3, 1, 3,
1, 2, 2, 3, 1, 3, 2, 3, 1, 3,
3, 2, 0, 1, 1, 1, 1, 1, 3, 7,
10, 5, 7, 9, 5, 3, 3, 3, 3, 3,
3, 1, 2, 5, 7, 9, 6, 5, 6, 3,
2, 1, 1, 1, 1, 0, 2, 1, 3, 8,
0, 4, 2, 1, 3, 0, 1, 0, 1, 0,
1, 3, 1, 1, 1, 8, 9, 7, 8, 7,
6, 8, 0, 2, 0, 2, 1, 2, 1, 2,
1, 1, 1, 0, 2, 0, 2, 0, 2, 2,
1, 3, 1, 4, 1, 4, 1, 1, 4, 2,
1, 3, 3, 3, 4, 4, 5, 0, 2, 4,
3, 1, 1, 7, 0, 2, 1, 3, 3, 4,
1, 4, 0, 2, 5, 0, 2, 6, 0, 2,
0, 3, 1, 2, 1, 1, 2, 0, 1, 3,
0, 2, 1, 1, 1, 1, 6, 8, 6, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 3,
3, 3, 1, 3, 3, 3, 3, 3, 1, 3,
3, 1, 1, 2, 1, 1, 0, 1, 0, 2,
2, 2, 4, 3, 1, 1, 3, 1, 2, 2,
3, 2, 3, 1, 1, 2, 3, 1, 1, 3,
2, 0, 1, 5, 5, 6, 10, 3, 5, 1,
1, 3, 0, 2, 4, 5, 4, 4, 4, 3,
1, 1, 1, 1, 1, 1, 0, 1, 1, 2,
1, 1, 1, 1, 1, 1, 1, 2, 1, 3,
1, 1, 3, 2, 2, 3, 1, 0, 1, 1,
3, 3, 3, 4, 4, 1, 1, 2, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 2, 2, 2, 2, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 2, 2, 2, 2, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 5, 4,
3, 4, 4, 2, 2, 4, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 1, 3, 2,
1, 2, 4, 2, 2, 8, 9, 8, 9, 9,
10, 9, 10, 8, 3, 2, 2, 1, 1, 0,
4, 2, 1, 3, 2, 1, 2, 2, 2, 4,
1, 1, 1, 1, 1, 1, 1, 1, 3, 1,
1, 1, 0, 3, 0, 1, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 3, 5,
3, 3, 4, 1, 1, 3, 1, 1, 1, 1,
1, 3, 2, 3, 0, 1, 1, 3, 1, 1,
1, 1, 1, 1, 3, 1, 1, 1, 4, 4,
1, 4, 4, 0, 1, 1, 1, 3, 3, 1,
4, 2, 2, 1, 3, 1, 4, 4, 3, 3,
3, 3, 1, 3, 1, 1, 3, 1, 1, 4,
1, 1, 1, 3, 1, 1, 2, 1, 3, 4,
3, 2, 0, 2, 2, 1, 2, 1, 1, 1,
4, 3, 3, 3, 3, 6, 3, 1, 1, 2,
1
);
protected function initReduceCallbacks(): void {
$this->reduceCallbacks = [
0 => null,
1 => static function ($self, $stackPos) {
$self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]);
},
2 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
},
3 => static function ($self, $stackPos) {
$self->semValue = array();
},
4 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
5 => null,
6 => null,
7 => null,
8 => null,
9 => null,
10 => null,
11 => null,
12 => null,
13 => null,
14 => null,
15 => null,
16 => null,
17 => null,
18 => null,
19 => null,
20 => null,
21 => null,
22 => null,
23 => null,
24 => null,
25 => null,
26 => null,
27 => null,
28 => null,
29 => null,
30 => null,
31 => null,
32 => null,
33 => null,
34 => null,
35 => null,
36 => null,
37 => null,
38 => null,
39 => null,
40 => null,
41 => null,
42 => null,
43 => null,
44 => null,
45 => null,
46 => null,
47 => null,
48 => null,
49 => null,
50 => null,
51 => null,
52 => null,
53 => null,
54 => null,
55 => null,
56 => null,
57 => null,
58 => null,
59 => null,
60 => null,
61 => null,
62 => null,
63 => null,
64 => null,
65 => null,
66 => null,
67 => null,
68 => null,
69 => null,
70 => null,
71 => null,
72 => null,
73 => null,
74 => null,
75 => null,
76 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "<?=") $self->emitError(new Error('Cannot use "<?=" as an identifier', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
},
77 => null,
78 => null,
79 => null,
80 => null,
81 => null,
82 => null,
83 => null,
84 => null,
85 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
86 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
87 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
88 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
89 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
90 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
91 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
92 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
93 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
94 => null,
95 => static function ($self, $stackPos) {
$self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
96 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
97 => static function ($self, $stackPos) {
/* nothing */
},
98 => static function ($self, $stackPos) {
/* nothing */
},
99 => static function ($self, $stackPos) {
/* nothing */
},
100 => static function ($self, $stackPos) {
$self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])));
},
101 => null,
102 => null,
103 => static function ($self, $stackPos) {
$self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
104 => static function ($self, $stackPos) {
$self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
105 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
106 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
107 => static function ($self, $stackPos) {
$self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
108 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
109 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
110 => static function ($self, $stackPos) {
$self->semValue = [];
},
111 => null,
112 => null,
113 => null,
114 => null,
115 => static function ($self, $stackPos) {
$self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
116 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON);
$self->checkNamespace($self->semValue);
},
117 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
$self->checkNamespace($self->semValue);
},
118 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED);
$self->checkNamespace($self->semValue);
},
119 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
120 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
121 => null,
122 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
123 => static function ($self, $stackPos) {
$self->semValue = Stmt\Use_::TYPE_FUNCTION;
},
124 => static function ($self, $stackPos) {
$self->semValue = Stmt\Use_::TYPE_CONSTANT;
},
125 => static function ($self, $stackPos) {
$self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
126 => static function ($self, $stackPos) {
$self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
127 => null,
128 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
129 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
130 => null,
131 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
132 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
133 => null,
134 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
135 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
136 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
},
137 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
},
138 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1));
},
139 => static function ($self, $stackPos) {
$self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3));
},
140 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL;
},
141 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)];
},
142 => null,
143 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
144 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
145 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
146 => null,
147 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
148 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
149 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
150 => static function ($self, $stackPos) {
$self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
151 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];;
},
152 => static function ($self, $stackPos) {
$self->semValue = array();
},
153 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
154 => null,
155 => null,
156 => null,
157 => static function ($self, $stackPos) {
throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
158 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
159 => static function ($self, $stackPos) {
$self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
160 => static function ($self, $stackPos) {
$self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
161 => static function ($self, $stackPos) {
$self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
162 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
163 => static function ($self, $stackPos) {
$self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
164 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
165 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
166 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
167 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
168 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
169 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
170 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
171 => static function ($self, $stackPos) {
$self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
$self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1)));
},
172 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
173 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
174 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
175 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
176 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)], $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
177 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
178 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue);
},
179 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
180 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
181 => static function ($self, $stackPos) {
$self->semValue = null; /* means: no statement */
},
182 => null,
183 => static function ($self, $stackPos) {
$self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]);
},
184 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
},
185 => static function ($self, $stackPos) {
$self->semValue = array();
},
186 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
187 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
188 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
189 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
190 => static function ($self, $stackPos) {
$self->semValue = null;
},
191 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
192 => null,
193 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
194 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
195 => static function ($self, $stackPos) {
$self->semValue = false;
},
196 => static function ($self, $stackPos) {
$self->semValue = true;
},
197 => static function ($self, $stackPos) {
$self->semValue = false;
},
198 => static function ($self, $stackPos) {
$self->semValue = true;
},
199 => static function ($self, $stackPos) {
$self->semValue = false;
},
200 => static function ($self, $stackPos) {
$self->semValue = true;
},
201 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
202 => static function ($self, $stackPos) {
$self->semValue = [];
},
203 => null,
204 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
205 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
206 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
207 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
$self->checkClass($self->semValue, $stackPos-(7-2));
},
208 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
$self->checkClass($self->semValue, $stackPos-(8-3));
},
209 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
$self->checkInterface($self->semValue, $stackPos-(7-3));
},
210 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
211 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
$self->checkEnum($self->semValue, $stackPos-(8-3));
},
212 => static function ($self, $stackPos) {
$self->semValue = null;
},
213 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
214 => static function ($self, $stackPos) {
$self->semValue = null;
},
215 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
216 => static function ($self, $stackPos) {
$self->semValue = 0;
},
217 => null,
218 => null,
219 => static function ($self, $stackPos) {
$self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
220 => static function ($self, $stackPos) {
$self->semValue = Modifiers::ABSTRACT;
},
221 => static function ($self, $stackPos) {
$self->semValue = Modifiers::FINAL;
},
222 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
223 => static function ($self, $stackPos) {
$self->semValue = null;
},
224 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
225 => static function ($self, $stackPos) {
$self->semValue = array();
},
226 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
227 => static function ($self, $stackPos) {
$self->semValue = array();
},
228 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
229 => null,
230 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
231 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
232 => null,
233 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
234 => null,
235 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
236 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; };
},
237 => static function ($self, $stackPos) {
$self->semValue = null;
},
238 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
239 => null,
240 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
241 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
242 => static function ($self, $stackPos) {
$self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
243 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
244 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-3)];
},
245 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
246 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(5-3)];
},
247 => static function ($self, $stackPos) {
$self->semValue = array();
},
248 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
249 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
250 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
251 => null,
252 => null,
253 => static function ($self, $stackPos) {
$self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos]));
},
254 => static function ($self, $stackPos) {
$self->semValue = [];
},
255 => null,
256 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
257 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
258 => static function ($self, $stackPos) {
$self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
259 => static function ($self, $stackPos) {
$self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
260 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)];
},
261 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
262 => static function ($self, $stackPos) {
$self->semValue = array();
},
263 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
264 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
265 => static function ($self, $stackPos) {
$self->semValue = array();
},
266 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
267 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
},
268 => static function ($self, $stackPos) {
$self->semValue = null;
},
269 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
270 => static function ($self, $stackPos) {
$self->semValue = null;
},
271 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue);
},
272 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)], false);
},
273 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(2-2)], true);
},
274 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)], false);
},
275 => static function ($self, $stackPos) {
$self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false);
},
276 => null,
277 => static function ($self, $stackPos) {
$self->semValue = array();
},
278 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
279 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
280 => static function ($self, $stackPos) {
$self->semValue = 0;
},
281 => static function ($self, $stackPos) {
$self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
282 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PUBLIC;
},
283 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PROTECTED;
},
284 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PRIVATE;
},
285 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
286 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(6-6)], null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
$self->checkParam($self->semValue);
},
287 => static function ($self, $stackPos) {
$self->semValue = new Node\Param($self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-8)], $self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(8-2)], $self->semStack[$stackPos-(8-1)]);
$self->checkParam($self->semValue);
},
288 => static function ($self, $stackPos) {
$self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]);
},
289 => null,
290 => static function ($self, $stackPos) {
$self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
291 => static function ($self, $stackPos) {
$self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
292 => null,
293 => null,
294 => static function ($self, $stackPos) {
$self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
295 => static function ($self, $stackPos) {
$self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]);
},
296 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
297 => static function ($self, $stackPos) {
$self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
298 => null,
299 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
300 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
301 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
302 => null,
303 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
304 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
305 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
306 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
307 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
308 => static function ($self, $stackPos) {
$self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
309 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
310 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
311 => static function ($self, $stackPos) {
$self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
312 => null,
313 => static function ($self, $stackPos) {
$self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
314 => static function ($self, $stackPos) {
$self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
315 => null,
316 => static function ($self, $stackPos) {
$self->semValue = null;
},
317 => null,
318 => static function ($self, $stackPos) {
$self->semValue = null;
},
319 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(2-2)];
},
320 => static function ($self, $stackPos) {
$self->semValue = null;
},
321 => static function ($self, $stackPos) {
$self->semValue = array();
},
322 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-2)];
},
323 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-2)]);
},
324 => static function ($self, $stackPos) {
$self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
325 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
326 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
327 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
328 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
329 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
330 => static function ($self, $stackPos) {
$self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]);
},
331 => null,
332 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
333 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
334 => null,
335 => null,
336 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
337 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
338 => static function ($self, $stackPos) {
$self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
339 => static function ($self, $stackPos) {
$self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
340 => static function ($self, $stackPos) {
if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; }
},
341 => static function ($self, $stackPos) {
$self->semValue = array();
},
342 => static function ($self, $stackPos) {
$nop = $self->maybeCreateZeroLengthNop($self->tokenPos);;
if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)];
},
343 => static function ($self, $stackPos) {
$self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]);
$self->checkProperty($self->semValue, $stackPos-(5-2));
},
344 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]);
$self->checkClassConst($self->semValue, $stackPos-(5-2));
},
345 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]);
$self->checkClassConst($self->semValue, $stackPos-(6-2));
},
346 => static function ($self, $stackPos) {
$self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
$self->checkClassMethod($self->semValue, $stackPos-(10-2));
},
347 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
348 => static function ($self, $stackPos) {
$self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
349 => static function ($self, $stackPos) {
$self->semValue = null; /* will be skipped */
},
350 => static function ($self, $stackPos) {
$self->semValue = array();
},
351 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
352 => static function ($self, $stackPos) {
$self->semValue = array();
},
353 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
354 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
355 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
356 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
357 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
358 => static function ($self, $stackPos) {
$self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
359 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]);
},
360 => null,
361 => static function ($self, $stackPos) {
$self->semValue = array(null, $self->semStack[$stackPos-(1-1)]);
},
362 => static function ($self, $stackPos) {
$self->semValue = null;
},
363 => null,
364 => null,
365 => static function ($self, $stackPos) {
$self->semValue = 0;
},
366 => static function ($self, $stackPos) {
$self->semValue = 0;
},
367 => null,
368 => null,
369 => static function ($self, $stackPos) {
$self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)];
},
370 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PUBLIC;
},
371 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PROTECTED;
},
372 => static function ($self, $stackPos) {
$self->semValue = Modifiers::PRIVATE;
},
373 => static function ($self, $stackPos) {
$self->semValue = Modifiers::STATIC;
},
374 => static function ($self, $stackPos) {
$self->semValue = Modifiers::ABSTRACT;
},
375 => static function ($self, $stackPos) {
$self->semValue = Modifiers::FINAL;
},
376 => static function ($self, $stackPos) {
$self->semValue = Modifiers::READONLY;
},
377 => null,
378 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
379 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
380 => static function ($self, $stackPos) {
$self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
381 => static function ($self, $stackPos) {
$self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
382 => static function ($self, $stackPos) {
$self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
383 => null,
384 => null,
385 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
386 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
387 => static function ($self, $stackPos) {
$self->semValue = array();
},
388 => null,
389 => null,
390 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
391 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
392 => static function ($self, $stackPos) {
$self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
393 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
394 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
if (!$self->phpVersion->allowsAssignNewByReference()) {
$self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])));
}
},
395 => null,
396 => null,
397 => static function ($self, $stackPos) {
$self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
398 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
399 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
400 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
401 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
402 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
403 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
404 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
405 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
406 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
407 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
408 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
409 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
410 => static function ($self, $stackPos) {
$self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
411 => static function ($self, $stackPos) {
$self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
412 => static function ($self, $stackPos) {
$self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
413 => static function ($self, $stackPos) {
$self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
414 => static function ($self, $stackPos) {
$self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
415 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
416 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
417 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
418 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
419 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
420 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
421 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
422 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
423 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
424 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
425 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
426 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
427 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
428 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
429 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
430 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
431 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
432 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
433 => static function ($self, $stackPos) {
$self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
434 => static function ($self, $stackPos) {
$self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
435 => static function ($self, $stackPos) {
$self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
436 => static function ($self, $stackPos) {
$self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
437 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
438 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
439 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
440 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
441 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
442 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
443 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
444 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
445 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
446 => static function ($self, $stackPos) {
$self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
447 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
448 => static function ($self, $stackPos) {
$self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
449 => static function ($self, $stackPos) {
$self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
450 => static function ($self, $stackPos) {
$self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
451 => static function ($self, $stackPos) {
$self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
452 => static function ($self, $stackPos) {
$self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
453 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
454 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
455 => static function ($self, $stackPos) {
$self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
456 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
457 => static function ($self, $stackPos) {
$self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
458 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
459 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
$attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]);
$self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs);
},
460 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
461 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
462 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
463 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
464 => static function ($self, $stackPos) {
$self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
465 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]);
$attrs['kind'] = strtolower($self->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
$self->semValue = new Expr\Exit_($self->semStack[$stackPos-(2-2)], $attrs);
},
466 => static function ($self, $stackPos) {
$self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
467 => null,
468 => static function ($self, $stackPos) {
$self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
469 => static function ($self, $stackPos) {
$self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
470 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
471 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
472 => static function ($self, $stackPos) {
$self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
473 => static function ($self, $stackPos) {
$self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
474 => static function ($self, $stackPos) {
$self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
475 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
476 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
477 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]));
},
478 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
479 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
480 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
481 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos]));
},
482 => static function ($self, $stackPos) {
$self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos]));
},
483 => static function ($self, $stackPos) {
$self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]);
$self->checkClass($self->semValue[0], -1);
},
484 => static function ($self, $stackPos) {
$self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
485 => static function ($self, $stackPos) {
list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
486 => static function ($self, $stackPos) {
$self->semValue = new Expr\New_($self->semStack[$stackPos-(2-2)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
487 => null,
488 => null,
489 => static function ($self, $stackPos) {
$self->semValue = array();
},
490 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(4-3)];
},
491 => null,
492 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
493 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
494 => static function ($self, $stackPos) {
$self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
495 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
496 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
497 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
498 => static function ($self, $stackPos) {
$self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
499 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
500 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
501 => null,
502 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
503 => static function ($self, $stackPos) {
$self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
504 => static function ($self, $stackPos) {
$self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
505 => static function ($self, $stackPos) {
$self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
506 => null,
507 => null,
508 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
509 => static function ($self, $stackPos) {
$self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
510 => null,
511 => null,
512 => static function ($self, $stackPos) {
$self->semValue = null;
},
513 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
514 => static function ($self, $stackPos) {
$self->semValue = array();
},
515 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } };
},
516 => static function ($self, $stackPos) {
foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)];
},
517 => static function ($self, $stackPos) {
$self->semValue = array();
},
518 => null,
519 => static function ($self, $stackPos) {
$self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
520 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
521 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
522 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
523 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
524 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
525 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
526 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
527 => static function ($self, $stackPos) {
$self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
528 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
529 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]));
},
530 => static function ($self, $stackPos) {
$self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
531 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT;
$self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs);
},
532 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG;
$self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs);
$self->createdArrays->attach($self->semValue);
},
533 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->attach($self->semValue);
},
534 => static function ($self, $stackPos) {
$self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes());
},
535 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs);
},
536 => static function ($self, $stackPos) {
$self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals());
},
537 => static function ($self, $stackPos) {
$self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
538 => null,
539 => null,
540 => null,
541 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true);
},
542 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)], $self->tokenEndStack[$stackPos-(2-2)]), true);
},
543 => static function ($self, $stackPos) {
$self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true);
},
544 => static function ($self, $stackPos) {
$self->semValue = null;
},
545 => null,
546 => null,
547 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
548 => null,
549 => null,
550 => null,
551 => null,
552 => null,
553 => null,
554 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
555 => null,
556 => null,
557 => null,
558 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
559 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
560 => null,
561 => static function ($self, $stackPos) {
$self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
562 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
563 => static function ($self, $stackPos) {
$self->semValue = null;
},
564 => null,
565 => null,
566 => null,
567 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
568 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
569 => null,
570 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
571 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
572 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
573 => static function ($self, $stackPos) {
$var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var;
},
574 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
575 => null,
576 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
577 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
578 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
579 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
580 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
581 => static function ($self, $stackPos) {
$self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
582 => null,
583 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
584 => null,
585 => null,
586 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
587 => null,
588 => static function ($self, $stackPos) {
$self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2;
},
589 => static function ($self, $stackPos) {
$self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST);
$self->postprocessList($self->semValue);
},
590 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue);
},
591 => null,
592 => static function ($self, $stackPos) {
/* do nothing -- prevent default action of $$=$self->semStack[$1]. See $551. */
},
593 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)];
},
594 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
595 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
596 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
597 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
598 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
599 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
600 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
601 => static function ($self, $stackPos) {
$self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true);
},
602 => static function ($self, $stackPos) {
/* Create an Error node now to remember the position. We'll later either report an error,
or convert this into a null element, depending on whether this is a creation or destructuring context. */
$attrs = $self->createEmptyElemAttributes($self->tokenPos);
$self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs);
},
603 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
604 => static function ($self, $stackPos) {
$self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)];
},
605 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(1-1)]);
},
606 => static function ($self, $stackPos) {
$self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]);
},
607 => static function ($self, $stackPos) {
$attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs);
},
608 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
609 => null,
610 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]));
},
611 => static function ($self, $stackPos) {
$self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
612 => static function ($self, $stackPos) {
$self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
613 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
614 => static function ($self, $stackPos) {
$self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]));
},
615 => static function ($self, $stackPos) {
$self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]));
},
616 => static function ($self, $stackPos) {
$self->semValue = $self->semStack[$stackPos-(3-2)];
},
617 => static function ($self, $stackPos) {
$self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
618 => static function ($self, $stackPos) {
$self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]));
},
619 => static function ($self, $stackPos) {
$self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]));
},
620 => null,
];
}
}

View File

@ -0,0 +1,1241 @@
<?php declare(strict_types=1);
namespace PhpParser;
/*
* This parser is based on a skeleton written by Moriyoshi Koizumi, which in
* turn is based on work by Masato Bito.
*/
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Cast\Double;
use PhpParser\Node\Identifier;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Enum_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\TryCatch;
use PhpParser\Node\UseItem;
use PhpParser\NodeVisitor\CommentAnnotatingVisitor;
abstract class ParserAbstract implements Parser {
private const SYMBOL_NONE = -1;
/** @var Lexer Lexer that is used when parsing */
protected Lexer $lexer;
/** @var PhpVersion PHP version to target on a best-effort basis */
protected PhpVersion $phpVersion;
/*
* The following members will be filled with generated parsing data:
*/
/** @var int Size of $tokenToSymbol map */
protected int $tokenToSymbolMapSize;
/** @var int Size of $action table */
protected int $actionTableSize;
/** @var int Size of $goto table */
protected int $gotoTableSize;
/** @var int Symbol number signifying an invalid token */
protected int $invalidSymbol;
/** @var int Symbol number of error recovery token */
protected int $errorSymbol;
/** @var int Action number signifying default action */
protected int $defaultAction;
/** @var int Rule number signifying that an unexpected token was encountered */
protected int $unexpectedTokenRule;
protected int $YY2TBLSTATE;
/** @var int Number of non-leaf states */
protected int $numNonLeafStates;
/** @var int[] Map of PHP token IDs to internal symbols */
protected array $phpTokenToSymbol;
/** @var array<int, bool> Map of PHP token IDs to drop */
protected array $dropTokens;
/** @var int[] Map of external symbols (static::T_*) to internal symbols */
protected array $tokenToSymbol;
/** @var string[] Map of symbols to their names */
protected array $symbolToName;
/** @var array<int, string> Names of the production rules (only necessary for debugging) */
protected array $productions;
/** @var int[] Map of states to a displacement into the $action table. The corresponding action for this
* state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the
* action is defaulted, i.e. $actionDefault[$state] should be used instead. */
protected array $actionBase;
/** @var int[] Table of actions. Indexed according to $actionBase comment. */
protected array $action;
/** @var int[] Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol
* then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */
protected array $actionCheck;
/** @var int[] Map of states to their default action */
protected array $actionDefault;
/** @var callable[] Semantic action callbacks */
protected array $reduceCallbacks;
/** @var int[] Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this
* non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */
protected array $gotoBase;
/** @var int[] Table of states to goto after reduction. Indexed according to $gotoBase comment. */
protected array $goto;
/** @var int[] Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal
* then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */
protected array $gotoCheck;
/** @var int[] Map of non-terminals to the default state to goto after their reduction */
protected array $gotoDefault;
/** @var int[] Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for
* determining the state to goto after reduction. */
protected array $ruleToNonTerminal;
/** @var int[] Map of rules to the length of their right-hand side, which is the number of elements that have to
* be popped from the stack(s) on reduction. */
protected array $ruleToLength;
/*
* The following members are part of the parser state:
*/
/** @var mixed Temporary value containing the result of last semantic action (reduction) */
protected $semValue;
/** @var mixed[] Semantic value stack (contains values of tokens and semantic action results) */
protected array $semStack;
/** @var int[] Token start position stack */
protected array $tokenStartStack;
/** @var int[] Token end position stack */
protected array $tokenEndStack;
/** @var ErrorHandler Error handler */
protected ErrorHandler $errorHandler;
/** @var int Error state, used to avoid error floods */
protected int $errorState;
/** @var \SplObjectStorage<Array_, null>|null Array nodes created during parsing, for postprocessing of empty elements. */
protected ?\SplObjectStorage $createdArrays;
/** @var Token[] Tokens for the current parse */
protected array $tokens;
/** @var int Current position in token array */
protected int $tokenPos;
/**
* Initialize $reduceCallbacks map.
*/
abstract protected function initReduceCallbacks(): void;
/**
* Creates a parser instance.
*
* Options:
* * phpVersion: ?PhpVersion,
*
* @param Lexer $lexer A lexer
* @param PhpVersion $phpVersion PHP version to target, defaults to latest supported. This
* option is best-effort: Even if specified, parsing will generally assume the latest
* supported version and only adjust behavior in minor ways, for example by omitting
* errors in older versions and interpreting type hints as a name or identifier depending
* on version.
*/
public function __construct(Lexer $lexer, ?PhpVersion $phpVersion = null) {
$this->lexer = $lexer;
$this->phpVersion = $phpVersion ?? PhpVersion::getNewestSupported();
$this->initReduceCallbacks();
$this->phpTokenToSymbol = $this->createTokenMap();
$this->dropTokens = array_fill_keys(
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], true
);
}
/**
* Parses PHP code into a node tree.
*
* If a non-throwing error handler is used, the parser will continue parsing after an error
* occurred and attempt to build a partial AST.
*
* @param string $code The source code to parse
* @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
* to ErrorHandler\Throwing.
*
* @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and
* the parser was unable to recover from an error).
*/
public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array {
$this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing();
$this->createdArrays = new \SplObjectStorage();
$this->tokens = $this->lexer->tokenize($code, $this->errorHandler);
$result = $this->doParse();
// Report errors for any empty elements used inside arrays. This is delayed until after the main parse,
// because we don't know a priori whether a given array expression will be used in a destructuring context
// or not.
foreach ($this->createdArrays as $node) {
foreach ($node->items as $item) {
if ($item->value instanceof Expr\Error) {
$this->errorHandler->handleError(
new Error('Cannot use empty array elements in arrays', $item->getAttributes()));
}
}
}
// Clear out some of the interior state, so we don't hold onto unnecessary
// memory between uses of the parser
$this->tokenStartStack = [];
$this->tokenEndStack = [];
$this->semStack = [];
$this->semValue = null;
$this->createdArrays = null;
if ($result !== null) {
$traverser = new NodeTraverser(new CommentAnnotatingVisitor($this->tokens));
$traverser->traverse($result);
}
return $result;
}
public function getTokens(): array {
return $this->tokens;
}
/** @return Stmt[]|null */
protected function doParse(): ?array {
// We start off with no lookahead-token
$symbol = self::SYMBOL_NONE;
$tokenValue = null;
$this->tokenPos = -1;
// Keep stack of start and end attributes
$this->tokenStartStack = [];
$this->tokenEndStack = [0];
// Start off in the initial state and keep a stack of previous states
$state = 0;
$stateStack = [$state];
// Semantic value stack (contains values of tokens and semantic action results)
$this->semStack = [];
// Current position in the stack(s)
$stackPos = 0;
$this->errorState = 0;
for (;;) {
//$this->traceNewState($state, $symbol);
if ($this->actionBase[$state] === 0) {
$rule = $this->actionDefault[$state];
} else {
if ($symbol === self::SYMBOL_NONE) {
do {
$token = $this->tokens[++$this->tokenPos];
$tokenId = $token->id;
} while (isset($this->dropTokens[$tokenId]));
// Map the lexer token id to the internally used symbols.
$tokenValue = $token->text;
if (!isset($this->phpTokenToSymbol[$tokenId])) {
throw new \RangeException(sprintf(
'The lexer returned an invalid token (id=%d, value=%s)',
$tokenId, $tokenValue
));
}
$symbol = $this->phpTokenToSymbol[$tokenId];
//$this->traceRead($symbol);
}
$idx = $this->actionBase[$state] + $symbol;
if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol)
|| ($state < $this->YY2TBLSTATE
&& ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0
&& $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol))
&& ($action = $this->action[$idx]) !== $this->defaultAction) {
/*
* >= numNonLeafStates: shift and reduce
* > 0: shift
* = 0: accept
* < 0: reduce
* = -YYUNEXPECTED: error
*/
if ($action > 0) {
/* shift */
//$this->traceShift($symbol);
++$stackPos;
$stateStack[$stackPos] = $state = $action;
$this->semStack[$stackPos] = $tokenValue;
$this->tokenStartStack[$stackPos] = $this->tokenPos;
$this->tokenEndStack[$stackPos] = $this->tokenPos;
$symbol = self::SYMBOL_NONE;
if ($this->errorState) {
--$this->errorState;
}
if ($action < $this->numNonLeafStates) {
continue;
}
/* $yyn >= numNonLeafStates means shift-and-reduce */
$rule = $action - $this->numNonLeafStates;
} else {
$rule = -$action;
}
} else {
$rule = $this->actionDefault[$state];
}
}
for (;;) {
if ($rule === 0) {
/* accept */
//$this->traceAccept();
return $this->semValue;
}
if ($rule !== $this->unexpectedTokenRule) {
/* reduce */
//$this->traceReduce($rule);
$ruleLength = $this->ruleToLength[$rule];
try {
$callback = $this->reduceCallbacks[$rule];
if ($callback !== null) {
$callback($this, $stackPos);
} elseif ($ruleLength > 0) {
$this->semValue = $this->semStack[$stackPos - $ruleLength + 1];
}
} catch (Error $e) {
if (-1 === $e->getStartLine()) {
$e->setStartLine($this->tokens[$this->tokenPos]->line);
}
$this->emitError($e);
// Can't recover from this type of error
return null;
}
/* Goto - shift nonterminal */
$lastTokenEnd = $this->tokenEndStack[$stackPos];
$stackPos -= $ruleLength;
$nonTerminal = $this->ruleToNonTerminal[$rule];
$idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos];
if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) {
$state = $this->goto[$idx];
} else {
$state = $this->gotoDefault[$nonTerminal];
}
++$stackPos;
$stateStack[$stackPos] = $state;
$this->semStack[$stackPos] = $this->semValue;
$this->tokenEndStack[$stackPos] = $lastTokenEnd;
if ($ruleLength === 0) {
// Empty productions use the start attributes of the lookahead token.
$this->tokenStartStack[$stackPos] = $this->tokenPos;
}
} else {
/* error */
switch ($this->errorState) {
case 0:
$msg = $this->getErrorMessage($symbol, $state);
$this->emitError(new Error($msg, $this->getAttributesForToken($this->tokenPos)));
// Break missing intentionally
// no break
case 1:
case 2:
$this->errorState = 3;
// Pop until error-expecting state uncovered
while (!(
(($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0
&& $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol)
|| ($state < $this->YY2TBLSTATE
&& ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0
&& $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol)
) || ($action = $this->action[$idx]) === $this->defaultAction) { // Not totally sure about this
if ($stackPos <= 0) {
// Could not recover from error
return null;
}
$state = $stateStack[--$stackPos];
//$this->tracePop($state);
}
//$this->traceShift($this->errorSymbol);
++$stackPos;
$stateStack[$stackPos] = $state = $action;
// We treat the error symbol as being empty, so we reset the end attributes
// to the end attributes of the last non-error symbol
$this->tokenStartStack[$stackPos] = $this->tokenPos;
$this->tokenEndStack[$stackPos] = $this->tokenEndStack[$stackPos - 1];
break;
case 3:
if ($symbol === 0) {
// Reached EOF without recovering from error
return null;
}
//$this->traceDiscard($symbol);
$symbol = self::SYMBOL_NONE;
break 2;
}
}
if ($state < $this->numNonLeafStates) {
break;
}
/* >= numNonLeafStates means shift-and-reduce */
$rule = $state - $this->numNonLeafStates;
}
}
throw new \RuntimeException('Reached end of parser loop');
}
protected function emitError(Error $error): void {
$this->errorHandler->handleError($error);
}
/**
* Format error message including expected tokens.
*
* @param int $symbol Unexpected symbol
* @param int $state State at time of error
*
* @return string Formatted error message
*/
protected function getErrorMessage(int $symbol, int $state): string {
$expectedString = '';
if ($expected = $this->getExpectedTokens($state)) {
$expectedString = ', expecting ' . implode(' or ', $expected);
}
return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString;
}
/**
* Get limited number of expected tokens in given state.
*
* @param int $state State
*
* @return string[] Expected tokens. If too many, an empty array is returned.
*/
protected function getExpectedTokens(int $state): array {
$expected = [];
$base = $this->actionBase[$state];
foreach ($this->symbolToName as $symbol => $name) {
$idx = $base + $symbol;
if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
|| $state < $this->YY2TBLSTATE
&& ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0
&& $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
) {
if ($this->action[$idx] !== $this->unexpectedTokenRule
&& $this->action[$idx] !== $this->defaultAction
&& $symbol !== $this->errorSymbol
) {
if (count($expected) === 4) {
/* Too many expected tokens */
return [];
}
$expected[] = $name;
}
}
}
return $expected;
}
/**
* Get attributes for a node with the given start and end token positions.
*
* @param int $tokenStartPos Token position the node starts at
* @param int $tokenEndPos Token position the node ends at
* @return array<string, mixed> Attributes
*/
protected function getAttributes(int $tokenStartPos, int $tokenEndPos): array {
$startToken = $this->tokens[$tokenStartPos];
$afterEndToken = $this->tokens[$tokenEndPos + 1];
return [
'startLine' => $startToken->line,
'startTokenPos' => $tokenStartPos,
'startFilePos' => $startToken->pos,
'endLine' => $afterEndToken->line,
'endTokenPos' => $tokenEndPos,
'endFilePos' => $afterEndToken->pos - 1,
];
}
/**
* Get attributes for a single token at the given token position.
*
* @return array<string, mixed> Attributes
*/
protected function getAttributesForToken(int $tokenPos): array {
if ($tokenPos < \count($this->tokens) - 1) {
return $this->getAttributes($tokenPos, $tokenPos);
}
// Get attributes for the sentinel token.
$token = $this->tokens[$tokenPos];
return [
'startLine' => $token->line,
'startTokenPos' => $tokenPos,
'startFilePos' => $token->pos,
'endLine' => $token->line,
'endTokenPos' => $tokenPos,
'endFilePos' => $token->pos,
];
}
/*
* Tracing functions used for debugging the parser.
*/
/*
protected function traceNewState($state, $symbol): void {
echo '% State ' . $state
. ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
}
protected function traceRead($symbol): void {
echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
}
protected function traceShift($symbol): void {
echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
}
protected function traceAccept(): void {
echo "% Accepted.\n";
}
protected function traceReduce($n): void {
echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
}
protected function tracePop($state): void {
echo '% Recovering, uncovered state ' . $state . "\n";
}
protected function traceDiscard($symbol): void {
echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
}
*/
/*
* Helper functions invoked by semantic actions
*/
/**
* Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions.
*
* @param Node\Stmt[] $stmts
* @return Node\Stmt[]
*/
protected function handleNamespaces(array $stmts): array {
$hasErrored = false;
$style = $this->getNamespacingStyle($stmts);
if (null === $style) {
// not namespaced, nothing to do
return $stmts;
}
if ('brace' === $style) {
// For braced namespaces we only have to check that there are no invalid statements between the namespaces
$afterFirstNamespace = false;
foreach ($stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Namespace_) {
$afterFirstNamespace = true;
} elseif (!$stmt instanceof Node\Stmt\HaltCompiler
&& !$stmt instanceof Node\Stmt\Nop
&& $afterFirstNamespace && !$hasErrored) {
$this->emitError(new Error(
'No code may exist outside of namespace {}', $stmt->getAttributes()));
$hasErrored = true; // Avoid one error for every statement
}
}
return $stmts;
} else {
// For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts
$resultStmts = [];
$targetStmts = &$resultStmts;
$lastNs = null;
foreach ($stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Namespace_) {
if ($lastNs !== null) {
$this->fixupNamespaceAttributes($lastNs);
}
if ($stmt->stmts === null) {
$stmt->stmts = [];
$targetStmts = &$stmt->stmts;
$resultStmts[] = $stmt;
} else {
// This handles the invalid case of mixed style namespaces
$resultStmts[] = $stmt;
$targetStmts = &$resultStmts;
}
$lastNs = $stmt;
} elseif ($stmt instanceof Node\Stmt\HaltCompiler) {
// __halt_compiler() is not moved into the namespace
$resultStmts[] = $stmt;
} else {
$targetStmts[] = $stmt;
}
}
if ($lastNs !== null) {
$this->fixupNamespaceAttributes($lastNs);
}
return $resultStmts;
}
}
private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt): void {
// We moved the statements into the namespace node, as such the end of the namespace node
// needs to be extended to the end of the statements.
if (empty($stmt->stmts)) {
return;
}
// We only move the builtin end attributes here. This is the best we can do with the
// knowledge we have.
$endAttributes = ['endLine', 'endFilePos', 'endTokenPos'];
$lastStmt = $stmt->stmts[count($stmt->stmts) - 1];
foreach ($endAttributes as $endAttribute) {
if ($lastStmt->hasAttribute($endAttribute)) {
$stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute));
}
}
}
/** @return array<string, mixed> */
private function getNamespaceErrorAttributes(Namespace_ $node): array {
$attrs = $node->getAttributes();
// Adjust end attributes to only cover the "namespace" keyword, not the whole namespace.
if (isset($attrs['startLine'])) {
$attrs['endLine'] = $attrs['startLine'];
}
if (isset($attrs['startTokenPos'])) {
$attrs['endTokenPos'] = $attrs['startTokenPos'];
}
if (isset($attrs['startFilePos'])) {
$attrs['endFilePos'] = $attrs['startFilePos'] + \strlen('namespace') - 1;
}
return $attrs;
}
/**
* Determine namespacing style (semicolon or brace)
*
* @param Node[] $stmts Top-level statements.
*
* @return null|string One of "semicolon", "brace" or null (no namespaces)
*/
private function getNamespacingStyle(array $stmts): ?string {
$style = null;
$hasNotAllowedStmts = false;
foreach ($stmts as $i => $stmt) {
if ($stmt instanceof Node\Stmt\Namespace_) {
$currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace';
if (null === $style) {
$style = $currentStyle;
if ($hasNotAllowedStmts) {
$this->emitError(new Error(
'Namespace declaration statement has to be the very first statement in the script',
$this->getNamespaceErrorAttributes($stmt)
));
}
} elseif ($style !== $currentStyle) {
$this->emitError(new Error(
'Cannot mix bracketed namespace declarations with unbracketed namespace declarations',
$this->getNamespaceErrorAttributes($stmt)
));
// Treat like semicolon style for namespace normalization
return 'semicolon';
}
continue;
}
/* declare(), __halt_compiler() and nops can be used before a namespace declaration */
if ($stmt instanceof Node\Stmt\Declare_
|| $stmt instanceof Node\Stmt\HaltCompiler
|| $stmt instanceof Node\Stmt\Nop) {
continue;
}
/* There may be a hashbang line at the very start of the file */
if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) {
continue;
}
/* Everything else if forbidden before namespace declarations */
$hasNotAllowedStmts = true;
}
return $style;
}
/** @return Name|Identifier */
protected function handleBuiltinTypes(Name $name) {
if (!$name->isUnqualified()) {
return $name;
}
$lowerName = $name->toLowerString();
if (!$this->phpVersion->supportsBuiltinType($lowerName)) {
return $name;
}
return new Node\Identifier($lowerName, $name->getAttributes());
}
/**
* Get combined start and end attributes at a stack location
*
* @param int $stackPos Stack location
*
* @return array<string, mixed> Combined start and end attributes
*/
protected function getAttributesAt(int $stackPos): array {
return $this->getAttributes($this->tokenStartStack[$stackPos], $this->tokenEndStack[$stackPos]);
}
protected function getFloatCastKind(string $cast): int {
$cast = strtolower($cast);
if (strpos($cast, 'float') !== false) {
return Double::KIND_FLOAT;
}
if (strpos($cast, 'real') !== false) {
return Double::KIND_REAL;
}
return Double::KIND_DOUBLE;
}
/** @param array<string, mixed> $attributes */
protected function parseLNumber(string $str, array $attributes, bool $allowInvalidOctal = false): Int_ {
try {
return Int_::fromString($str, $attributes, $allowInvalidOctal);
} catch (Error $error) {
$this->emitError($error);
// Use dummy value
return new Int_(0, $attributes);
}
}
/**
* Parse a T_NUM_STRING token into either an integer or string node.
*
* @param string $str Number string
* @param array<string, mixed> $attributes Attributes
*
* @return Int_|String_ Integer or string node.
*/
protected function parseNumString(string $str, array $attributes) {
if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) {
return new String_($str, $attributes);
}
$num = +$str;
if (!is_int($num)) {
return new String_($str, $attributes);
}
return new Int_($num, $attributes);
}
/** @param array<string, mixed> $attributes */
protected function stripIndentation(
string $string, int $indentLen, string $indentChar,
bool $newlineAtStart, bool $newlineAtEnd, array $attributes
): string {
if ($indentLen === 0) {
return $string;
}
$start = $newlineAtStart ? '(?:(?<=\n)|\A)' : '(?<=\n)';
$end = $newlineAtEnd ? '(?:(?=[\r\n])|\z)' : '(?=[\r\n])';
$regex = '/' . $start . '([ \t]*)(' . $end . ')?/';
return preg_replace_callback(
$regex,
function ($matches) use ($indentLen, $indentChar, $attributes) {
$prefix = substr($matches[1], 0, $indentLen);
if (false !== strpos($prefix, $indentChar === " " ? "\t" : " ")) {
$this->emitError(new Error(
'Invalid indentation - tabs and spaces cannot be mixed', $attributes
));
} elseif (strlen($prefix) < $indentLen && !isset($matches[2])) {
$this->emitError(new Error(
'Invalid body indentation level ' .
'(expecting an indentation level of at least ' . $indentLen . ')',
$attributes
));
}
return substr($matches[0], strlen($prefix));
},
$string
);
}
/**
* @param string|(Expr|InterpolatedStringPart)[] $contents
* @param array<string, mixed> $attributes
* @param array<string, mixed> $endTokenAttributes
*/
protected function parseDocString(
string $startToken, $contents, string $endToken,
array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape
): Expr {
$kind = strpos($startToken, "'") === false
? String_::KIND_HEREDOC : String_::KIND_NOWDOC;
$regex = '/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/';
$result = preg_match($regex, $startToken, $matches);
assert($result === 1);
$label = $matches[1];
$result = preg_match('/\A[ \t]*/', $endToken, $matches);
assert($result === 1);
$indentation = $matches[0];
$attributes['kind'] = $kind;
$attributes['docLabel'] = $label;
$attributes['docIndentation'] = $indentation;
$indentHasSpaces = false !== strpos($indentation, " ");
$indentHasTabs = false !== strpos($indentation, "\t");
if ($indentHasSpaces && $indentHasTabs) {
$this->emitError(new Error(
'Invalid indentation - tabs and spaces cannot be mixed',
$endTokenAttributes
));
// Proceed processing as if this doc string is not indented
$indentation = '';
}
$indentLen = \strlen($indentation);
$indentChar = $indentHasSpaces ? " " : "\t";
if (\is_string($contents)) {
if ($contents === '') {
$attributes['rawValue'] = $contents;
return new String_('', $attributes);
}
$contents = $this->stripIndentation(
$contents, $indentLen, $indentChar, true, true, $attributes
);
$contents = preg_replace('~(\r\n|\n|\r)\z~', '', $contents);
$attributes['rawValue'] = $contents;
if ($kind === String_::KIND_HEREDOC) {
$contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape);
}
return new String_($contents, $attributes);
} else {
assert(count($contents) > 0);
if (!$contents[0] instanceof Node\InterpolatedStringPart) {
// If there is no leading encapsed string part, pretend there is an empty one
$this->stripIndentation(
'', $indentLen, $indentChar, true, false, $contents[0]->getAttributes()
);
}
$newContents = [];
foreach ($contents as $i => $part) {
if ($part instanceof Node\InterpolatedStringPart) {
$isLast = $i === \count($contents) - 1;
$part->value = $this->stripIndentation(
$part->value, $indentLen, $indentChar,
$i === 0, $isLast, $part->getAttributes()
);
if ($isLast) {
$part->value = preg_replace('~(\r\n|\n|\r)\z~', '', $part->value);
}
$part->setAttribute('rawValue', $part->value);
$part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape);
if ('' === $part->value) {
continue;
}
}
$newContents[] = $part;
}
return new InterpolatedString($newContents, $attributes);
}
}
protected function createCommentFromToken(Token $token, int $tokenPos): Comment {
assert($token->id === \T_COMMENT || $token->id == \T_DOC_COMMENT);
return \T_DOC_COMMENT === $token->id
? new Comment\Doc($token->text, $token->line, $token->pos, $tokenPos,
$token->getEndLine(), $token->getEndPos() - 1, $tokenPos)
: new Comment($token->text, $token->line, $token->pos, $tokenPos,
$token->getEndLine(), $token->getEndPos() - 1, $tokenPos);
}
/**
* Get last comment before the given token position, if any
*/
protected function getCommentBeforeToken(int $tokenPos): ?Comment {
while (--$tokenPos >= 0) {
$token = $this->tokens[$tokenPos];
if (!isset($this->dropTokens[$token->id])) {
break;
}
if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) {
return $this->createCommentFromToken($token, $tokenPos);
}
}
return null;
}
/**
* Create a zero-length nop to capture preceding comments, if any.
*/
protected function maybeCreateZeroLengthNop(int $tokenPos): ?Nop {
$comment = $this->getCommentBeforeToken($tokenPos);
if ($comment === null) {
return null;
}
$commentEndLine = $comment->getEndLine();
$commentEndFilePos = $comment->getEndFilePos();
$commentEndTokenPos = $comment->getEndTokenPos();
$attributes = [
'startLine' => $commentEndLine,
'endLine' => $commentEndLine,
'startFilePos' => $commentEndFilePos + 1,
'endFilePos' => $commentEndFilePos,
'startTokenPos' => $commentEndTokenPos + 1,
'endTokenPos' => $commentEndTokenPos,
];
return new Nop($attributes);
}
protected function maybeCreateNop(int $tokenStartPos, int $tokenEndPos): ?Nop {
if ($this->getCommentBeforeToken($tokenStartPos) === null) {
return null;
}
return new Nop($this->getAttributes($tokenStartPos, $tokenEndPos));
}
protected function handleHaltCompiler(): string {
// Prevent the lexer from returning any further tokens.
$nextToken = $this->tokens[$this->tokenPos + 1];
$this->tokenPos = \count($this->tokens) - 2;
// Return text after __halt_compiler.
return $nextToken->id === \T_INLINE_HTML ? $nextToken->text : '';
}
protected function inlineHtmlHasLeadingNewline(int $stackPos): bool {
$tokenPos = $this->tokenStartStack[$stackPos];
$token = $this->tokens[$tokenPos];
assert($token->id == \T_INLINE_HTML);
if ($tokenPos > 0) {
$prevToken = $this->tokens[$tokenPos - 1];
assert($prevToken->id == \T_CLOSE_TAG);
return false !== strpos($prevToken->text, "\n")
|| false !== strpos($prevToken->text, "\r");
}
return true;
}
/**
* @return array<string, mixed>
*/
protected function createEmptyElemAttributes(int $tokenPos): array {
return $this->getAttributesForToken($tokenPos);
}
protected function fixupArrayDestructuring(Array_ $node): Expr\List_ {
$this->createdArrays->detach($node);
return new Expr\List_(array_map(function (Node\ArrayItem $item) {
if ($item->value instanceof Expr\Error) {
// We used Error as a placeholder for empty elements, which are legal for destructuring.
return null;
}
if ($item->value instanceof Array_) {
return new Node\ArrayItem(
$this->fixupArrayDestructuring($item->value),
$item->key, $item->byRef, $item->getAttributes());
}
return $item;
}, $node->items), ['kind' => Expr\List_::KIND_ARRAY] + $node->getAttributes());
}
protected function postprocessList(Expr\List_ $node): void {
foreach ($node->items as $i => $item) {
if ($item->value instanceof Expr\Error) {
// We used Error as a placeholder for empty elements, which are legal for destructuring.
$node->items[$i] = null;
}
}
}
/** @param ElseIf_|Else_ $node */
protected function fixupAlternativeElse($node): void {
// Make sure a trailing nop statement carrying comments is part of the node.
$numStmts = \count($node->stmts);
if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) {
$nopAttrs = $node->stmts[$numStmts - 1]->getAttributes();
if (isset($nopAttrs['endLine'])) {
$node->setAttribute('endLine', $nopAttrs['endLine']);
}
if (isset($nopAttrs['endFilePos'])) {
$node->setAttribute('endFilePos', $nopAttrs['endFilePos']);
}
if (isset($nopAttrs['endTokenPos'])) {
$node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']);
}
}
}
protected function checkClassModifier(int $a, int $b, int $modifierPos): void {
try {
Modifiers::verifyClassModifier($a, $b);
} catch (Error $error) {
$error->setAttributes($this->getAttributesAt($modifierPos));
$this->emitError($error);
}
}
protected function checkModifier(int $a, int $b, int $modifierPos): void {
// Jumping through some hoops here because verifyModifier() is also used elsewhere
try {
Modifiers::verifyModifier($a, $b);
} catch (Error $error) {
$error->setAttributes($this->getAttributesAt($modifierPos));
$this->emitError($error);
}
}
protected function checkParam(Param $node): void {
if ($node->variadic && null !== $node->default) {
$this->emitError(new Error(
'Variadic parameter cannot have a default value',
$node->default->getAttributes()
));
}
}
protected function checkTryCatch(TryCatch $node): void {
if (empty($node->catches) && null === $node->finally) {
$this->emitError(new Error(
'Cannot use try without catch or finally', $node->getAttributes()
));
}
}
protected function checkNamespace(Namespace_ $node): void {
if (null !== $node->stmts) {
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Namespace_) {
$this->emitError(new Error(
'Namespace declarations cannot be nested', $stmt->getAttributes()
));
}
}
}
}
private function checkClassName(?Identifier $name, int $namePos): void {
if (null !== $name && $name->isSpecialClassName()) {
$this->emitError(new Error(
sprintf('Cannot use \'%s\' as class name as it is reserved', $name),
$this->getAttributesAt($namePos)
));
}
}
/** @param Name[] $interfaces */
private function checkImplementedInterfaces(array $interfaces): void {
foreach ($interfaces as $interface) {
if ($interface->isSpecialClassName()) {
$this->emitError(new Error(
sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
$interface->getAttributes()
));
}
}
}
protected function checkClass(Class_ $node, int $namePos): void {
$this->checkClassName($node->name, $namePos);
if ($node->extends && $node->extends->isSpecialClassName()) {
$this->emitError(new Error(
sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
$node->extends->getAttributes()
));
}
$this->checkImplementedInterfaces($node->implements);
}
protected function checkInterface(Interface_ $node, int $namePos): void {
$this->checkClassName($node->name, $namePos);
$this->checkImplementedInterfaces($node->extends);
}
protected function checkEnum(Enum_ $node, int $namePos): void {
$this->checkClassName($node->name, $namePos);
$this->checkImplementedInterfaces($node->implements);
}
protected function checkClassMethod(ClassMethod $node, int $modifierPos): void {
if ($node->flags & Modifiers::STATIC) {
switch ($node->name->toLowerString()) {
case '__construct':
$this->emitError(new Error(
sprintf('Constructor %s() cannot be static', $node->name),
$this->getAttributesAt($modifierPos)));
break;
case '__destruct':
$this->emitError(new Error(
sprintf('Destructor %s() cannot be static', $node->name),
$this->getAttributesAt($modifierPos)));
break;
case '__clone':
$this->emitError(new Error(
sprintf('Clone method %s() cannot be static', $node->name),
$this->getAttributesAt($modifierPos)));
break;
}
}
if ($node->flags & Modifiers::READONLY) {
$this->emitError(new Error(
sprintf('Method %s() cannot be readonly', $node->name),
$this->getAttributesAt($modifierPos)));
}
}
protected function checkClassConst(ClassConst $node, int $modifierPos): void {
if ($node->flags & Modifiers::STATIC) {
$this->emitError(new Error(
"Cannot use 'static' as constant modifier",
$this->getAttributesAt($modifierPos)));
}
if ($node->flags & Modifiers::ABSTRACT) {
$this->emitError(new Error(
"Cannot use 'abstract' as constant modifier",
$this->getAttributesAt($modifierPos)));
}
if ($node->flags & Modifiers::READONLY) {
$this->emitError(new Error(
"Cannot use 'readonly' as constant modifier",
$this->getAttributesAt($modifierPos)));
}
}
protected function checkProperty(Property $node, int $modifierPos): void {
if ($node->flags & Modifiers::ABSTRACT) {
$this->emitError(new Error('Properties cannot be declared abstract',
$this->getAttributesAt($modifierPos)));
}
if ($node->flags & Modifiers::FINAL) {
$this->emitError(new Error('Properties cannot be declared final',
$this->getAttributesAt($modifierPos)));
}
}
protected function checkUseUse(UseItem $node, int $namePos): void {
if ($node->alias && $node->alias->isSpecialClassName()) {
$this->emitError(new Error(
sprintf(
'Cannot use %s as %s because \'%2$s\' is a special class name',
$node->name, $node->alias
),
$this->getAttributesAt($namePos)
));
}
}
/**
* Creates the token map.
*
* The token map maps the PHP internal token identifiers
* to the identifiers used by the Parser. Additionally it
* maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
*
* @return array<int, int> The token map
*/
protected function createTokenMap(): array {
$tokenMap = [];
for ($i = 0; $i < 1000; ++$i) {
if ($i < 256) {
// Single-char tokens use an identity mapping.
$tokenMap[$i] = $i;
} elseif (\T_DOUBLE_COLON === $i) {
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
$tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM;
} elseif (\T_OPEN_TAG_WITH_ECHO === $i) {
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
$tokenMap[$i] = static::T_ECHO;
} elseif (\T_CLOSE_TAG === $i) {
// T_CLOSE_TAG is equivalent to ';'
$tokenMap[$i] = ord(';');
} elseif ('UNKNOWN' !== $name = token_name($i)) {
if (defined($name = static::class . '::' . $name)) {
// Other tokens can be mapped directly
$tokenMap[$i] = constant($name);
}
}
}
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
$tokenMap[\T_FN] = static::T_FN;
$tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL;
$tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED;
$tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED;
$tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE;
$tokenMap[\T_MATCH] = static::T_MATCH;
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR;
$tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE;
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_ENUM] = static::T_ENUM;
$tokenMap[\T_READONLY] = static::T_READONLY;
// We have create a map from PHP token IDs to external symbol IDs.
// Now map them to the internal symbol ID.
$fullTokenMap = [];
foreach ($tokenMap as $phpToken => $extSymbol) {
$intSymbol = $this->tokenToSymbol[$extSymbol];
if ($intSymbol === $this->invalidSymbol) {
continue;
}
$fullTokenMap[$phpToken] = $intSymbol;
}
return $fullTokenMap;
}
}

View File

@ -0,0 +1,42 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Parser\Php7;
use PhpParser\Parser\Php8;
class ParserFactory {
/**
* Create a parser targeting the given version on a best-effort basis. The parser will generally
* accept code for the newest supported version, but will try to accommodate code that becomes
* invalid in newer versions or changes in interpretation.
*/
public function createForVersion(PhpVersion $version): Parser {
if ($version->isHostVersion()) {
$lexer = new Lexer();
} else {
$lexer = new Lexer\Emulative($version);
}
if ($version->id >= 80000) {
return new Php8($lexer, $version);
}
return new Php7($lexer, $version);
}
/**
* Create a parser targeting the newest version supported by this library. Code for older
* versions will be accepted if there have been no relevant backwards-compatibility breaks in
* PHP.
*/
public function createForNewestSupportedVersion(): Parser {
return $this->createForVersion(PhpVersion::getNewestSupported());
}
/**
* Create a parser targeting the host PHP version, that is the PHP version we're currently
* running on. This parser will not use any token emulation.
*/
public function createForHostVersion(): Parser {
return $this->createForVersion(PhpVersion::getHostVersion());
}
}

View File

@ -0,0 +1,164 @@
<?php declare(strict_types=1);
namespace PhpParser;
/**
* A PHP version, representing only the major and minor version components.
*/
class PhpVersion {
/** @var int Version ID in PHP_VERSION_ID format */
public int $id;
/** @var int[] Minimum versions for builtin types */
private const BUILTIN_TYPE_VERSIONS = [
'array' => 50100,
'callable' => 50400,
'bool' => 70000,
'int' => 70000,
'float' => 70000,
'string' => 70000,
'iterable' => 70100,
'void' => 70100,
'object' => 70200,
'null' => 80000,
'false' => 80000,
'mixed' => 80000,
'never' => 80100,
'true' => 80200,
];
private function __construct(int $id) {
$this->id = $id;
}
/**
* Create a PhpVersion object from major and minor version components.
*/
public static function fromComponents(int $major, int $minor): self {
return new self($major * 10000 + $minor * 100);
}
/**
* Get the newest PHP version supported by this library. Support for this version may be partial,
* if it is still under development.
*/
public static function getNewestSupported(): self {
return self::fromComponents(8, 3);
}
/**
* Get the host PHP version, that is the PHP version we're currently running on.
*/
public static function getHostVersion(): self {
return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION);
}
/**
* Parse the version from a string like "8.1".
*/
public static function fromString(string $version): self {
if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) {
throw new \LogicException("Invalid PHP version \"$version\"");
}
return self::fromComponents((int) $matches[1], (int) $matches[2]);
}
/**
* Check whether two versions are the same.
*/
public function equals(PhpVersion $other): bool {
return $this->id === $other->id;
}
/**
* Check whether this version is greater than or equal to the argument.
*/
public function newerOrEqual(PhpVersion $other): bool {
return $this->id >= $other->id;
}
/**
* Check whether this version is older than the argument.
*/
public function older(PhpVersion $other): bool {
return $this->id < $other->id;
}
/**
* Check whether this is the host PHP version.
*/
public function isHostVersion(): bool {
return $this->equals(self::getHostVersion());
}
/**
* Check whether this PHP version supports the given builtin type. Type name must be lowercase.
*/
public function supportsBuiltinType(string $type): bool {
$minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null;
return $minVersion !== null && $this->id >= $minVersion;
}
/**
* Whether this version supports [] array literals.
*/
public function supportsShortArraySyntax(): bool {
return $this->id >= 50400;
}
/**
* Whether this version supports [] for destructuring.
*/
public function supportsShortArrayDestructuring(): bool {
return $this->id >= 70100;
}
/**
* Whether this version supports flexible heredoc/nowdoc.
*/
public function supportsFlexibleHeredoc(): bool {
return $this->id >= 70300;
}
/**
* Whether this version supports trailing commas in parameter lists.
*/
public function supportsTrailingCommaInParamList(): bool {
return $this->id >= 80000;
}
/**
* Whether this version allows "$var =& new Obj".
*/
public function allowsAssignNewByReference(): bool {
return $this->id < 70000;
}
/**
* Whether this version allows invalid octals like "08".
*/
public function allowsInvalidOctals(): bool {
return $this->id < 70000;
}
/**
* Whether this version allows DEL (\x7f) to occur in identifiers.
*/
public function allowsDelInIdentifiers(): bool {
return $this->id < 70100;
}
/**
* Whether this version supports yield in expression context without parentheses.
*/
public function supportsYieldWithoutParentheses(): bool {
return $this->id >= 70000;
}
/**
* Whether this version supports unicode escape sequences in strings.
*/
public function supportsUnicodeEscapes(): bool {
return $this->id >= 70000;
}
}

View File

@ -0,0 +1,51 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Expr;
interface PrettyPrinter {
/**
* Pretty prints an array of statements.
*
* @param Node[] $stmts Array of statements
*
* @return string Pretty printed statements
*/
public function prettyPrint(array $stmts): string;
/**
* Pretty prints an expression.
*
* @param Expr $node Expression node
*
* @return string Pretty printed node
*/
public function prettyPrintExpr(Expr $node): string;
/**
* Pretty prints a file of statements (includes the opening <?php tag if it is required).
*
* @param Node[] $stmts Array of statements
*
* @return string Pretty printed statements
*/
public function prettyPrintFile(array $stmts): string;
/**
* Perform a format-preserving pretty print of an AST.
*
* The format preservation is best effort. For some changes to the AST the formatting will not
* be preserved (at least not locally).
*
* In order to use this method a number of prerequisites must be satisfied:
* * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
* * The CloningVisitor must be run on the AST prior to modification.
* * The original tokens must be provided, using the getTokens() method on the lexer.
*
* @param Node[] $stmts Modified AST with links to original AST
* @param Node[] $origStmts Original AST with token offset information
* @param Token[] $origTokens Tokens of the original code
*/
public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string;
}

View File

@ -0,0 +1,1177 @@
<?php declare(strict_types=1);
namespace PhpParser\PrettyPrinter;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\MagicConst;
use PhpParser\Node\Stmt;
use PhpParser\PrettyPrinterAbstract;
class Standard extends PrettyPrinterAbstract {
// Special nodes
protected function pParam(Node\Param $node): string {
return $this->pAttrGroups($node->attrGroups, true)
. $this->pModifiers($node->flags)
. ($node->type ? $this->p($node->type) . ' ' : '')
. ($node->byRef ? '&' : '')
. ($node->variadic ? '...' : '')
. $this->p($node->var)
. ($node->default ? ' = ' . $this->p($node->default) : '');
}
protected function pArg(Node\Arg $node): string {
return ($node->name ? $node->name->toString() . ': ' : '')
. ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '')
. $this->p($node->value);
}
protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node): string {
return '...';
}
protected function pConst(Node\Const_ $node): string {
return $node->name . ' = ' . $this->p($node->value);
}
protected function pNullableType(Node\NullableType $node): string {
return '?' . $this->p($node->type);
}
protected function pUnionType(Node\UnionType $node): string {
$types = [];
foreach ($node->types as $typeNode) {
if ($typeNode instanceof Node\IntersectionType) {
$types[] = '('. $this->p($typeNode) . ')';
continue;
}
$types[] = $this->p($typeNode);
}
return implode('|', $types);
}
protected function pIntersectionType(Node\IntersectionType $node): string {
return $this->pImplode($node->types, '&');
}
protected function pIdentifier(Node\Identifier $node): string {
return $node->name;
}
protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node): string {
return '$' . $node->name;
}
protected function pAttribute(Node\Attribute $node): string {
return $this->p($node->name)
. ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : '');
}
protected function pAttributeGroup(Node\AttributeGroup $node): string {
return '#[' . $this->pCommaSeparated($node->attrs) . ']';
}
// Names
protected function pName(Name $node): string {
return $node->name;
}
protected function pName_FullyQualified(Name\FullyQualified $node): string {
return '\\' . $node->name;
}
protected function pName_Relative(Name\Relative $node): string {
return 'namespace\\' . $node->name;
}
// Magic Constants
protected function pScalar_MagicConst_Class(MagicConst\Class_ $node): string {
return '__CLASS__';
}
protected function pScalar_MagicConst_Dir(MagicConst\Dir $node): string {
return '__DIR__';
}
protected function pScalar_MagicConst_File(MagicConst\File $node): string {
return '__FILE__';
}
protected function pScalar_MagicConst_Function(MagicConst\Function_ $node): string {
return '__FUNCTION__';
}
protected function pScalar_MagicConst_Line(MagicConst\Line $node): string {
return '__LINE__';
}
protected function pScalar_MagicConst_Method(MagicConst\Method $node): string {
return '__METHOD__';
}
protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node): string {
return '__NAMESPACE__';
}
protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node): string {
return '__TRAIT__';
}
// Scalars
private function indentString(string $str): string {
return str_replace("\n", $this->nl, $str);
}
protected function pScalar_String(Scalar\String_ $node): string {
$kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
switch ($kind) {
case Scalar\String_::KIND_NOWDOC:
$label = $node->getAttribute('docLabel');
if ($label && !$this->containsEndLabel($node->value, $label)) {
$shouldIdent = $this->phpVersion->supportsFlexibleHeredoc();
$nl = $shouldIdent ? $this->nl : $this->newline;
if ($node->value === '') {
return "<<<'$label'$nl$label{$this->docStringEndToken}";
}
// Make sure trailing \r is not combined with following \n into CRLF.
if ($node->value[strlen($node->value) - 1] !== "\r") {
$value = $shouldIdent ? $this->indentString($node->value) : $node->value;
return "<<<'$label'$nl$value$nl$label{$this->docStringEndToken}";
}
}
/* break missing intentionally */
// no break
case Scalar\String_::KIND_SINGLE_QUOTED:
return $this->pSingleQuotedString($node->value);
case Scalar\String_::KIND_HEREDOC:
$label = $node->getAttribute('docLabel');
$escaped = $this->escapeString($node->value, null);
if ($label && !$this->containsEndLabel($escaped, $label)) {
$nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
if ($escaped === '') {
return "<<<$label$nl$label{$this->docStringEndToken}";
}
return "<<<$label$nl$escaped$nl$label{$this->docStringEndToken}";
}
/* break missing intentionally */
// no break
case Scalar\String_::KIND_DOUBLE_QUOTED:
return '"' . $this->escapeString($node->value, '"') . '"';
}
throw new \Exception('Invalid string kind');
}
protected function pScalar_InterpolatedString(Scalar\InterpolatedString $node): string {
if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
$label = $node->getAttribute('docLabel');
if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
$nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
if (count($node->parts) === 1
&& $node->parts[0] instanceof Node\InterpolatedStringPart
&& $node->parts[0]->value === ''
) {
return "<<<$label$nl$label{$this->docStringEndToken}";
}
return "<<<$label$nl" . $this->pEncapsList($node->parts, null)
. "$nl$label{$this->docStringEndToken}";
}
}
return '"' . $this->pEncapsList($node->parts, '"') . '"';
}
protected function pScalar_Int(Scalar\Int_ $node): string {
if ($node->value === -\PHP_INT_MAX - 1) {
// PHP_INT_MIN cannot be represented as a literal,
// because the sign is not part of the literal
return '(-' . \PHP_INT_MAX . '-1)';
}
$kind = $node->getAttribute('kind', Scalar\Int_::KIND_DEC);
if (Scalar\Int_::KIND_DEC === $kind) {
return (string) $node->value;
}
if ($node->value < 0) {
$sign = '-';
$str = (string) -$node->value;
} else {
$sign = '';
$str = (string) $node->value;
}
switch ($kind) {
case Scalar\Int_::KIND_BIN:
return $sign . '0b' . base_convert($str, 10, 2);
case Scalar\Int_::KIND_OCT:
return $sign . '0' . base_convert($str, 10, 8);
case Scalar\Int_::KIND_HEX:
return $sign . '0x' . base_convert($str, 10, 16);
}
throw new \Exception('Invalid number kind');
}
protected function pScalar_Float(Scalar\Float_ $node): string {
if (!is_finite($node->value)) {
if ($node->value === \INF) {
return '1.0E+1000';
}
if ($node->value === -\INF) {
return '-1.0E+1000';
} else {
return '\NAN';
}
}
// Try to find a short full-precision representation
$stringValue = sprintf('%.16G', $node->value);
if ($node->value !== (float) $stringValue) {
$stringValue = sprintf('%.17G', $node->value);
}
// %G is locale dependent and there exists no locale-independent alternative. We don't want
// mess with switching locales here, so let's assume that a comma is the only non-standard
// decimal separator we may encounter...
$stringValue = str_replace(',', '.', $stringValue);
// ensure that number is really printed as float
return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue;
}
// Assignments
protected function pExpr_Assign(Expr\Assign $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\Assign::class, $this->p($node->var) . ' = ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignRef(Expr\AssignRef $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\AssignRef::class, $this->p($node->var) . ' =& ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Plus(AssignOp\Plus $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Plus::class, $this->p($node->var) . ' += ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Minus(AssignOp\Minus $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Minus::class, $this->p($node->var) . ' -= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Mul(AssignOp\Mul $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Mul::class, $this->p($node->var) . ' *= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Div(AssignOp\Div $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Div::class, $this->p($node->var) . ' /= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Concat(AssignOp\Concat $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Concat::class, $this->p($node->var) . ' .= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Mod(AssignOp\Mod $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Mod::class, $this->p($node->var) . ' %= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\BitwiseAnd::class, $this->p($node->var) . ' &= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\BitwiseOr::class, $this->p($node->var) . ' |= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\BitwiseXor::class, $this->p($node->var) . ' ^= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\ShiftLeft::class, $this->p($node->var) . ' <<= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\ShiftRight::class, $this->p($node->var) . ' >>= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Pow(AssignOp\Pow $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Pow::class, $this->p($node->var) . ' **= ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(AssignOp\Coalesce::class, $this->p($node->var) . ' ??= ', $node->expr, $precedence, $lhsPrecedence);
}
// Binary expressions
protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Div(BinaryOp\Div $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node, int $precedence, int $lhsPrecedence): string {
return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
}
protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPostfixOp(
Expr\Instanceof_::class, $node->expr,
' instanceof ' . $this->pNewOperand($node->class),
$precedence, $lhsPrecedence);
}
// Unary expressions
protected function pExpr_BooleanNot(Expr\BooleanNot $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_BitwiseNot(Expr\BitwiseNot $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_UnaryMinus(Expr\UnaryMinus $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_UnaryPlus(Expr\UnaryPlus $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_PreInc(Expr\PreInc $node): string {
return '++' . $this->p($node->var);
}
protected function pExpr_PreDec(Expr\PreDec $node): string {
return '--' . $this->p($node->var);
}
protected function pExpr_PostInc(Expr\PostInc $node): string {
return $this->p($node->var) . '++';
}
protected function pExpr_PostDec(Expr\PostDec $node): string {
return $this->p($node->var) . '--';
}
protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_YieldFrom(Expr\YieldFrom $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Print(Expr\Print_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr, $precedence, $lhsPrecedence);
}
// Casts
protected function pExpr_Cast_Int(Cast\Int_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_Double(Cast\Double $node, int $precedence, int $lhsPrecedence): string {
$kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE);
if ($kind === Cast\Double::KIND_DOUBLE) {
$cast = '(double)';
} elseif ($kind === Cast\Double::KIND_FLOAT) {
$cast = '(float)';
} else {
assert($kind === Cast\Double::KIND_REAL);
$cast = '(real)';
}
return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_String(Cast\String_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_Array(Cast\Array_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_Object(Cast\Object_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_Bool(Cast\Bool_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Cast_Unset(Cast\Unset_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence);
}
// Function calls and similar constructs
protected function pExpr_FuncCall(Expr\FuncCall $node): string {
return $this->pCallLhs($node->name)
. '(' . $this->pMaybeMultiline($node->args) . ')';
}
protected function pExpr_MethodCall(Expr\MethodCall $node): string {
return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name)
. '(' . $this->pMaybeMultiline($node->args) . ')';
}
protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node): string {
return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name)
. '(' . $this->pMaybeMultiline($node->args) . ')';
}
protected function pExpr_StaticCall(Expr\StaticCall $node): string {
return $this->pStaticDereferenceLhs($node->class) . '::'
. ($node->name instanceof Expr
? ($node->name instanceof Expr\Variable
? $this->p($node->name)
: '{' . $this->p($node->name) . '}')
: $node->name)
. '(' . $this->pMaybeMultiline($node->args) . ')';
}
protected function pExpr_Empty(Expr\Empty_ $node): string {
return 'empty(' . $this->p($node->expr) . ')';
}
protected function pExpr_Isset(Expr\Isset_ $node): string {
return 'isset(' . $this->pCommaSeparated($node->vars) . ')';
}
protected function pExpr_Eval(Expr\Eval_ $node): string {
return 'eval(' . $this->p($node->expr) . ')';
}
protected function pExpr_Include(Expr\Include_ $node, int $precedence, int $lhsPrecedence): string {
static $map = [
Expr\Include_::TYPE_INCLUDE => 'include',
Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once',
Expr\Include_::TYPE_REQUIRE => 'require',
Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once',
];
return $this->pPrefixOp(Expr\Include_::class, $map[$node->type] . ' ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_List(Expr\List_ $node): string {
$syntax = $node->getAttribute('kind',
$this->phpVersion->supportsShortArrayDestructuring() ? Expr\List_::KIND_ARRAY : Expr\List_::KIND_LIST);
if ($syntax === Expr\List_::KIND_ARRAY) {
return '[' . $this->pMaybeMultiline($node->items, true) . ']';
} else {
return 'list(' . $this->pMaybeMultiline($node->items, true) . ')';
}
}
// Other
protected function pExpr_Error(Expr\Error $node): string {
throw new \LogicException('Cannot pretty-print AST with Error nodes');
}
protected function pExpr_Variable(Expr\Variable $node): string {
if ($node->name instanceof Expr) {
return '${' . $this->p($node->name) . '}';
} else {
return '$' . $node->name;
}
}
protected function pExpr_Array(Expr\Array_ $node): string {
$syntax = $node->getAttribute('kind',
$this->shortArraySyntax ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG);
if ($syntax === Expr\Array_::KIND_SHORT) {
return '[' . $this->pMaybeMultiline($node->items, true) . ']';
} else {
return 'array(' . $this->pMaybeMultiline($node->items, true) . ')';
}
}
protected function pKey(?Node $node): string {
if ($node === null) {
return '';
}
// => is not really an operator and does not typically participate in precedence resolution.
// However, there is an exception if yield expressions with keys are involved:
// [yield $a => $b] is interpreted as [(yield $a => $b)], so we need to ensure that
// [(yield $a) => $b] is printed with parentheses. We approximate this by lowering the LHS
// precedence to that of yield (which will also print unnecessary parentheses for rare low
// precedence unary operators like include).
$yieldPrecedence = $this->precedenceMap[Expr\Yield_::class][0];
return $this->p($node, self::MAX_PRECEDENCE, $yieldPrecedence) . ' => ';
}
protected function pArrayItem(Node\ArrayItem $node): string {
return $this->pKey($node->key)
. ($node->byRef ? '&' : '')
. ($node->unpack ? '...' : '')
. $this->p($node->value);
}
protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node): string {
return $this->pDereferenceLhs($node->var)
. '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']';
}
protected function pExpr_ConstFetch(Expr\ConstFetch $node): string {
return $this->p($node->name);
}
protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string {
return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name);
}
protected function pExpr_PropertyFetch(Expr\PropertyFetch $node): string {
return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name);
}
protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node): string {
return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name);
}
protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node): string {
return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name);
}
protected function pExpr_ShellExec(Expr\ShellExec $node): string {
return '`' . $this->pEncapsList($node->parts, '`') . '`';
}
protected function pExpr_Closure(Expr\Closure $node): string {
return $this->pAttrGroups($node->attrGroups, true)
. $this->pStatic($node->static)
. 'function ' . ($node->byRef ? '&' : '')
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '')
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pExpr_Match(Expr\Match_ $node): string {
return 'match (' . $this->p($node->cond) . ') {'
. $this->pCommaSeparatedMultiline($node->arms, true)
. $this->nl
. '}';
}
protected function pMatchArm(Node\MatchArm $node): string {
$result = '';
if ($node->conds) {
for ($i = 0, $c = \count($node->conds); $i + 1 < $c; $i++) {
$result .= $this->p($node->conds[$i]) . ', ';
}
$result .= $this->pKey($node->conds[$i]);
} else {
$result = 'default => ';
}
return $result . $this->p($node->body);
}
protected function pExpr_ArrowFunction(Expr\ArrowFunction $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(
Expr\ArrowFunction::class,
$this->pAttrGroups($node->attrGroups, true)
. $this->pStatic($node->static)
. 'fn' . ($node->byRef ? '&' : '')
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. ' => ',
$node->expr, $precedence, $lhsPrecedence);
}
protected function pClosureUse(Node\ClosureUse $node): string {
return ($node->byRef ? '&' : '') . $this->p($node->var);
}
protected function pExpr_New(Expr\New_ $node): string {
if ($node->class instanceof Stmt\Class_) {
$args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : '';
return 'new ' . $this->pClassCommon($node->class, $args);
}
return 'new ' . $this->pNewOperand($node->class)
. '(' . $this->pMaybeMultiline($node->args) . ')';
}
protected function pExpr_Clone(Expr\Clone_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\Clone_::class, 'clone ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Ternary(Expr\Ternary $node, int $precedence, int $lhsPrecedence): string {
// a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator.
// this is okay because the part between ? and : never needs parentheses.
return $this->pInfixOp(Expr\Ternary::class,
$node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else,
$precedence, $lhsPrecedence
);
}
protected function pExpr_Exit(Expr\Exit_ $node): string {
$kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE);
return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die')
. (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
}
protected function pExpr_Throw(Expr\Throw_ $node, int $precedence, int $lhsPrecedence): string {
return $this->pPrefixOp(Expr\Throw_::class, 'throw ', $node->expr, $precedence, $lhsPrecedence);
}
protected function pExpr_Yield(Expr\Yield_ $node, int $precedence, int $lhsPrecedence): string {
if ($node->value === null) {
$opPrecedence = $this->precedenceMap[Expr\Yield_::class][0];
return $opPrecedence >= $lhsPrecedence ? '(yield)' : 'yield';
} else {
if (!$this->phpVersion->supportsYieldWithoutParentheses()) {
return '(yield ' . $this->pKey($node->key) . $this->p($node->value) . ')';
}
return $this->pPrefixOp(
Expr\Yield_::class, 'yield ' . $this->pKey($node->key),
$node->value, $precedence, $lhsPrecedence);
}
}
// Declarations
protected function pStmt_Namespace(Stmt\Namespace_ $node): string {
if ($this->canUseSemicolonNamespaces) {
return 'namespace ' . $this->p($node->name) . ';'
. $this->nl . $this->pStmts($node->stmts, false);
} else {
return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '')
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
}
protected function pStmt_Use(Stmt\Use_ $node): string {
return 'use ' . $this->pUseType($node->type)
. $this->pCommaSeparated($node->uses) . ';';
}
protected function pStmt_GroupUse(Stmt\GroupUse $node): string {
return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix)
. '\{' . $this->pCommaSeparated($node->uses) . '};';
}
protected function pUseItem(Node\UseItem $node): string {
return $this->pUseType($node->type) . $this->p($node->name)
. (null !== $node->alias ? ' as ' . $node->alias : '');
}
protected function pUseType(int $type): string {
return $type === Stmt\Use_::TYPE_FUNCTION ? 'function '
: ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : '');
}
protected function pStmt_Interface(Stmt\Interface_ $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'interface ' . $node->name
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Enum(Stmt\Enum_ $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'enum ' . $node->name
. ($node->scalarType ? ' : ' . $this->p($node->scalarType) : '')
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Class(Stmt\Class_ $node): string {
return $this->pClassCommon($node, ' ' . $node->name);
}
protected function pStmt_Trait(Stmt\Trait_ $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'trait ' . $node->name
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_EnumCase(Stmt\EnumCase $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'case ' . $node->name
. ($node->expr ? ' = ' . $this->p($node->expr) : '')
. ';';
}
protected function pStmt_TraitUse(Stmt\TraitUse $node): string {
return 'use ' . $this->pCommaSeparated($node->traits)
. (empty($node->adaptations)
? ';'
: ' {' . $this->pStmts($node->adaptations) . $this->nl . '}');
}
protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node): string {
return $this->p($node->trait) . '::' . $node->method
. ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';';
}
protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node): string {
return (null !== $node->trait ? $this->p($node->trait) . '::' : '')
. $node->method . ' as'
. (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '')
. (null !== $node->newName ? ' ' . $node->newName : '')
. ';';
}
protected function pStmt_Property(Stmt\Property $node): string {
return $this->pAttrGroups($node->attrGroups)
. (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
. ($node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->props) . ';';
}
protected function pPropertyItem(Node\PropertyItem $node): string {
return '$' . $node->name
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
}
protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. (null !== $node->stmts
? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'
: ';');
}
protected function pStmt_ClassConst(Stmt\ClassConst $node): string {
return $this->pAttrGroups($node->attrGroups)
. $this->pModifiers($node->flags)
. 'const '
. (null !== $node->type ? $this->p($node->type) . ' ' : '')
. $this->pCommaSeparated($node->consts) . ';';
}
protected function pStmt_Function(Stmt\Function_ $node): string {
return $this->pAttrGroups($node->attrGroups)
. 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')'
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Const(Stmt\Const_ $node): string {
return 'const ' . $this->pCommaSeparated($node->consts) . ';';
}
protected function pStmt_Declare(Stmt\Declare_ $node): string {
return 'declare (' . $this->pCommaSeparated($node->declares) . ')'
. (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';');
}
protected function pDeclareItem(Node\DeclareItem $node): string {
return $node->key . '=' . $this->p($node->value);
}
// Control flow
protected function pStmt_If(Stmt\If_ $node): string {
return 'if (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . $this->nl . '}'
. ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '')
. (null !== $node->else ? ' ' . $this->p($node->else) : '');
}
protected function pStmt_ElseIf(Stmt\ElseIf_ $node): string {
return 'elseif (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Else(Stmt\Else_ $node): string {
if (\count($node->stmts) === 1 && $node->stmts[0] instanceof Stmt\If_) {
// Print as "else if" rather than "else { if }"
return 'else ' . $this->p($node->stmts[0]);
}
return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_For(Stmt\For_ $node): string {
return 'for ('
. $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '')
. $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '')
. $this->pCommaSeparated($node->loop)
. ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Foreach(Stmt\Foreach_ $node): string {
return 'foreach (' . $this->p($node->expr) . ' as '
. (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_While(Stmt\While_ $node): string {
return 'while (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Do(Stmt\Do_ $node): string {
return 'do {' . $this->pStmts($node->stmts) . $this->nl
. '} while (' . $this->p($node->cond) . ');';
}
protected function pStmt_Switch(Stmt\Switch_ $node): string {
return 'switch (' . $this->p($node->cond) . ') {'
. $this->pStmts($node->cases) . $this->nl . '}';
}
protected function pStmt_Case(Stmt\Case_ $node): string {
return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
. $this->pStmts($node->stmts);
}
protected function pStmt_TryCatch(Stmt\TryCatch $node): string {
return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}'
. ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '')
. ($node->finally !== null ? ' ' . $this->p($node->finally) : '');
}
protected function pStmt_Catch(Stmt\Catch_ $node): string {
return 'catch (' . $this->pImplode($node->types, '|')
. ($node->var !== null ? ' ' . $this->p($node->var) : '')
. ') {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Finally(Stmt\Finally_ $node): string {
return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pStmt_Break(Stmt\Break_ $node): string {
return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
}
protected function pStmt_Continue(Stmt\Continue_ $node): string {
return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';';
}
protected function pStmt_Return(Stmt\Return_ $node): string {
return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';';
}
protected function pStmt_Label(Stmt\Label $node): string {
return $node->name . ':';
}
protected function pStmt_Goto(Stmt\Goto_ $node): string {
return 'goto ' . $node->name . ';';
}
// Other
protected function pStmt_Expression(Stmt\Expression $node): string {
return $this->p($node->expr) . ';';
}
protected function pStmt_Echo(Stmt\Echo_ $node): string {
return 'echo ' . $this->pCommaSeparated($node->exprs) . ';';
}
protected function pStmt_Static(Stmt\Static_ $node): string {
return 'static ' . $this->pCommaSeparated($node->vars) . ';';
}
protected function pStmt_Global(Stmt\Global_ $node): string {
return 'global ' . $this->pCommaSeparated($node->vars) . ';';
}
protected function pStaticVar(Node\StaticVar $node): string {
return $this->p($node->var)
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
}
protected function pStmt_Unset(Stmt\Unset_ $node): string {
return 'unset(' . $this->pCommaSeparated($node->vars) . ');';
}
protected function pStmt_InlineHTML(Stmt\InlineHTML $node): string {
$newline = $node->getAttribute('hasLeadingNewline', true) ? $this->newline : '';
return '?>' . $newline . $node->value . '<?php ';
}
protected function pStmt_HaltCompiler(Stmt\HaltCompiler $node): string {
return '__halt_compiler();' . $node->remaining;
}
protected function pStmt_Nop(Stmt\Nop $node): string {
return '';
}
protected function pStmt_Block(Stmt\Block $node): string {
return '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
// Helpers
protected function pClassCommon(Stmt\Class_ $node, string $afterClassToken): string {
return $this->pAttrGroups($node->attrGroups, $node->name === null)
. $this->pModifiers($node->flags)
. 'class' . $afterClassToken
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
. $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}';
}
protected function pObjectProperty(Node $node): string {
if ($node instanceof Expr) {
return '{' . $this->p($node) . '}';
} else {
assert($node instanceof Node\Identifier);
return $node->name;
}
}
/** @param (Expr|Node\InterpolatedStringPart)[] $encapsList */
protected function pEncapsList(array $encapsList, ?string $quote): string {
$return = '';
foreach ($encapsList as $element) {
if ($element instanceof Node\InterpolatedStringPart) {
$return .= $this->escapeString($element->value, $quote);
} else {
$return .= '{' . $this->p($element) . '}';
}
}
return $return;
}
protected function pSingleQuotedString(string $string): string {
// It is idiomatic to only escape backslashes when necessary, i.e. when followed by ', \ or
// the end of the string ('Foo\Bar' instead of 'Foo\\Bar'). However, we also don't want to
// produce an odd number of backslashes, so '\\\\a' should not get rendered as '\\\a', even
// though that would be legal.
$regex = '/\'|\\\\(?=[\'\\\\]|$)|(?<=\\\\)\\\\/';
return '\'' . preg_replace($regex, '\\\\$0', $string) . '\'';
}
protected function escapeString(string $string, ?string $quote): string {
if (null === $quote) {
// For doc strings, don't escape newlines
$escaped = addcslashes($string, "\t\f\v$\\");
// But do escape isolated \r. Combined with the terminating newline, it might get
// interpreted as \r\n and dropped from the string contents.
$escaped = preg_replace('/\r(?!\n)/', '\\r', $escaped);
if ($this->phpVersion->supportsFlexibleHeredoc()) {
$escaped = $this->indentString($escaped);
}
} else {
$escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
}
// Escape control characters and non-UTF-8 characters.
// Regex based on https://stackoverflow.com/a/11709412/385378.
$regex = '/(
[\x00-\x08\x0E-\x1F] # Control characters
| [\xC0-\xC1] # Invalid UTF-8 Bytes
| [\xF5-\xFF] # Invalid UTF-8 Bytes
| \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
| \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
| [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
| [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
| [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
| (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
| (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
| (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
| (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
| (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
)/x';
return preg_replace_callback($regex, function ($matches): string {
assert(strlen($matches[0]) === 1);
$hex = dechex(ord($matches[0]));
return '\\x' . str_pad($hex, 2, '0', \STR_PAD_LEFT);
}, $escaped);
}
protected function containsEndLabel(string $string, string $label, bool $atStart = true): bool {
$start = $atStart ? '(?:^|[\r\n])[ \t]*' : '[\r\n][ \t]*';
return false !== strpos($string, $label)
&& preg_match('/' . $start . $label . '(?:$|[^_A-Za-z0-9\x80-\xff])/', $string);
}
/** @param (Expr|Node\InterpolatedStringPart)[] $parts */
protected function encapsedContainsEndLabel(array $parts, string $label): bool {
foreach ($parts as $i => $part) {
if ($part instanceof Node\InterpolatedStringPart
&& $this->containsEndLabel($this->escapeString($part->value, null), $label, $i === 0)
) {
return true;
}
}
return false;
}
protected function pDereferenceLhs(Node $node): string {
if (!$this->dereferenceLhsRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
protected function pStaticDereferenceLhs(Node $node): string {
if (!$this->staticDereferenceLhsRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
protected function pCallLhs(Node $node): string {
if (!$this->callLhsRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
protected function pNewOperand(Node $node): string {
if (!$this->newOperandRequiresParens($node)) {
return $this->p($node);
} else {
return '(' . $this->p($node) . ')';
}
}
/**
* @param Node[] $nodes
*/
protected function hasNodeWithComments(array $nodes): bool {
foreach ($nodes as $node) {
if ($node && $node->getComments()) {
return true;
}
}
return false;
}
/** @param Node[] $nodes */
protected function pMaybeMultiline(array $nodes, bool $trailingComma = false): string {
if (!$this->hasNodeWithComments($nodes)) {
return $this->pCommaSeparated($nodes);
} else {
return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl;
}
}
/** @param Node\AttributeGroup[] $nodes */
protected function pAttrGroups(array $nodes, bool $inline = false): string {
$result = '';
$sep = $inline ? ' ' : $this->nl;
foreach ($nodes as $node) {
$result .= $this->p($node) . $sep;
}
return $result;
}
}

View File

@ -0,0 +1,1655 @@
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Internal\DiffElem;
use PhpParser\Internal\Differ;
use PhpParser\Internal\PrintableNewAnonClassNode;
use PhpParser\Internal\TokenStream;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\IntersectionType;
use PhpParser\Node\MatchArm;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
use PhpParser\Node\UnionType;
abstract class PrettyPrinterAbstract implements PrettyPrinter {
protected const FIXUP_PREC_LEFT = 0; // LHS operand affected by precedence
protected const FIXUP_PREC_RIGHT = 1; // RHS operand affected by precedence
protected const FIXUP_PREC_UNARY = 2; // Only operand affected by precedence
protected const FIXUP_CALL_LHS = 3; // LHS of call
protected const FIXUP_DEREF_LHS = 4; // LHS of dereferencing operation
protected const FIXUP_STATIC_DEREF_LHS = 5; // LHS of static dereferencing operation
protected const FIXUP_BRACED_NAME = 6; // Name operand that may require bracing
protected const FIXUP_VAR_BRACED_NAME = 7; // Name operand that may require ${} bracing
protected const FIXUP_ENCAPSED = 8; // Encapsed string part
protected const FIXUP_NEW = 9; // New/instanceof operand
protected const MAX_PRECEDENCE = 1000;
/** @var array<class-string, array{int, int, int}> */
protected array $precedenceMap = [
// [precedence, precedenceLHS, precedenceRHS]
// Where the latter two are the precedences to use for the LHS and RHS of a binary operator,
// where 1 is added to one of the sides depending on associativity. This information is not
// used for unary operators and set to -1.
Expr\Clone_::class => [-10, 0, 1],
BinaryOp\Pow::class => [ 0, 0, 1],
Expr\BitwiseNot::class => [ 10, -1, -1],
Expr\UnaryPlus::class => [ 10, -1, -1],
Expr\UnaryMinus::class => [ 10, -1, -1],
Cast\Int_::class => [ 10, -1, -1],
Cast\Double::class => [ 10, -1, -1],
Cast\String_::class => [ 10, -1, -1],
Cast\Array_::class => [ 10, -1, -1],
Cast\Object_::class => [ 10, -1, -1],
Cast\Bool_::class => [ 10, -1, -1],
Cast\Unset_::class => [ 10, -1, -1],
Expr\ErrorSuppress::class => [ 10, -1, -1],
Expr\Instanceof_::class => [ 20, -1, -1],
Expr\BooleanNot::class => [ 30, -1, -1],
BinaryOp\Mul::class => [ 40, 41, 40],
BinaryOp\Div::class => [ 40, 41, 40],
BinaryOp\Mod::class => [ 40, 41, 40],
BinaryOp\Plus::class => [ 50, 51, 50],
BinaryOp\Minus::class => [ 50, 51, 50],
BinaryOp\Concat::class => [ 50, 51, 50],
BinaryOp\ShiftLeft::class => [ 60, 61, 60],
BinaryOp\ShiftRight::class => [ 60, 61, 60],
BinaryOp\Smaller::class => [ 70, 70, 70],
BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
BinaryOp\Greater::class => [ 70, 70, 70],
BinaryOp\GreaterOrEqual::class => [ 70, 70, 70],
BinaryOp\Equal::class => [ 80, 80, 80],
BinaryOp\NotEqual::class => [ 80, 80, 80],
BinaryOp\Identical::class => [ 80, 80, 80],
BinaryOp\NotIdentical::class => [ 80, 80, 80],
BinaryOp\Spaceship::class => [ 80, 80, 80],
BinaryOp\BitwiseAnd::class => [ 90, 91, 90],
BinaryOp\BitwiseXor::class => [100, 101, 100],
BinaryOp\BitwiseOr::class => [110, 111, 110],
BinaryOp\BooleanAnd::class => [120, 121, 120],
BinaryOp\BooleanOr::class => [130, 131, 130],
BinaryOp\Coalesce::class => [140, 140, 141],
Expr\Ternary::class => [150, 150, 150],
Expr\Assign::class => [160, -1, -1],
Expr\AssignRef::class => [160, -1, -1],
AssignOp\Plus::class => [160, -1, -1],
AssignOp\Minus::class => [160, -1, -1],
AssignOp\Mul::class => [160, -1, -1],
AssignOp\Div::class => [160, -1, -1],
AssignOp\Concat::class => [160, -1, -1],
AssignOp\Mod::class => [160, -1, -1],
AssignOp\BitwiseAnd::class => [160, -1, -1],
AssignOp\BitwiseOr::class => [160, -1, -1],
AssignOp\BitwiseXor::class => [160, -1, -1],
AssignOp\ShiftLeft::class => [160, -1, -1],
AssignOp\ShiftRight::class => [160, -1, -1],
AssignOp\Pow::class => [160, -1, -1],
AssignOp\Coalesce::class => [160, -1, -1],
Expr\YieldFrom::class => [170, -1, -1],
Expr\Yield_::class => [175, -1, -1],
Expr\Print_::class => [180, -1, -1],
BinaryOp\LogicalAnd::class => [190, 191, 190],
BinaryOp\LogicalXor::class => [200, 201, 200],
BinaryOp\LogicalOr::class => [210, 211, 210],
Expr\Include_::class => [220, -1, -1],
Expr\ArrowFunction::class => [230, -1, -1],
Expr\Throw_::class => [240, -1, -1],
];
/** @var int Current indentation level. */
protected int $indentLevel;
/** @var string Newline style. Does not include current indentation. */
protected string $newline;
/** @var string Newline including current indentation. */
protected string $nl;
/** @var string|null Token placed at end of doc string to ensure it is followed by a newline.
* Null if flexible doc strings are used. */
protected ?string $docStringEndToken;
/** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */
protected bool $canUseSemicolonNamespaces;
/** @var bool Whether to use short array syntax if the node specifies no preference */
protected bool $shortArraySyntax;
/** @var PhpVersion PHP version to target */
protected PhpVersion $phpVersion;
/** @var TokenStream|null Original tokens for use in format-preserving pretty print */
protected ?TokenStream $origTokens;
/** @var Internal\Differ<Node> Differ for node lists */
protected Differ $nodeListDiffer;
/** @var array<string, bool> Map determining whether a certain character is a label character */
protected array $labelCharMap;
/**
* @var array<string, array<string, int>> Map from token classes and subnode names to FIXUP_* constants.
* This is used during format-preserving prints to place additional parens/braces if necessary.
*/
protected array $fixupMap;
/**
* @var array<string, array{left?: int|string, right?: int|string}> Map from "{$node->getType()}->{$subNode}"
* to ['left' => $l, 'right' => $r], where $l and $r specify the token type that needs to be stripped
* when removing this node.
*/
protected array $removalMap;
/**
* @var array<string, array{int|string|null, bool, string|null, string|null}> Map from
* "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight].
* $find is an optional token after which the insertion occurs. $extraLeft/Right
* are optionally added before/after the main insertions.
*/
protected array $insertionMap;
/**
* @var array<string, string> Map From "{$class}->{$subNode}" to string that should be inserted
* between elements of this list subnode.
*/
protected array $listInsertionMap;
/**
* @var array<string, array{int|string|null, string, string}>
*/
protected array $emptyListInsertionMap;
/** @var array<string, array{string, int}> Map from "{$class}->{$subNode}" to [$printFn, $token]
* where $printFn is the function to print the modifiers and $token is the token before which
* the modifiers should be reprinted. */
protected array $modifierChangeMap;
/**
* Creates a pretty printer instance using the given options.
*
* Supported options:
* * PhpVersion $phpVersion: The PHP version to target (default to PHP 7.4). This option
* controls compatibility of the generated code with older PHP
* versions in cases where a simple stylistic choice exists (e.g.
* array() vs []). It is safe to pretty-print an AST for a newer
* PHP version while specifying an older target (but the result will
* of course not be compatible with the older version in that case).
* * string $newline: The newline style to use. Should be "\n" (default) or "\r\n".
* * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
* syntax, if the node does not specify a format. Defaults to whether
* the phpVersion support short array syntax.
*
* @param array{
* phpVersion?: PhpVersion, newline?: string, shortArraySyntax?: bool
* } $options Dictionary of formatting options
*/
public function __construct(array $options = []) {
$this->phpVersion = $options['phpVersion'] ?? PhpVersion::fromComponents(7, 4);
$this->newline = $options['newline'] ?? "\n";
if ($this->newline !== "\n" && $this->newline != "\r\n") {
throw new \LogicException('Option "newline" must be one of "\n" or "\r\n"');
}
$this->shortArraySyntax =
$options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
$this->docStringEndToken =
$this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();
}
/**
* Reset pretty printing state.
*/
protected function resetState(): void {
$this->indentLevel = 0;
$this->nl = $this->newline;
$this->origTokens = null;
}
/**
* Set indentation level
*
* @param int $level Level in number of spaces
*/
protected function setIndentLevel(int $level): void {
$this->indentLevel = $level;
$this->nl = $this->newline . \str_repeat(' ', $level);
}
/**
* Increase indentation level.
*/
protected function indent(): void {
$this->indentLevel += 4;
$this->nl .= ' ';
}
/**
* Decrease indentation level.
*/
protected function outdent(): void {
assert($this->indentLevel >= 4);
$this->indentLevel -= 4;
$this->nl = $this->newline . str_repeat(' ', $this->indentLevel);
}
/**
* Pretty prints an array of statements.
*
* @param Node[] $stmts Array of statements
*
* @return string Pretty printed statements
*/
public function prettyPrint(array $stmts): string {
$this->resetState();
$this->preprocessNodes($stmts);
return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
}
/**
* Pretty prints an expression.
*
* @param Expr $node Expression node
*
* @return string Pretty printed node
*/
public function prettyPrintExpr(Expr $node): string {
$this->resetState();
return $this->handleMagicTokens($this->p($node));
}
/**
* Pretty prints a file of statements (includes the opening <?php tag if it is required).
*
* @param Node[] $stmts Array of statements
*
* @return string Pretty printed statements
*/
public function prettyPrintFile(array $stmts): string {
if (!$stmts) {
return "<?php" . $this->newline . $this->newline;
}
$p = "<?php" . $this->newline . $this->newline . $this->prettyPrint($stmts);
if ($stmts[0] instanceof Stmt\InlineHTML) {
$p = preg_replace('/^<\?php\s+\?>\r?\n?/', '', $p);
}
if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) {
$p = preg_replace('/<\?php$/', '', rtrim($p));
}
return $p;
}
/**
* Preprocesses the top-level nodes to initialize pretty printer state.
*
* @param Node[] $nodes Array of nodes
*/
protected function preprocessNodes(array $nodes): void {
/* We can use semicolon-namespaces unless there is a global namespace declaration */
$this->canUseSemicolonNamespaces = true;
foreach ($nodes as $node) {
if ($node instanceof Stmt\Namespace_ && null === $node->name) {
$this->canUseSemicolonNamespaces = false;
break;
}
}
}
/**
* Handles (and removes) doc-string-end tokens.
*/
protected function handleMagicTokens(string $str): string {
if ($this->docStringEndToken !== null) {
// Replace doc-string-end tokens with nothing or a newline
$str = str_replace(
$this->docStringEndToken . ';' . $this->newline,
';' . $this->newline,
$str);
$str = str_replace($this->docStringEndToken, $this->newline, $str);
}
return $str;
}
/**
* Pretty prints an array of nodes (statements) and indents them optionally.
*
* @param Node[] $nodes Array of nodes
* @param bool $indent Whether to indent the printed nodes
*
* @return string Pretty printed statements
*/
protected function pStmts(array $nodes, bool $indent = true): string {
if ($indent) {
$this->indent();
}
$result = '';
foreach ($nodes as $node) {
$comments = $node->getComments();
if ($comments) {
$result .= $this->nl . $this->pComments($comments);
if ($node instanceof Stmt\Nop) {
continue;
}
}
$result .= $this->nl . $this->p($node);
}
if ($indent) {
$this->outdent();
}
return $result;
}
/**
* Pretty-print an infix operation while taking precedence into account.
*
* @param string $class Node class of operator
* @param Node $leftNode Left-hand side node
* @param string $operatorString String representation of the operator
* @param Node $rightNode Right-hand side node
* @param int $precedence Precedence of parent operator
* @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
*
* @return string Pretty printed infix operation
*/
protected function pInfixOp(
string $class, Node $leftNode, string $operatorString, Node $rightNode,
int $precedence, int $lhsPrecedence
): string {
list($opPrecedence, $newPrecedenceLHS, $newPrecedenceRHS) = $this->precedenceMap[$class];
$prefix = '';
$suffix = '';
if ($opPrecedence >= $precedence) {
$prefix = '(';
$suffix = ')';
$lhsPrecedence = self::MAX_PRECEDENCE;
}
return $prefix . $this->p($leftNode, $newPrecedenceLHS, $newPrecedenceLHS)
. $operatorString . $this->p($rightNode, $newPrecedenceRHS, $lhsPrecedence) . $suffix;
}
/**
* Pretty-print a prefix operation while taking precedence into account.
*
* @param string $class Node class of operator
* @param string $operatorString String representation of the operator
* @param Node $node Node
* @param int $precedence Precedence of parent operator
* @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
*
* @return string Pretty printed prefix operation
*/
protected function pPrefixOp(string $class, string $operatorString, Node $node, int $precedence, int $lhsPrecedence): string {
$opPrecedence = $this->precedenceMap[$class][0];
$prefix = '';
$suffix = '';
if ($opPrecedence >= $lhsPrecedence) {
$prefix = '(';
$suffix = ')';
$lhsPrecedence = self::MAX_PRECEDENCE;
}
$printedArg = $this->p($node, $opPrecedence, $lhsPrecedence);
if (($operatorString === '+' && $printedArg[0] === '+') ||
($operatorString === '-' && $printedArg[0] === '-')
) {
// Avoid printing +(+$a) as ++$a and similar.
$printedArg = '(' . $printedArg . ')';
}
return $prefix . $operatorString . $printedArg . $suffix;
}
/**
* Pretty-print a postfix operation while taking precedence into account.
*
* @param string $class Node class of operator
* @param string $operatorString String representation of the operator
* @param Node $node Node
* @param int $precedence Precedence of parent operator
* @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
*
* @return string Pretty printed postfix operation
*/
protected function pPostfixOp(string $class, Node $node, string $operatorString, int $precedence, int $lhsPrecedence): string {
$opPrecedence = $this->precedenceMap[$class][0];
$prefix = '';
$suffix = '';
if ($opPrecedence >= $precedence) {
$prefix = '(';
$suffix = ')';
$lhsPrecedence = self::MAX_PRECEDENCE;
}
if ($opPrecedence < $lhsPrecedence) {
$lhsPrecedence = $opPrecedence;
}
return $prefix . $this->p($node, $opPrecedence, $lhsPrecedence) . $operatorString . $suffix;
}
/**
* Pretty prints an array of nodes and implodes the printed values.
*
* @param Node[] $nodes Array of Nodes to be printed
* @param string $glue Character to implode with
*
* @return string Imploded pretty printed nodes> $pre
*/
protected function pImplode(array $nodes, string $glue = ''): string {
$pNodes = [];
foreach ($nodes as $node) {
if (null === $node) {
$pNodes[] = '';
} else {
$pNodes[] = $this->p($node);
}
}
return implode($glue, $pNodes);
}
/**
* Pretty prints an array of nodes and implodes the printed values with commas.
*
* @param Node[] $nodes Array of Nodes to be printed
*
* @return string Comma separated pretty printed nodes
*/
protected function pCommaSeparated(array $nodes): string {
return $this->pImplode($nodes, ', ');
}
/**
* Pretty prints a comma-separated list of nodes in multiline style, including comments.
*
* The result includes a leading newline and one level of indentation (same as pStmts).
*
* @param Node[] $nodes Array of Nodes to be printed
* @param bool $trailingComma Whether to use a trailing comma
*
* @return string Comma separated pretty printed nodes in multiline style
*/
protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma): string {
$this->indent();
$result = '';
$lastIdx = count($nodes) - 1;
foreach ($nodes as $idx => $node) {
if ($node !== null) {
$comments = $node->getComments();
if ($comments) {
$result .= $this->nl . $this->pComments($comments);
}
$result .= $this->nl . $this->p($node);
} else {
$result .= $this->nl;
}
if ($trailingComma || $idx !== $lastIdx) {
$result .= ',';
}
}
$this->outdent();
return $result;
}
/**
* Prints reformatted text of the passed comments.
*
* @param Comment[] $comments List of comments
*
* @return string Reformatted text of comments
*/
protected function pComments(array $comments): string {
$formattedComments = [];
foreach ($comments as $comment) {
$formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText());
}
return implode($this->nl, $formattedComments);
}
/**
* Perform a format-preserving pretty print of an AST.
*
* The format preservation is best effort. For some changes to the AST the formatting will not
* be preserved (at least not locally).
*
* In order to use this method a number of prerequisites must be satisfied:
* * The startTokenPos and endTokenPos attributes in the lexer must be enabled.
* * The CloningVisitor must be run on the AST prior to modification.
* * The original tokens must be provided, using the getTokens() method on the lexer.
*
* @param Node[] $stmts Modified AST with links to original AST
* @param Node[] $origStmts Original AST with token offset information
* @param Token[] $origTokens Tokens of the original code
*/
public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string {
$this->initializeNodeListDiffer();
$this->initializeLabelCharMap();
$this->initializeFixupMap();
$this->initializeRemovalMap();
$this->initializeInsertionMap();
$this->initializeListInsertionMap();
$this->initializeEmptyListInsertionMap();
$this->initializeModifierChangeMap();
$this->resetState();
$this->origTokens = new TokenStream($origTokens);
$this->preprocessNodes($stmts);
$pos = 0;
$result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null);
if (null !== $result) {
$result .= $this->origTokens->getTokenCode($pos, count($origTokens) - 1, 0);
} else {
// Fallback
// TODO Add <?php properly
$result = "<?php" . $this->newline . $this->pStmts($stmts, false);
}
return $this->handleMagicTokens($result);
}
protected function pFallback(Node $node, int $precedence, int $lhsPrecedence): string {
return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
}
/**
* Pretty prints a node.
*
* This method also handles formatting preservation for nodes.
*
* @param Node $node Node to be pretty printed
* @param int $precedence Precedence of parent operator
* @param int $lhsPrecedence Precedence for unary operator on LHS of binary operator
* @param bool $parentFormatPreserved Whether parent node has preserved formatting
*
* @return string Pretty printed node
*/
protected function p(
Node $node, int $precedence = self::MAX_PRECEDENCE, int $lhsPrecedence = self::MAX_PRECEDENCE,
bool $parentFormatPreserved = false
): string {
// No orig tokens means this is a normal pretty print without preservation of formatting
if (!$this->origTokens) {
return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence);
}
/** @var Node|null $origNode */
$origNode = $node->getAttribute('origNode');
if (null === $origNode) {
return $this->pFallback($node, $precedence, $lhsPrecedence);
}
$class = \get_class($node);
\assert($class === \get_class($origNode));
$startPos = $origNode->getStartTokenPos();
$endPos = $origNode->getEndTokenPos();
\assert($startPos >= 0 && $endPos >= 0);
$fallbackNode = $node;
if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) {
// Normalize node structure of anonymous classes
assert($origNode instanceof Expr\New_);
$node = PrintableNewAnonClassNode::fromNewNode($node);
$origNode = PrintableNewAnonClassNode::fromNewNode($origNode);
$class = PrintableNewAnonClassNode::class;
}
// InlineHTML node does not contain closing and opening PHP tags. If the parent formatting
// is not preserved, then we need to use the fallback code to make sure the tags are
// printed.
if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) {
return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
}
$indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos);
$type = $node->getType();
$fixupInfo = $this->fixupMap[$class] ?? null;
$result = '';
$pos = $startPos;
foreach ($node->getSubNodeNames() as $subNodeName) {
$subNode = $node->$subNodeName;
$origSubNode = $origNode->$subNodeName;
if ((!$subNode instanceof Node && $subNode !== null)
|| (!$origSubNode instanceof Node && $origSubNode !== null)
) {
if ($subNode === $origSubNode) {
// Unchanged, can reuse old code
continue;
}
if (is_array($subNode) && is_array($origSubNode)) {
// Array subnode changed, we might be able to reconstruct it
$listResult = $this->pArray(
$subNode, $origSubNode, $pos, $indentAdjustment, $class, $subNodeName,
$fixupInfo[$subNodeName] ?? null
);
if (null === $listResult) {
return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
}
$result .= $listResult;
continue;
}
// Check if this is a modifier change
$key = $class . '->' . $subNodeName;
if (!isset($this->modifierChangeMap[$key])) {
return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
}
[$printFn, $findToken] = $this->modifierChangeMap[$key];
$result .= $this->$printFn($subNode);
$pos = $this->origTokens->findRight($pos, $findToken);
continue;
}
$extraLeft = '';
$extraRight = '';
if ($origSubNode !== null) {
$subStartPos = $origSubNode->getStartTokenPos();
$subEndPos = $origSubNode->getEndTokenPos();
\assert($subStartPos >= 0 && $subEndPos >= 0);
} else {
if ($subNode === null) {
// Both null, nothing to do
continue;
}
// A node has been inserted, check if we have insertion information for it
$key = $type . '->' . $subNodeName;
if (!isset($this->insertionMap[$key])) {
return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
}
list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
if (null !== $findToken) {
$subStartPos = $this->origTokens->findRight($pos, $findToken)
+ (int) !$beforeToken;
} else {
$subStartPos = $pos;
}
if (null === $extraLeft && null !== $extraRight) {
// If inserting on the right only, skipping whitespace looks better
$subStartPos = $this->origTokens->skipRightWhitespace($subStartPos);
}
$subEndPos = $subStartPos - 1;
}
if (null === $subNode) {
// A node has been removed, check if we have removal information for it
$key = $type . '->' . $subNodeName;
if (!isset($this->removalMap[$key])) {
return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence);
}
// Adjust positions to account for additional tokens that must be skipped
$removalInfo = $this->removalMap[$key];
if (isset($removalInfo['left'])) {
$subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1;
}
if (isset($removalInfo['right'])) {
$subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1;
}
}
$result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment);
if (null !== $subNode) {
$result .= $extraLeft;
$origIndentLevel = $this->indentLevel;
$this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
// If it's the same node that was previously in this position, it certainly doesn't
// need fixup. It's important to check this here, because our fixup checks are more
// conservative than strictly necessary.
if (isset($fixupInfo[$subNodeName])
&& $subNode->getAttribute('origNode') !== $origSubNode
) {
$fixup = $fixupInfo[$subNodeName];
$res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos);
} else {
$res = $this->p($subNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
}
$this->safeAppend($result, $res);
$this->setIndentLevel($origIndentLevel);
$result .= $extraRight;
}
$pos = $subEndPos + 1;
}
$result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment);
return $result;
}
/**
* Perform a format-preserving pretty print of an array.
*
* @param Node[] $nodes New nodes
* @param Node[] $origNodes Original nodes
* @param int $pos Current token position (updated by reference)
* @param int $indentAdjustment Adjustment for indentation
* @param string $parentNodeClass Class of the containing node.
* @param string $subNodeName Name of array subnode.
* @param null|int $fixup Fixup information for array item nodes
*
* @return null|string Result of pretty print or null if cannot preserve formatting
*/
protected function pArray(
array $nodes, array $origNodes, int &$pos, int $indentAdjustment,
string $parentNodeClass, string $subNodeName, ?int $fixup
): ?string {
$diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes);
$mapKey = $parentNodeClass . '->' . $subNodeName;
$insertStr = $this->listInsertionMap[$mapKey] ?? null;
$isStmtList = $subNodeName === 'stmts';
$beforeFirstKeepOrReplace = true;
$skipRemovedNode = false;
$delayedAdd = [];
$lastElemIndentLevel = $this->indentLevel;
$insertNewline = false;
if ($insertStr === "\n") {
$insertStr = '';
$insertNewline = true;
}
if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
$startPos = $origNodes[0]->getStartTokenPos();
$endPos = $origNodes[0]->getEndTokenPos();
\assert($startPos >= 0 && $endPos >= 0);
if (!$this->origTokens->haveBraces($startPos, $endPos)) {
// This was a single statement without braces, but either additional statements
// have been added, or the single statement has been removed. This requires the
// addition of braces. For now fall back.
// TODO: Try to preserve formatting
return null;
}
}
$result = '';
foreach ($diff as $i => $diffElem) {
$diffType = $diffElem->type;
/** @var Node|string|null $arrItem */
$arrItem = $diffElem->new;
/** @var Node|string|null $origArrItem */
$origArrItem = $diffElem->old;
if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
$beforeFirstKeepOrReplace = false;
if ($origArrItem === null || $arrItem === null) {
// We can only handle the case where both are null
if ($origArrItem === $arrItem) {
continue;
}
return null;
}
if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
// We can only deal with nodes. This can occur for Names, which use string arrays.
return null;
}
$itemStartPos = $origArrItem->getStartTokenPos();
$itemEndPos = $origArrItem->getEndTokenPos();
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
$origIndentLevel = $this->indentLevel;
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
$this->setIndentLevel($lastElemIndentLevel);
$comments = $arrItem->getComments();
$origComments = $origArrItem->getComments();
$commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
\assert($commentStartPos >= 0);
if ($commentStartPos < $pos) {
// Comments may be assigned to multiple nodes if they start at the same position.
// Make sure we don't try to print them multiple times.
$commentStartPos = $itemStartPos;
}
if ($skipRemovedNode) {
if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
// We'd remove an opening/closing PHP tag.
// TODO: Preserve formatting.
$this->setIndentLevel($origIndentLevel);
return null;
}
} else {
$result .= $this->origTokens->getTokenCode(
$pos, $commentStartPos, $indentAdjustment);
}
if (!empty($delayedAdd)) {
/** @var Node $delayedAddNode */
foreach ($delayedAdd as $delayedAddNode) {
if ($insertNewline) {
$delayedAddComments = $delayedAddNode->getComments();
if ($delayedAddComments) {
$result .= $this->pComments($delayedAddComments) . $this->nl;
}
}
$this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true));
if ($insertNewline) {
$result .= $insertStr . $this->nl;
} else {
$result .= $insertStr;
}
}
$delayedAdd = [];
}
if ($comments !== $origComments) {
if ($comments) {
$result .= $this->pComments($comments) . $this->nl;
}
} else {
$result .= $this->origTokens->getTokenCode(
$commentStartPos, $itemStartPos, $indentAdjustment);
}
// If we had to remove anything, we have done so now.
$skipRemovedNode = false;
} elseif ($diffType === DiffElem::TYPE_ADD) {
if (null === $insertStr) {
// We don't have insertion information for this list type
return null;
}
if (!$arrItem instanceof Node) {
// We only support list insertion of nodes.
return null;
}
// We go multiline if the original code was multiline,
// or if it's an array item with a comment above it.
// Match always uses multiline formatting.
if ($insertStr === ', ' &&
($this->isMultiline($origNodes) || $arrItem->getComments() ||
$parentNodeClass === Expr\Match_::class)
) {
$insertStr = ',';
$insertNewline = true;
}
if ($beforeFirstKeepOrReplace) {
// Will be inserted at the next "replace" or "keep" element
$delayedAdd[] = $arrItem;
continue;
}
$itemStartPos = $pos;
$itemEndPos = $pos - 1;
$origIndentLevel = $this->indentLevel;
$this->setIndentLevel($lastElemIndentLevel);
if ($insertNewline) {
$result .= $insertStr . $this->nl;
$comments = $arrItem->getComments();
if ($comments) {
$result .= $this->pComments($comments) . $this->nl;
}
} else {
$result .= $insertStr;
}
} elseif ($diffType === DiffElem::TYPE_REMOVE) {
if (!$origArrItem instanceof Node) {
// We only support removal for nodes
return null;
}
$itemStartPos = $origArrItem->getStartTokenPos();
$itemEndPos = $origArrItem->getEndTokenPos();
\assert($itemStartPos >= 0 && $itemEndPos >= 0);
// Consider comments part of the node.
$origComments = $origArrItem->getComments();
if ($origComments) {
$itemStartPos = $origComments[0]->getStartTokenPos();
}
if ($i === 0) {
// If we're removing from the start, keep the tokens before the node and drop those after it,
// instead of the other way around.
$result .= $this->origTokens->getTokenCode(
$pos, $itemStartPos, $indentAdjustment);
$skipRemovedNode = true;
} else {
if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) {
// We'd remove an opening/closing PHP tag.
// TODO: Preserve formatting.
return null;
}
}
$pos = $itemEndPos + 1;
continue;
} else {
throw new \Exception("Shouldn't happen");
}
if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
$res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
} else {
$res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
}
$this->safeAppend($result, $res);
$this->setIndentLevel($origIndentLevel);
$pos = $itemEndPos + 1;
}
if ($skipRemovedNode) {
// TODO: Support removing single node.
return null;
}
if (!empty($delayedAdd)) {
if (!isset($this->emptyListInsertionMap[$mapKey])) {
return null;
}
list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
if (null !== $findToken) {
$insertPos = $this->origTokens->findRight($pos, $findToken) + 1;
$result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment);
$pos = $insertPos;
}
$first = true;
$result .= $extraLeft;
foreach ($delayedAdd as $delayedAddNode) {
if (!$first) {
$result .= $insertStr;
if ($insertNewline) {
$result .= $this->nl;
}
}
$result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
$first = false;
}
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
}
return $result;
}
/**
* Print node with fixups.
*
* Fixups here refer to the addition of extra parentheses, braces or other characters, that
* are required to preserve program semantics in a certain context (e.g. to maintain precedence
* or because only certain expressions are allowed in certain places).
*
* @param int $fixup Fixup type
* @param Node $subNode Subnode to print
* @param string|null $parentClass Class of parent node
* @param int $subStartPos Original start pos of subnode
* @param int $subEndPos Original end pos of subnode
*
* @return string Result of fixed-up print of subnode
*/
protected function pFixup(int $fixup, Node $subNode, ?string $parentClass, int $subStartPos, int $subEndPos): string {
switch ($fixup) {
case self::FIXUP_PREC_LEFT:
// We use a conservative approximation where lhsPrecedence == precedence.
if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
$precedence = $this->precedenceMap[$parentClass][1];
return $this->p($subNode, $precedence, $precedence);
}
break;
case self::FIXUP_PREC_RIGHT:
if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
$precedence = $this->precedenceMap[$parentClass][2];
return $this->p($subNode, $precedence, $precedence);
}
break;
case self::FIXUP_PREC_UNARY:
if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) {
$precedence = $this->precedenceMap[$parentClass][0];
return $this->p($subNode, $precedence, $precedence);
}
break;
case self::FIXUP_CALL_LHS:
if ($this->callLhsRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_DEREF_LHS:
if ($this->dereferenceLhsRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_STATIC_DEREF_LHS:
if ($this->staticDereferenceLhsRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)
) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_NEW:
if ($this->newOperandRequiresParens($subNode)
&& !$this->origTokens->haveParens($subStartPos, $subEndPos)) {
return '(' . $this->p($subNode) . ')';
}
break;
case self::FIXUP_BRACED_NAME:
case self::FIXUP_VAR_BRACED_NAME:
if ($subNode instanceof Expr
&& !$this->origTokens->haveBraces($subStartPos, $subEndPos)
) {
return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '')
. '{' . $this->p($subNode) . '}';
}
break;
case self::FIXUP_ENCAPSED:
if (!$subNode instanceof Node\InterpolatedStringPart
&& !$this->origTokens->haveBraces($subStartPos, $subEndPos)
) {
return '{' . $this->p($subNode) . '}';
}
break;
default:
throw new \Exception('Cannot happen');
}
// Nothing special to do
return $this->p($subNode);
}
/**
* Appends to a string, ensuring whitespace between label characters.
*
* Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x".
* Without safeAppend the result would be "echox", which does not preserve semantics.
*/
protected function safeAppend(string &$str, string $append): void {
if ($str === "") {
$str = $append;
return;
}
if ($append === "") {
return;
}
if (!$this->labelCharMap[$append[0]]
|| !$this->labelCharMap[$str[\strlen($str) - 1]]) {
$str .= $append;
} else {
$str .= " " . $append;
}
}
/**
* Determines whether the LHS of a call must be wrapped in parenthesis.
*
* @param Node $node LHS of a call
*
* @return bool Whether parentheses are required
*/
protected function callLhsRequiresParens(Node $node): bool {
return !($node instanceof Node\Name
|| $node instanceof Expr\Variable
|| $node instanceof Expr\ArrayDimFetch
|| $node instanceof Expr\FuncCall
|| $node instanceof Expr\MethodCall
|| $node instanceof Expr\NullsafeMethodCall
|| $node instanceof Expr\StaticCall
|| $node instanceof Expr\Array_);
}
/**
* Determines whether the LHS of an array/object operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function dereferenceLhsRequiresParens(Node $node): bool {
// A constant can occur on the LHS of an array/object deref, but not a static deref.
return $this->staticDereferenceLhsRequiresParens($node)
&& !$node instanceof Expr\ConstFetch;
}
/**
* Determines whether the LHS of a static operation must be wrapped in parentheses.
*
* @param Node $node LHS of dereferencing operation
*
* @return bool Whether parentheses are required
*/
protected function staticDereferenceLhsRequiresParens(Node $node): bool {
return !($node instanceof Expr\Variable
|| $node instanceof Node\Name
|| $node instanceof Expr\ArrayDimFetch
|| $node instanceof Expr\PropertyFetch
|| $node instanceof Expr\NullsafePropertyFetch
|| $node instanceof Expr\StaticPropertyFetch
|| $node instanceof Expr\FuncCall
|| $node instanceof Expr\MethodCall
|| $node instanceof Expr\NullsafeMethodCall
|| $node instanceof Expr\StaticCall
|| $node instanceof Expr\Array_
|| $node instanceof Scalar\String_
|| $node instanceof Expr\ClassConstFetch);
}
/**
* Determines whether an expression used in "new" or "instanceof" requires parentheses.
*
* @param Node $node New or instanceof operand
*
* @return bool Whether parentheses are required
*/
protected function newOperandRequiresParens(Node $node): bool {
if ($node instanceof Node\Name || $node instanceof Expr\Variable) {
return false;
}
if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch ||
$node instanceof Expr\NullsafePropertyFetch
) {
return $this->newOperandRequiresParens($node->var);
}
if ($node instanceof Expr\StaticPropertyFetch) {
return $this->newOperandRequiresParens($node->class);
}
return true;
}
/**
* Print modifiers, including trailing whitespace.
*
* @param int $modifiers Modifier mask to print
*
* @return string Printed modifiers
*/
protected function pModifiers(int $modifiers): string {
return ($modifiers & Modifiers::FINAL ? 'final ' : '')
. ($modifiers & Modifiers::ABSTRACT ? 'abstract ' : '')
. ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
. ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
. ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
. ($modifiers & Modifiers::STATIC ? 'static ' : '')
. ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
}
protected function pStatic(bool $static): string {
return $static ? 'static ' : '';
}
/**
* Determine whether a list of nodes uses multiline formatting.
*
* @param (Node|null)[] $nodes Node list
*
* @return bool Whether multiline formatting is used
*/
protected function isMultiline(array $nodes): bool {
if (\count($nodes) < 2) {
return false;
}
$pos = -1;
foreach ($nodes as $node) {
if (null === $node) {
continue;
}
$endPos = $node->getEndTokenPos() + 1;
if ($pos >= 0) {
$text = $this->origTokens->getTokenCode($pos, $endPos, 0);
if (false === strpos($text, "\n")) {
// We require that a newline is present between *every* item. If the formatting
// is inconsistent, with only some items having newlines, we don't consider it
// as multiline
return false;
}
}
$pos = $endPos;
}
return true;
}
/**
* Lazily initializes label char map.
*
* The label char map determines whether a certain character may occur in a label.
*/
protected function initializeLabelCharMap(): void {
if (isset($this->labelCharMap)) {
return;
}
$this->labelCharMap = [];
for ($i = 0; $i < 256; $i++) {
$chr = chr($i);
$this->labelCharMap[$chr] = $i >= 0x80 || ctype_alnum($chr);
}
if ($this->phpVersion->allowsDelInIdentifiers()) {
$this->labelCharMap["\x7f"] = true;
}
}
/**
* Lazily initializes node list differ.
*
* The node list differ is used to determine differences between two array subnodes.
*/
protected function initializeNodeListDiffer(): void {
if (isset($this->nodeListDiffer)) {
return;
}
$this->nodeListDiffer = new Internal\Differ(function ($a, $b) {
if ($a instanceof Node && $b instanceof Node) {
return $a === $b->getAttribute('origNode');
}
// Can happen for array destructuring
return $a === null && $b === null;
});
}
/**
* Lazily initializes fixup map.
*
* The fixup map is used to determine whether a certain subnode of a certain node may require
* some kind of "fixup" operation, e.g. the addition of parenthesis or braces.
*/
protected function initializeFixupMap(): void {
if (isset($this->fixupMap)) {
return;
}
$this->fixupMap = [
Expr\Instanceof_::class => [
'expr' => self::FIXUP_PREC_UNARY,
'class' => self::FIXUP_NEW,
],
Expr\Ternary::class => [
'cond' => self::FIXUP_PREC_LEFT,
'else' => self::FIXUP_PREC_RIGHT,
],
Expr\Yield_::class => ['value' => self::FIXUP_PREC_UNARY],
Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS],
Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS],
Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS],
Expr\ClassConstFetch::class => [
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\New_::class => ['class' => self::FIXUP_NEW],
Expr\MethodCall::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\NullsafeMethodCall::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\StaticPropertyFetch::class => [
'class' => self::FIXUP_STATIC_DEREF_LHS,
'name' => self::FIXUP_VAR_BRACED_NAME,
],
Expr\PropertyFetch::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Expr\NullsafePropertyFetch::class => [
'var' => self::FIXUP_DEREF_LHS,
'name' => self::FIXUP_BRACED_NAME,
],
Scalar\InterpolatedString::class => [
'parts' => self::FIXUP_ENCAPSED,
],
];
$binaryOps = [
BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class,
BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class,
BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class,
BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class,
BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class,
BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class,
];
foreach ($binaryOps as $binaryOp) {
$this->fixupMap[$binaryOp] = [
'left' => self::FIXUP_PREC_LEFT,
'right' => self::FIXUP_PREC_RIGHT
];
}
$prefixOps = [
Expr\Clone_::class, Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class,
Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class,
Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class,
Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class,
Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class,
AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class,
AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class,
AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class,
Expr\ArrowFunction::class, Expr\Throw_::class,
];
foreach ($prefixOps as $prefixOp) {
$this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_UNARY];
}
}
/**
* Lazily initializes the removal map.
*
* The removal map is used to determine which additional tokens should be removed when a
* certain node is replaced by null.
*/
protected function initializeRemovalMap(): void {
if (isset($this->removalMap)) {
return;
}
$stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE];
$stripLeft = ['left' => \T_WHITESPACE];
$stripRight = ['right' => \T_WHITESPACE];
$stripDoubleArrow = ['right' => \T_DOUBLE_ARROW];
$stripColon = ['left' => ':'];
$stripEquals = ['left' => '='];
$this->removalMap = [
'Expr_ArrayDimFetch->dim' => $stripBoth,
'ArrayItem->key' => $stripDoubleArrow,
'Expr_ArrowFunction->returnType' => $stripColon,
'Expr_Closure->returnType' => $stripColon,
'Expr_Exit->expr' => $stripBoth,
'Expr_Ternary->if' => $stripBoth,
'Expr_Yield->key' => $stripDoubleArrow,
'Expr_Yield->value' => $stripBoth,
'Param->type' => $stripRight,
'Param->default' => $stripEquals,
'Stmt_Break->num' => $stripBoth,
'Stmt_Catch->var' => $stripLeft,
'Stmt_ClassConst->type' => $stripRight,
'Stmt_ClassMethod->returnType' => $stripColon,
'Stmt_Class->extends' => ['left' => \T_EXTENDS],
'Stmt_Enum->scalarType' => $stripColon,
'Stmt_EnumCase->expr' => $stripEquals,
'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS],
'Stmt_Continue->num' => $stripBoth,
'Stmt_Foreach->keyVar' => $stripDoubleArrow,
'Stmt_Function->returnType' => $stripColon,
'Stmt_If->else' => $stripLeft,
'Stmt_Namespace->name' => $stripLeft,
'Stmt_Property->type' => $stripRight,
'PropertyItem->default' => $stripEquals,
'Stmt_Return->expr' => $stripBoth,
'Stmt_StaticVar->default' => $stripEquals,
'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft,
'Stmt_TryCatch->finally' => $stripLeft,
// 'Stmt_Case->cond': Replace with "default"
// 'Stmt_Class->name': Unclear what to do
// 'Stmt_Declare->stmts': Not a plain node
// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
];
}
protected function initializeInsertionMap(): void {
if (isset($this->insertionMap)) {
return;
}
// TODO: "yield" where both key and value are inserted doesn't work
// [$find, $beforeToken, $extraLeft, $extraRight]
$this->insertionMap = [
'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
'ArrayItem->key' => [null, false, null, ' => '],
'Expr_ArrowFunction->returnType' => [')', false, ': ', null],
'Expr_Closure->returnType' => [')', false, ': ', null],
'Expr_Ternary->if' => ['?', false, ' ', ' '],
'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
'Expr_Yield->value' => [\T_YIELD, false, ' ', null],
'Param->type' => [null, false, null, ' '],
'Param->default' => [null, false, ' = ', null],
'Stmt_Break->num' => [\T_BREAK, false, ' ', null],
'Stmt_Catch->var' => [null, false, ' ', null],
'Stmt_ClassMethod->returnType' => [')', false, ': ', null],
'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null],
'Stmt_Class->extends' => [null, false, ' extends ', null],
'Stmt_Enum->scalarType' => [null, false, ' : ', null],
'Stmt_EnumCase->expr' => [null, false, ' = ', null],
'Expr_PrintableNewAnonClass->extends' => [null, false, ' extends ', null],
'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null],
'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '],
'Stmt_Function->returnType' => [')', false, ': ', null],
'Stmt_If->else' => [null, false, ' ', null],
'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null],
'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '],
'PropertyItem->default' => [null, false, ' = ', null],
'Stmt_Return->expr' => [\T_RETURN, false, ' ', null],
'Stmt_StaticVar->default' => [null, false, ' = ', null],
//'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO
'Stmt_TryCatch->finally' => [null, false, ' ', null],
// 'Expr_Exit->expr': Complicated due to optional ()
// 'Stmt_Case->cond': Conversion from default to case
// 'Stmt_Class->name': Unclear
// 'Stmt_Declare->stmts': Not a proper node
// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
];
}
protected function initializeListInsertionMap(): void {
if (isset($this->listInsertionMap)) {
return;
}
$this->listInsertionMap = [
// special
//'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
//'Scalar_InterpolatedString->parts' => '',
Stmt\Catch_::class . '->types' => '|',
UnionType::class . '->types' => '|',
IntersectionType::class . '->types' => '&',
Stmt\If_::class . '->elseifs' => ' ',
Stmt\TryCatch::class . '->catches' => ' ',
// comma-separated lists
Expr\Array_::class . '->items' => ', ',
Expr\ArrowFunction::class . '->params' => ', ',
Expr\Closure::class . '->params' => ', ',
Expr\Closure::class . '->uses' => ', ',
Expr\FuncCall::class . '->args' => ', ',
Expr\Isset_::class . '->vars' => ', ',
Expr\List_::class . '->items' => ', ',
Expr\MethodCall::class . '->args' => ', ',
Expr\NullsafeMethodCall::class . '->args' => ', ',
Expr\New_::class . '->args' => ', ',
PrintableNewAnonClassNode::class . '->args' => ', ',
Expr\StaticCall::class . '->args' => ', ',
Stmt\ClassConst::class . '->consts' => ', ',
Stmt\ClassMethod::class . '->params' => ', ',
Stmt\Class_::class . '->implements' => ', ',
Stmt\Enum_::class . '->implements' => ', ',
PrintableNewAnonClassNode::class . '->implements' => ', ',
Stmt\Const_::class . '->consts' => ', ',
Stmt\Declare_::class . '->declares' => ', ',
Stmt\Echo_::class . '->exprs' => ', ',
Stmt\For_::class . '->init' => ', ',
Stmt\For_::class . '->cond' => ', ',
Stmt\For_::class . '->loop' => ', ',
Stmt\Function_::class . '->params' => ', ',
Stmt\Global_::class . '->vars' => ', ',
Stmt\GroupUse::class . '->uses' => ', ',
Stmt\Interface_::class . '->extends' => ', ',
Expr\Match_::class . '->arms' => ', ',
Stmt\Property::class . '->props' => ', ',
Stmt\StaticVar::class . '->vars' => ', ',
Stmt\TraitUse::class . '->traits' => ', ',
Stmt\TraitUseAdaptation\Precedence::class . '->insteadof' => ', ',
Stmt\Unset_::class . '->vars' => ', ',
Stmt\UseUse::class . '->uses' => ', ',
MatchArm::class . '->conds' => ', ',
AttributeGroup::class . '->attrs' => ', ',
// statement lists
Expr\Closure::class . '->stmts' => "\n",
Stmt\Case_::class . '->stmts' => "\n",
Stmt\Catch_::class . '->stmts' => "\n",
Stmt\Class_::class . '->stmts' => "\n",
Stmt\Enum_::class . '->stmts' => "\n",
PrintableNewAnonClassNode::class . '->stmts' => "\n",
Stmt\Interface_::class . '->stmts' => "\n",
Stmt\Trait_::class . '->stmts' => "\n",
Stmt\ClassMethod::class . '->stmts' => "\n",
Stmt\Declare_::class . '->stmts' => "\n",
Stmt\Do_::class . '->stmts' => "\n",
Stmt\ElseIf_::class . '->stmts' => "\n",
Stmt\Else_::class . '->stmts' => "\n",
Stmt\Finally_::class . '->stmts' => "\n",
Stmt\Foreach_::class . '->stmts' => "\n",
Stmt\For_::class . '->stmts' => "\n",
Stmt\Function_::class . '->stmts' => "\n",
Stmt\If_::class . '->stmts' => "\n",
Stmt\Namespace_::class . '->stmts' => "\n",
Stmt\Block::class . '->stmts' => "\n",
// Attribute groups
Stmt\Class_::class . '->attrGroups' => "\n",
Stmt\Enum_::class . '->attrGroups' => "\n",
Stmt\EnumCase::class . '->attrGroups' => "\n",
Stmt\Interface_::class . '->attrGroups' => "\n",
Stmt\Trait_::class . '->attrGroups' => "\n",
Stmt\Function_::class . '->attrGroups' => "\n",
Stmt\ClassMethod::class . '->attrGroups' => "\n",
Stmt\ClassConst::class . '->attrGroups' => "\n",
Stmt\Property::class . '->attrGroups' => "\n",
PrintableNewAnonClassNode::class . '->attrGroups' => ' ',
Expr\Closure::class . '->attrGroups' => ' ',
Expr\ArrowFunction::class . '->attrGroups' => ' ',
Param::class . '->attrGroups' => ' ',
Stmt\Switch_::class . '->cases' => "\n",
Stmt\TraitUse::class . '->adaptations' => "\n",
Stmt\TryCatch::class . '->stmts' => "\n",
Stmt\While_::class . '->stmts' => "\n",
// dummy for top-level context
'File->stmts' => "\n",
];
}
protected function initializeEmptyListInsertionMap(): void {
if (isset($this->emptyListInsertionMap)) {
return;
}
// TODO Insertion into empty statement lists.
// [$find, $extraLeft, $extraRight]
$this->emptyListInsertionMap = [
Expr\ArrowFunction::class . '->params' => ['(', '', ''],
Expr\Closure::class . '->uses' => [')', ' use (', ')'],
Expr\Closure::class . '->params' => ['(', '', ''],
Expr\FuncCall::class . '->args' => ['(', '', ''],
Expr\MethodCall::class . '->args' => ['(', '', ''],
Expr\NullsafeMethodCall::class . '->args' => ['(', '', ''],
Expr\New_::class . '->args' => ['(', '', ''],
PrintableNewAnonClassNode::class . '->args' => ['(', '', ''],
PrintableNewAnonClassNode::class . '->implements' => [null, ' implements ', ''],
Expr\StaticCall::class . '->args' => ['(', '', ''],
Stmt\Class_::class . '->implements' => [null, ' implements ', ''],
Stmt\Enum_::class . '->implements' => [null, ' implements ', ''],
Stmt\ClassMethod::class . '->params' => ['(', '', ''],
Stmt\Interface_::class . '->extends' => [null, ' extends ', ''],
Stmt\Function_::class . '->params' => ['(', '', ''],
Stmt\Interface_::class . '->attrGroups' => [null, '', "\n"],
Stmt\Class_::class . '->attrGroups' => [null, '', "\n"],
Stmt\ClassConst::class . '->attrGroups' => [null, '', "\n"],
Stmt\ClassMethod::class . '->attrGroups' => [null, '', "\n"],
Stmt\Function_::class . '->attrGroups' => [null, '', "\n"],
Stmt\Property::class . '->attrGroups' => [null, '', "\n"],
Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"],
Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '],
Expr\Closure::class . '->attrGroups' => [null, '', ' '],
PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''],
/* These cannot be empty to start with:
* Expr_Isset->vars
* Stmt_Catch->types
* Stmt_Const->consts
* Stmt_ClassConst->consts
* Stmt_Declare->declares
* Stmt_Echo->exprs
* Stmt_Global->vars
* Stmt_GroupUse->uses
* Stmt_Property->props
* Stmt_StaticVar->vars
* Stmt_TraitUse->traits
* Stmt_TraitUseAdaptation_Precedence->insteadof
* Stmt_Unset->vars
* Stmt_Use->uses
* UnionType->types
*/
/* TODO
* Stmt_If->elseifs
* Stmt_TryCatch->catches
* Expr_Array->items
* Expr_List->items
* Stmt_For->init
* Stmt_For->cond
* Stmt_For->loop
*/
];
}
protected function initializeModifierChangeMap(): void {
if (isset($this->modifierChangeMap)) {
return;
}
$this->modifierChangeMap = [
Stmt\ClassConst::class . '->flags' => ['pModifiers', \T_CONST],
Stmt\ClassMethod::class . '->flags' => ['pModifiers', \T_FUNCTION],
Stmt\Class_::class . '->flags' => ['pModifiers', \T_CLASS],
Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE],
PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS],
Param::class . '->flags' => ['pModifiers', \T_VARIABLE],
Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION],
Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN],
//Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
];
// List of integer subnodes that are not modifiers:
// Expr_Include->type
// Stmt_GroupUse->type
// Stmt_Use->type
// UseItem->type
}
}

View File

@ -0,0 +1,18 @@
<?php declare(strict_types=1);
namespace PhpParser;
/**
* A PHP token. On PHP 8.0 this extends from PhpToken.
*/
class Token extends Internal\TokenPolyfill {
/** Get (exclusive) zero-based end position of the token. */
public function getEndPos(): int {
return $this->pos + \strlen($this->text);
}
/** Get 1-based end line number of the token. */
public function getEndLine(): int {
return $this->line + \substr_count($this->text, "\n");
}
}

View File

@ -0,0 +1,63 @@
<?php declare(strict_types=1);
namespace PhpParser;
if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
function defineCompatibilityTokens(): void {
$compatTokens = [
// PHP 8.0
'T_NAME_QUALIFIED',
'T_NAME_FULLY_QUALIFIED',
'T_NAME_RELATIVE',
'T_MATCH',
'T_NULLSAFE_OBJECT_OPERATOR',
'T_ATTRIBUTE',
// PHP 8.1
'T_ENUM',
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
'T_READONLY',
];
// PHP-Parser might be used together with another library that also emulates some or all
// of these tokens. Perform a sanity-check that all already defined tokens have been
// assigned a unique ID.
$usedTokenIds = [];
foreach ($compatTokens as $token) {
if (\defined($token)) {
$tokenId = \constant($token);
if (!\is_int($tokenId)) {
throw new \Error(sprintf(
'Token %s has ID of type %s, should be int. ' .
'You may be using a library with broken token emulation',
$token, \gettype($tokenId)
));
}
$clashingToken = $usedTokenIds[$tokenId] ?? null;
if ($clashingToken !== null) {
throw new \Error(sprintf(
'Token %s has same ID as token %s, ' .
'you may be using a library with broken token emulation',
$token, $clashingToken
));
}
$usedTokenIds[$tokenId] = $token;
}
}
// Now define any tokens that have not yet been emulated. Try to assign IDs from -1
// downwards, but skip any IDs that may already be in use.
$newTokenId = -1;
foreach ($compatTokens as $token) {
if (!\defined($token)) {
while (isset($usedTokenIds[$newTokenId])) {
$newTokenId--;
}
\define($token, $newTokenId);
$newTokenId--;
}
}
}
defineCompatibilityTokens();
}