Laravel version update

Laravel version update
This commit is contained in:
Manish Verma
2018-08-06 18:48:58 +05:30
parent d143048413
commit 126fbb0255
13678 changed files with 1031482 additions and 778530 deletions

View File

@@ -1,40 +0,0 @@
<?php
namespace PhpParser;
/**
* @codeCoverageIgnore
*/
class Autoloader
{
/** @var bool Whether the autoloader has been registered. */
private static $registered = false;
/**
* Registers PhpParser\Autoloader as an SPL autoloader.
*
* @param bool $prepend Whether to prepend the autoloader instead of appending
*/
static public function register($prepend = false) {
if (self::$registered === true) {
return;
}
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
self::$registered = true;
}
/**
* Handles autoloading of classes.
*
* @param string $class A class name.
*/
static public function autoload($class) {
if (0 === strpos($class, 'PhpParser\\')) {
$fileName = __DIR__ . strtr(substr($class, 9), '\\', '/') . '.php';
if (file_exists($fileName)) {
require $fileName;
}
}
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
@@ -9,5 +9,5 @@ interface Builder
*
* @return Node The built node
*/
public function getNode();
}
public function getNode() : Node;
}

View File

@@ -1,8 +1,9 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
@@ -11,20 +12,20 @@ class Class_ extends Declaration
protected $name;
protected $extends = null;
protected $implements = array();
protected $type = 0;
protected $implements = [];
protected $flags = 0;
protected $uses = array();
protected $constants = array();
protected $properties = array();
protected $methods = array();
protected $uses = [];
protected $constants = [];
protected $properties = [];
protected $methods = [];
/**
* Creates a class builder.
*
* @param string $name Name of the class
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -36,7 +37,7 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function extend($class) {
$this->extends = $this->normalizeName($class);
$this->extends = BuilderHelpers::normalizeName($class);
return $this;
}
@@ -48,9 +49,9 @@ class Class_ extends Declaration
*
* @return $this The builder instance (for fluid interface)
*/
public function implement() {
foreach (func_get_args() as $interface) {
$this->implements[] = $this->normalizeName($interface);
public function implement(...$interfaces) {
foreach ($interfaces as $interface) {
$this->implements[] = BuilderHelpers::normalizeName($interface);
}
return $this;
@@ -62,7 +63,7 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function makeAbstract() {
$this->setModifier(Stmt\Class_::MODIFIER_ABSTRACT);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
return $this;
}
@@ -73,7 +74,7 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->setModifier(Stmt\Class_::MODIFIER_FINAL);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
return $this;
}
@@ -86,21 +87,21 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = $this->normalizeNode($stmt);
$stmt = BuilderHelpers::normalizeNode($stmt);
$targets = array(
'Stmt_TraitUse' => &$this->uses,
'Stmt_ClassConst' => &$this->constants,
'Stmt_Property' => &$this->properties,
'Stmt_ClassMethod' => &$this->methods,
);
$targets = [
Stmt\TraitUse::class => &$this->uses,
Stmt\ClassConst::class => &$this->constants,
Stmt\Property::class => &$this->properties,
Stmt\ClassMethod::class => &$this->methods,
];
$type = $stmt->getType();
if (!isset($targets[$type])) {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
$class = \get_class($stmt);
if (!isset($targets[$class])) {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
$targets[$type][] = $stmt;
$targets[$class][] = $stmt;
return $this;
}
@@ -110,12 +111,12 @@ class Class_ extends Declaration
*
* @return Stmt\Class_ The built class node
*/
public function getNode() {
return new Stmt\Class_($this->name, array(
'type' => $this->type,
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),
), $this->attributes);
], $this->attributes);
}
}
}

View File

@@ -1,14 +1,13 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\BuilderHelpers;
abstract class Declaration extends PhpParser\BuilderAbstract
abstract class Declaration implements PhpParser\Builder
{
protected $attributes = array();
protected $attributes = [];
abstract public function addStmt($stmt);
@@ -35,10 +34,10 @@ abstract class Declaration extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes['comments'] = array(
$this->normalizeDocComment($docComment)
);
$this->attributes['comments'] = [
BuilderHelpers::normalizeDocComment($docComment)
];
return $this;
}
}
}

View File

@@ -1,15 +1,16 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
abstract class FunctionLike extends Declaration
{
protected $returnByRef = false;
protected $params = array();
protected $params = [];
/** @var string|Node\Name|Node\NullableType|null */
protected $returnType = null;
/**
@@ -31,7 +32,7 @@ abstract class FunctionLike extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function addParam($param) {
$param = $this->normalizeNode($param);
$param = BuilderHelpers::normalizeNode($param);
if (!$param instanceof Node\Param) {
throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
@@ -60,18 +61,13 @@ abstract class FunctionLike extends Declaration
/**
* Sets the return type for PHP 7.
*
* @param string|Node\Name $type One of array, callable, string, int, float, bool,
* or a class/interface name.
* @param string|Node\Name|Node\NullableType $type One of array, callable, string, int, float,
* bool, iterable, or a class/interface name.
*
* @return $this The builder instance (for fluid interface)
*/
public function setReturnType($type)
{
if (in_array($type, array('array', 'callable', 'string', 'int', 'float', 'bool'))) {
$this->returnType = $type;
} else {
$this->returnType = $this->normalizeName($type);
}
public function setReturnType($type) {
$this->returnType = BuilderHelpers::normalizeType($type);
return $this;
}

View File

@@ -1,22 +1,23 @@
<?php
<?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 $name;
protected $stmts = array();
protected $stmts = [];
/**
* Creates a function builder.
*
* @param string $name Name of the function
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -28,7 +29,7 @@ class Function_ extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$this->stmts[] = $this->normalizeNode($stmt);
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
@@ -38,12 +39,12 @@ class Function_ extends FunctionLike
*
* @return Stmt\Function_ The built function node
*/
public function getNode() {
return new Stmt\Function_($this->name, array(
public function getNode() : Node {
return new Stmt\Function_($this->name, [
'byRef' => $this->returnByRef,
'params' => $this->params,
'returnType' => $this->returnType,
'stmts' => $this->stmts,
), $this->attributes);
], $this->attributes);
}
}

View File

@@ -1,24 +1,25 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
class Interface_ extends Declaration
{
protected $name;
protected $extends = array();
protected $constants = array();
protected $methods = array();
protected $extends = [];
protected $constants = [];
protected $methods = [];
/**
* Creates an interface builder.
*
* @param string $name Name of the interface
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -29,9 +30,9 @@ class Interface_ extends Declaration
*
* @return $this The builder instance (for fluid interface)
*/
public function extend() {
foreach (func_get_args() as $interface) {
$this->extends[] = $this->normalizeName($interface);
public function extend(...$interfaces) {
foreach ($interfaces as $interface) {
$this->extends[] = BuilderHelpers::normalizeName($interface);
}
return $this;
@@ -45,22 +46,16 @@ class Interface_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = $this->normalizeNode($stmt);
$stmt = BuilderHelpers::normalizeNode($stmt);
$type = $stmt->getType();
switch ($type) {
case 'Stmt_ClassConst':
$this->constants[] = $stmt;
break;
case 'Stmt_ClassMethod':
// we erase all statements in the body of an interface method
$stmt->stmts = null;
$this->methods[] = $stmt;
break;
default:
throw new \LogicException(sprintf('Unexpected node of type "%s"', $type));
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;
@@ -71,10 +66,10 @@ class Interface_ extends Declaration
*
* @return Stmt\Interface_ The built interface node
*/
public function getNode() {
return new Stmt\Interface_($this->name, array(
public function getNode() : PhpParser\Node {
return new Stmt\Interface_($this->name, [
'extends' => $this->extends,
'stmts' => array_merge($this->constants, $this->methods),
), $this->attributes);
], $this->attributes);
}
}
}

View File

@@ -1,23 +1,26 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Method extends FunctionLike
{
protected $name;
protected $type = 0;
protected $stmts = array();
protected $flags = 0;
/** @var array|null */
protected $stmts = [];
/**
* Creates a method builder.
*
* @param string $name Name of the method
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -27,7 +30,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->setModifier(Stmt\Class_::MODIFIER_PUBLIC);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
return $this;
}
@@ -38,7 +41,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->setModifier(Stmt\Class_::MODIFIER_PROTECTED);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
return $this;
}
@@ -49,7 +52,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->setModifier(Stmt\Class_::MODIFIER_PRIVATE);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
return $this;
}
@@ -60,7 +63,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function makeStatic() {
$this->setModifier(Stmt\Class_::MODIFIER_STATIC);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
return $this;
}
@@ -75,7 +78,7 @@ class Method extends FunctionLike
throw new \LogicException('Cannot make method with statements abstract');
}
$this->setModifier(Stmt\Class_::MODIFIER_ABSTRACT);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
$this->stmts = null; // abstract methods don't have statements
return $this;
@@ -87,7 +90,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->setModifier(Stmt\Class_::MODIFIER_FINAL);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
return $this;
}
@@ -104,7 +107,7 @@ class Method extends FunctionLike
throw new \LogicException('Cannot add statements to an abstract method');
}
$this->stmts[] = $this->normalizeNode($stmt);
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
@@ -114,13 +117,13 @@ class Method extends FunctionLike
*
* @return Stmt\ClassMethod The built method node
*/
public function getNode() {
return new Stmt\ClassMethod($this->name, array(
'type' => $this->type,
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,
), $this->attributes);
], $this->attributes);
}
}

View File

@@ -1,15 +1,16 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
class Namespace_ extends PhpParser\BuilderAbstract
class Namespace_ extends Declaration
{
private $name;
private $stmts = array();
private $stmts = [];
/**
* Creates a namespace builder.
@@ -17,7 +18,7 @@ class Namespace_ extends PhpParser\BuilderAbstract
* @param Node\Name|string|null $name Name of the namespace
*/
public function __construct($name) {
$this->name = null !== $name ? $this->normalizeName($name) : null;
$this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
}
/**
@@ -28,22 +29,7 @@ class Namespace_ extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$this->stmts[] = $this->normalizeNode($stmt);
return $this;
}
/**
* Adds multiple statements.
*
* @param array $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);
}
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
return $this;
}
@@ -53,7 +39,7 @@ class Namespace_ extends PhpParser\BuilderAbstract
*
* @return Node The built node
*/
public function getNode() {
return new Stmt\Namespace_($this->name, $this->stmts);
public function getNode() : Node {
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
}
}

View File

@@ -1,24 +1,30 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
class Param extends PhpParser\BuilderAbstract
class Param implements PhpParser\Builder
{
protected $name;
protected $default = null;
/** @var string|Node\Name|Node\NullableType|null */
protected $type = null;
protected $byRef = false;
protected $variadic = false;
/**
* Creates a parameter builder.
*
* @param string $name Name of the parameter
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -30,7 +36,7 @@ class Param extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function setDefault($value) {
$this->default = $this->normalizeValue($value);
$this->default = BuilderHelpers::normalizeValue($value);
return $this;
}
@@ -38,15 +44,14 @@ class Param extends PhpParser\BuilderAbstract
/**
* Sets type hint for the parameter.
*
* @param string|Node\Name $type Type hint to use
* @param string|Node\Name|Node\NullableType $type Type hint to use
*
* @return $this The builder instance (for fluid interface)
*/
public function setTypeHint($type) {
if (in_array($type, array('array', 'callable', 'string', 'int', 'float', 'bool'))) {
$this->type = $type;
} else {
$this->type = $this->normalizeName($type);
$this->type = BuilderHelpers::normalizeType($type);
if ($this->type == 'void') {
throw new \LogicException('Parameter type cannot be void');
}
return $this;
@@ -63,14 +68,26 @@ class Param extends PhpParser\BuilderAbstract
return $this;
}
/**
* Make the parameter variadic
*
* @return $this The builder instance (for fluid interface)
*/
public function makeVariadic() {
$this->variadic = true;
return $this;
}
/**
* Returns the built parameter node.
*
* @return Node\Param The built parameter node
*/
public function getNode() {
public function getNode() : Node {
return new Node\Param(
$this->name, $this->default, $this->type, $this->byRef
new Node\Expr\Variable($this->name),
$this->default, $this->type, $this->byRef, $this->variadic
);
}
}

View File

@@ -1,24 +1,25 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Stmt;
class Property extends PhpParser\BuilderAbstract
class Property implements PhpParser\Builder
{
protected $name;
protected $type = 0;
protected $flags = 0;
protected $default = null;
protected $attributes = array();
protected $attributes = [];
/**
* Creates a property builder.
*
* @param string $name Name of the property
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -28,7 +29,7 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function makePublic() {
$this->setModifier(Stmt\Class_::MODIFIER_PUBLIC);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
return $this;
}
@@ -39,7 +40,7 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function makeProtected() {
$this->setModifier(Stmt\Class_::MODIFIER_PROTECTED);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
return $this;
}
@@ -50,7 +51,7 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function makePrivate() {
$this->setModifier(Stmt\Class_::MODIFIER_PRIVATE);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
return $this;
}
@@ -61,7 +62,7 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function makeStatic() {
$this->setModifier(Stmt\Class_::MODIFIER_STATIC);
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
return $this;
}
@@ -74,7 +75,7 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function setDefault($value) {
$this->default = $this->normalizeValue($value);
$this->default = BuilderHelpers::normalizeValue($value);
return $this;
}
@@ -87,9 +88,9 @@ class Property extends PhpParser\BuilderAbstract
* @return $this The builder instance (for fluid interface)
*/
public function setDocComment($docComment) {
$this->attributes = array(
'comments' => array($this->normalizeDocComment($docComment))
);
$this->attributes = [
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
];
return $this;
}
@@ -99,13 +100,13 @@ class Property extends PhpParser\BuilderAbstract
*
* @return Stmt\Property The built property node
*/
public function getNode() {
public function getNode() : PhpParser\Node {
return new Stmt\Property(
$this->type !== 0 ? $this->type : Stmt\Class_::MODIFIER_PUBLIC,
array(
$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC,
[
new Stmt\PropertyProperty($this->name, $this->default)
),
],
$this->attributes
);
}
}
}

View File

@@ -1,23 +1,24 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser;
use PhpParser\Node\Name;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Stmt;
class Trait_ extends Declaration
{
protected $name;
protected $properties = array();
protected $methods = array();
protected $uses = [];
protected $properties = [];
protected $methods = [];
/**
* Creates an interface builder.
*
* @param string $name Name of the interface
*/
public function __construct($name) {
public function __construct(string $name) {
$this->name = $name;
}
@@ -29,12 +30,14 @@ class Trait_ extends Declaration
* @return $this The builder instance (for fluid interface)
*/
public function addStmt($stmt) {
$stmt = $this->normalizeNode($stmt);
$stmt = BuilderHelpers::normalizeNode($stmt);
if ($stmt instanceof Stmt\Property) {
$this->properties[] = $stmt;
} else if ($stmt instanceof Stmt\ClassMethod) {
} elseif ($stmt instanceof Stmt\ClassMethod) {
$this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt;
} else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
}
@@ -47,9 +50,11 @@ class Trait_ extends Declaration
*
* @return Stmt\Trait_ The built interface node
*/
public function getNode() {
public function getNode() : PhpParser\Node {
return new Stmt\Trait_(
$this->name, array_merge($this->properties, $this->methods), $this->attributes
$this->name, [
'stmts' => array_merge($this->uses, $this->properties, $this->methods)
], $this->attributes
);
}
}

View File

@@ -1,15 +1,14 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Builder;
use PhpParser\BuilderAbstract;
use PhpParser\Builder;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Stmt;
/**
* @method $this as(string $alias) Sets alias for used name.
*/
class Use_ extends BuilderAbstract {
class Use_ implements Builder
{
protected $name;
protected $type;
protected $alias = null;
@@ -20,8 +19,8 @@ class Use_ extends BuilderAbstract {
* @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
* @param int $type One of the Stmt\Use_::TYPE_* constants
*/
public function __construct($name, $type) {
$this->name = $this->normalizeName($name);
public function __construct($name, int $type) {
$this->name = BuilderHelpers::normalizeName($name);
$this->type = $type;
}
@@ -32,27 +31,19 @@ class Use_ extends BuilderAbstract {
*
* @return $this The builder instance (for fluid interface)
*/
protected function as_($alias) {
public function as(string $alias) {
$this->alias = $alias;
return $this;
}
public function __call($name, $args) {
if (method_exists($this, $name . '_')) {
return call_user_func_array(array($this, $name . '_'), $args);
}
throw new \LogicException(sprintf('Method "%s" does not exist', $name));
}
/**
* Returns the built node.
*
* @return Node The built node
*/
public function getNode() {
$alias = null !== $this->alias ? $this->alias : $this->name->getLast();
return new Stmt\Use_(array(
new Stmt\UseUse($this->name, $alias)
), $this->type);
public function getNode() : Node {
return new Stmt\Use_([
new Stmt\UseUse($this->name, $this->alias)
], $this->type);
}
}

View File

@@ -1,131 +0,0 @@
<?php
namespace PhpParser;
use PhpParser\Node\Name;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
use PhpParser\Node\Scalar;
use PhpParser\Comment;
abstract class BuilderAbstract implements Builder {
/**
* Normalizes a node: Converts builder objects to nodes.
*
* @param Node|Builder $node The node to normalize
*
* @return Node The normalized node
*/
protected function normalizeNode($node) {
if ($node instanceof Builder) {
return $node->getNode();
} elseif ($node instanceof Node) {
return $node;
}
throw new \LogicException('Expected node or builder object');
}
/**
* Normalizes a name: Converts plain string names to PhpParser\Node\Name.
*
* @param Name|string $name The name to normalize
*
* @return Name The normalized name
*/
protected function normalizeName($name) {
if ($name instanceof Name) {
return $name;
} elseif (is_string($name)) {
if (!$name) {
throw new \LogicException('Name cannot be empty');
}
if ($name[0] == '\\') {
return new Name\FullyQualified(substr($name, 1));
} elseif (0 === strpos($name, 'namespace\\')) {
return new Name\Relative(substr($name, strlen('namespace\\')));
} else {
return new Name($name);
}
}
throw new \LogicException('Name must be a string or an instance of PhpParser\Node\Name');
}
/**
* Normalizes a value: Converts nulls, booleans, integers,
* floats, strings and arrays into their respective nodes
*
* @param mixed $value The value to normalize
*
* @return Expr The normalized value
*/
protected function normalizeValue($value) {
if ($value instanceof Node) {
return $value;
} elseif (is_null($value)) {
return new Expr\ConstFetch(
new Name('null')
);
} elseif (is_bool($value)) {
return new Expr\ConstFetch(
new Name($value ? 'true' : 'false')
);
} elseif (is_int($value)) {
return new Scalar\LNumber($value);
} elseif (is_float($value)) {
return new Scalar\DNumber($value);
} elseif (is_string($value)) {
return new Scalar\String_($value);
} elseif (is_array($value)) {
$items = array();
$lastKey = -1;
foreach ($value as $itemKey => $itemValue) {
// for consecutive, numeric keys don't generate keys
if (null !== $lastKey && ++$lastKey === $itemKey) {
$items[] = new Expr\ArrayItem(
$this->normalizeValue($itemValue)
);
} else {
$lastKey = null;
$items[] = new Expr\ArrayItem(
$this->normalizeValue($itemValue),
$this->normalizeValue($itemKey)
);
}
}
return new Expr\Array_($items);
} else {
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
*/
protected function normalizeDocComment($docComment) {
if ($docComment instanceof Comment\Doc) {
return $docComment;
} else if (is_string($docComment)) {
return new Comment\Doc($docComment);
} else {
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
}
}
/**
* Sets a modifier in the $this->type property.
*
* @param int $modifier Modifier to set
*/
protected function setModifier($modifier) {
Stmt\Class_::verifyModifier($this->type, $modifier);
$this->type |= $modifier;
}
}

View File

@@ -1,21 +1,16 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Builder;
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 PhpParser\Node\Stmt\Use_;
/**
* The following methods use reserved keywords, so their implementation is defined with an underscore and made available
* with the reserved name through __call() magic.
*
* @method Builder\Namespace_ namespace(string $name) Creates a namespace builder.
* @method Builder\Class_ class(string $name) Creates a class builder.
* @method Builder\Interface_ interface(string $name) Creates an interface builder.
* @method Builder\Trait_ trait(string $name) Creates a trait builder.
* @method Builder\Function_ function(string $name) Creates a function builder.
* @method Builder\Use_ use(string $name) Creates a namespace/class use builder.
*/
class BuilderFactory
{
/**
@@ -25,7 +20,7 @@ class BuilderFactory
*
* @return Builder\Namespace_ The created namespace builder
*/
protected function _namespace($name) {
public function namespace($name) : Builder\Namespace_ {
return new Builder\Namespace_($name);
}
@@ -36,7 +31,7 @@ class BuilderFactory
*
* @return Builder\Class_ The created class builder
*/
protected function _class($name) {
public function class(string $name) : Builder\Class_ {
return new Builder\Class_($name);
}
@@ -47,7 +42,7 @@ class BuilderFactory
*
* @return Builder\Interface_ The created interface builder
*/
protected function _interface($name) {
public function interface(string $name) : Builder\Interface_ {
return new Builder\Interface_($name);
}
@@ -58,7 +53,7 @@ class BuilderFactory
*
* @return Builder\Trait_ The created trait builder
*/
protected function _trait($name) {
public function trait(string $name) : Builder\Trait_ {
return new Builder\Trait_($name);
}
@@ -69,7 +64,7 @@ class BuilderFactory
*
* @return Builder\Method The created method builder
*/
public function method($name) {
public function method(string $name) : Builder\Method {
return new Builder\Method($name);
}
@@ -80,7 +75,7 @@ class BuilderFactory
*
* @return Builder\Param The created parameter builder
*/
public function param($name) {
public function param(string $name) : Builder\Param {
return new Builder\Param($name);
}
@@ -91,7 +86,7 @@ class BuilderFactory
*
* @return Builder\Property The created property builder
*/
public function property($name) {
public function property(string $name) : Builder\Property {
return new Builder\Property($name);
}
@@ -102,26 +97,176 @@ class BuilderFactory
*
* @return Builder\Function_ The created function builder
*/
protected function _function($name) {
public function function(string $name) : Builder\Function_ {
return new Builder\Function_($name);
}
/**
* Creates a namespace/class use builder.
*
* @param string|Node\Name Name to alias
* @param string|Node\Name $name Name to alias
*
* @return Builder\Use_ The create use builder
*/
protected function _use($name) {
public function use($name) : Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_NORMAL);
}
public function __call($name, array $args) {
if (method_exists($this, '_' . $name)) {
return call_user_func_array(array($this, '_' . $name), $args);
/**
* Creates node a for a literal value.
*
* @param Expr|bool|null|int|float|string|array $value $value
*
* @return Expr
*/
public function val($value) : Expr {
return BuilderHelpers::normalizeValue($value);
}
/**
* 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 Arg[]
*/
public function args(array $args) : array {
$normalizedArgs = [];
foreach ($args as $arg) {
if ($arg instanceof Arg) {
$normalizedArgs[] = $arg;
} else {
$normalizedArgs[] = new Arg(BuilderHelpers::normalizeValue($arg));
}
}
return $normalizedArgs;
}
/**
* Creates a function call node.
*
* @param string|Name|Expr $name Function name
* @param array $args Function arguments
*
* @return Expr\FuncCall
*/
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
*
* @return Expr\MethodCall
*/
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
*
* @return Expr\StaticCall
*/
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
*
* @return Expr\New_
*/
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
*
* @return Expr\ConstFetch
*/
public function constFetch($name) : Expr\ConstFetch {
return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
}
/**
* Creates a class constant fetch node.
*
* @param string|Name|Expr $class Class name
* @param string|Identifier $name Constant name
*
* @return Expr\ClassConstFetch
*/
public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch(
BuilderHelpers::normalizeNameOrExpr($class),
BuilderHelpers::normalizeIdentifier($name)
);
}
/**
* Creates nested Concat nodes from a list of expressions.
*
* @param Expr|string ...$exprs Expressions or literal strings
*
* @return Concat
*/
public function concat(...$exprs) : Concat {
$numExprs = count($exprs);
if ($numExprs < 2) {
throw new \LogicException('Expected at least two expressions');
}
throw new \LogicException(sprintf('Method "%s" does not exist', $name));
$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
* @return 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,277 @@
<?php declare(strict_types=1);
namespace PhpParser;
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();
} elseif ($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 or Node\Expr');
}
/**
* 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 {
return self::normalizeNameCommon($name, false);
}
/**
* 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) {
return self::normalizeNameCommon($name, true);
}
/**
* Normalizes a name: Converts string names to Name nodes, optionally allowing expressions.
*
* @param Expr|Name|string $name The name to normalize
* @param bool $allowExpr Whether to also allow expressions
*
* @return Name|Expr The normalized name, or expression (if allowed)
*/
private static function normalizeNameCommon($name, bool $allowExpr) {
if ($name instanceof Name) {
return $name;
} elseif (is_string($name)) {
if (!$name) {
throw new \LogicException('Name cannot be empty');
}
if ($name[0] === '\\') {
return new Name\FullyQualified(substr($name, 1));
} elseif (0 === strpos($name, 'namespace\\')) {
return new Name\Relative(substr($name, strlen('namespace\\')));
} else {
return new Name($name);
}
}
if ($allowExpr) {
if ($name instanceof Expr) {
return $name;
}
throw new \LogicException(
'Name must be a string or an instance of Node\Name or Node\Expr'
);
} else {
throw new \LogicException('Name must be a string or an instance of Node\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|NullableType $type The type to normalize
*
* @return Name|Identifier|NullableType The normalized type
*/
public static function normalizeType($type) {
if (!is_string($type)) {
if (!$type instanceof Name && !$type instanceof Identifier
&& !$type instanceof NullableType) {
throw new \LogicException(
'Type must be a string, or an instance of Name, Identifier or NullableType');
}
return $type;
}
$nullable = false;
if (strlen($type) > 0 && $type[0] === '?') {
$nullable = true;
$type = substr($type, 1);
}
$builtinTypes = [
'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object'
];
$lowerType = strtolower($type);
if (in_array($lowerType, $builtinTypes)) {
$type = new Identifier($lowerType);
} else {
$type = self::normalizeName($type);
}
if ($nullable && (string) $type === 'void') {
throw new \LogicException('void type cannot be nullable');
}
return $nullable ? new Node\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;
} elseif (is_null($value)) {
return new Expr\ConstFetch(
new Name('null')
);
} elseif (is_bool($value)) {
return new Expr\ConstFetch(
new Name($value ? 'true' : 'false')
);
} elseif (is_int($value)) {
return new Scalar\LNumber($value);
} elseif (is_float($value)) {
return new Scalar\DNumber($value);
} elseif (is_string($value)) {
return new Scalar\String_($value);
} elseif (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 Expr\ArrayItem(
self::normalizeValue($itemValue)
);
} else {
$lastKey = null;
$items[] = new Expr\ArrayItem(
self::normalizeValue($itemValue),
self::normalizeValue($itemKey)
);
}
}
return new Expr\Array_($items);
} else {
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;
} elseif (is_string($docComment)) {
return new Comment\Doc($docComment);
} else {
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
}
}
/**
* 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 {
Stmt\Class_::verifyModifier($modifiers, $modifier);
return $modifiers | $modifier;
}
}

View File

@@ -1,24 +1,29 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
class Comment
class Comment implements \JsonSerializable
{
protected $text;
protected $line;
protected $filePos;
protected $tokenPos;
/**
* 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 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($text, $startLine = -1, $startFilePos = -1) {
public function __construct(
string $text, int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1
) {
$this->text = $text;
$this->line = $startLine;
$this->filePos = $startFilePos;
$this->tokenPos = $startTokenPos;
}
/**
@@ -26,56 +31,43 @@ class Comment
*
* @return string The comment text (including comment delimiters like /*)
*/
public function getText() {
public function getText() : string {
return $this->text;
}
/**
* Sets the comment text.
*
* @param string $text The comment text (including comment delimiters like /*)
*
* @deprecated Construct a new comment instead
*/
public function setText($text) {
$this->text = $text;
}
/**
* Gets the line number the comment started on.
*
* @return int Line number
*/
public function getLine() {
public function getLine() : int {
return $this->line;
}
/**
* Sets the line number the comment started on.
*
* @param int $line Line number
*
* @deprecated Construct a new comment instead
*/
public function setLine($line) {
$this->line = $line;
}
/**
* Gets the file offset the comment started on.
*
* @return int File offset
*/
public function getFilePos() {
public function getFilePos() : int {
return $this->filePos;
}
/**
* Gets the token offset the comment started on.
*
* @return int Token offset
*/
public function getTokenPos() : int {
return $this->tokenPos;
}
/**
* Gets the comment text.
*
* @return string The comment text (including comment delimiters like /*)
*/
public function __toString() {
public function __toString() : string {
return $this->text;
}
@@ -136,9 +128,17 @@ class Comment
return $text;
}
private function getShortestWhitespacePrefixLen($str) {
/**
* 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 = INF;
$shortestPrefixLen = \INF;
foreach ($lines as $line) {
preg_match('(^\s*)', $line, $matches);
$prefixLen = strlen($matches[0]);
@@ -148,4 +148,20 @@ class Comment
}
return $shortestPrefixLen;
}
}
/**
* @return array
* @psalm-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,
'line' => $this->line,
'filePos' => $this->filePos,
'tokenPos' => $this->tokenPos,
];
}
}

View File

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

View File

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

View File

@@ -0,0 +1,226 @@
<?php
namespace PhpParser;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar;
/**
* 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
{
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);
}
private function evaluate(Expr $expr) {
if ($expr instanceof Scalar\LNumber
|| $expr instanceof Scalar\DNumber
|| $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 = [];
foreach ($expr->items as $item) {
if (null !== $item->key) {
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
} else {
$array[] = $this->evaluate($item->value);
}
}
return $array;
}
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);
}
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');
}
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

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
@@ -14,12 +14,12 @@ class Error extends \RuntimeException
* @param array|int $attributes Attributes of node/token where error occurred
* (or start line of error -- deprecated)
*/
public function __construct($message, $attributes = array()) {
$this->rawMessage = (string) $message;
public function __construct(string $message, $attributes = []) {
$this->rawMessage = $message;
if (is_array($attributes)) {
$this->attributes = $attributes;
} else {
$this->attributes = array('startLine' => $attributes);
$this->attributes = ['startLine' => $attributes];
}
$this->updateMessage();
}
@@ -29,7 +29,7 @@ class Error extends \RuntimeException
*
* @return string Error message
*/
public function getRawMessage() {
public function getRawMessage() : string {
return $this->rawMessage;
}
@@ -38,8 +38,8 @@ class Error extends \RuntimeException
*
* @return int Error start line
*/
public function getStartLine() {
return isset($this->attributes['startLine']) ? $this->attributes['startLine'] : -1;
public function getStartLine() : int {
return $this->attributes['startLine'] ?? -1;
}
/**
@@ -47,27 +47,36 @@ class Error extends \RuntimeException
*
* @return int Error end line
*/
public function getEndLine() {
return isset($this->attributes['endLine']) ? $this->attributes['endLine'] : -1;
public function getEndLine() : int {
return $this->attributes['endLine'] ?? -1;
}
/**
* Gets the attributes of the node/token the error occurred at.
*
* @return array
*/
public function getAttributes() {
public function getAttributes() : array {
return $this->attributes;
}
/**
* Sets the attributes of the node/token the error occurred at.
*
* @param array $attributes
*/
public function setAttributes(array $attributes) {
$this->attributes = $attributes;
$this->updateMessage();
}
/**
* Sets the line of the PHP file the error occurred in.
*
* @param string $message Error message
*/
public function setRawMessage($message) {
$this->rawMessage = (string) $message;
public function setRawMessage(string $message) {
$this->rawMessage = $message;
$this->updateMessage();
}
@@ -76,8 +85,8 @@ class Error extends \RuntimeException
*
* @param int $line Error start line
*/
public function setStartLine($line) {
$this->attributes['startLine'] = (int) $line;
public function setStartLine(int $line) {
$this->attributes['startLine'] = $line;
$this->updateMessage();
}
@@ -88,8 +97,8 @@ class Error extends \RuntimeException
*
* @return bool
*/
public function hasColumnInfo() {
return isset($this->attributes['startFilePos']) && isset($this->attributes['endFilePos']);
public function hasColumnInfo() : bool {
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
}
/**
@@ -98,7 +107,7 @@ class Error extends \RuntimeException
* @param string $code Source code of the file
* @return int
*/
public function getStartColumn($code) {
public function getStartColumn(string $code) : int {
if (!$this->hasColumnInfo()) {
throw new \RuntimeException('Error does not have column information');
}
@@ -112,7 +121,7 @@ class Error extends \RuntimeException
* @param string $code Source code of the file
* @return int
*/
public function getEndColumn($code) {
public function getEndColumn(string $code) : int {
if (!$this->hasColumnInfo()) {
throw new \RuntimeException('Error does not have column information');
}
@@ -120,7 +129,30 @@ class Error extends \RuntimeException
return $this->toColumn($code, $this->attributes['endFilePos']);
}
private function toColumn($code, $pos) {
/**
* 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');
}
@@ -145,14 +177,4 @@ class Error extends \RuntimeException
$this->message .= ' on line ' . $this->getStartLine();
}
}
/** @deprecated Use getStartLine() instead */
public function getRawLine() {
return $this->getStartLine();
}
/** @deprecated Use setStartLine() instead */
public function setRawLine($line) {
$this->setStartLine($line);
}
}

View File

@@ -0,0 +1,13 @@
<?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);
}

View File

@@ -0,0 +1,46 @@
<?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 $errors = [];
public function handleError(Error $error) {
$this->errors[] = $error;
}
/**
* Get collected errors.
*
* @return Error[]
*/
public function getErrors() : array {
return $this->errors;
}
/**
* Check whether there are any errors.
*
* @return bool
*/
public function hasErrors() : bool {
return !empty($this->errors);
}
/**
* Reset/clear collected errors.
*/
public function clearErrors() {
$this->errors = [];
}
}

View File

@@ -0,0 +1,18 @@
<?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) {
throw $error;
}
}

View File

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

View File

@@ -0,0 +1,164 @@
<?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.
*
* @internal
*/
class Differ
{
private $isEqual;
/**
* Create differ over the given equality relation.
*
* @param callable $isEqual Equality relation with signature function($a, $b) : bool
*/
public function __construct(callable $isEqual) {
$this->isEqual = $isEqual;
}
/**
* Calculate diff (edit script) from $old to $new.
*
* @param array $old Original array
* @param array $new New array
*
* @return DiffElem[] Diff (edit script)
*/
public function diff(array $old, array $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 array $old Original array
* @param array $new New array
*
* @return DiffElem[] Diff (edit script), including replace operations
*/
public function diffWithReplacements(array $old, array $new) {
return $this->coalesceReplacements($this->diff($old, $new));
}
private function calculateTrace(array $a, array $b) {
$n = \count($a);
$m = \count($b);
$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)($a[$x], $b[$y])) {
$x++;
$y++;
}
$v[$k] = $x;
if ($x >= $n && $y >= $m) {
return [$trace, $x, $y];
}
}
}
throw new \Exception('Should not happen');
}
private function extractDiff(array $trace, int $x, int $y, array $a, array $b) {
$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, $a[$x-1], $b[$y-1]);
$x--;
$y--;
}
if ($d === 0) {
break;
}
while ($x > $prevX) {
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null);
$x--;
}
while ($y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$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) {
$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,56 @@
<?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\Arg[] Arguments */
public $args;
/** @var null|Node\Name Name of extended class */
public $extends;
/** @var Node\Name[] Names of implemented interfaces */
public $implements;
/** @var Node\Stmt[] Statements */
public $stmts;
public function __construct(
array $args, Node\Name $extends = null, array $implements, array $stmts, array $attributes
) {
parent::__construct($attributes);
$this->args = $args;
$this->extends = $extends;
$this->implements = $implements;
$this->stmts = $stmts;
}
public static function fromNewNode(Expr\New_ $newNode) {
$class = $newNode->class;
assert($class instanceof Node\Stmt\Class_);
assert($class->name === null);
return new self(
$newNode->args, $class->extends, $class->implements,
$class->stmts, $newNode->getAttributes()
);
}
public function getType() : string {
return 'Expr_PrintableNewAnonClass';
}
public function getSubNodeNames() : array {
return ['args', 'extends', 'implements', 'stmts'];
}
}

View File

@@ -0,0 +1,256 @@
<?php declare(strict_types=1);
namespace PhpParser\Internal;
/**
* Provides operations on token streams, for use by pretty printer.
*
* @internal
*/
class TokenStream
{
/** @var array Tokens (in token_get_all format) */
private $tokens;
/** @var int[] Map from position to indentation */
private $indentMap;
/**
* Create token stream instance.
*
* @param array $tokens Tokens in token_get_all() 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
*
* @return bool
*/
public function haveParens(int $startPos, int $endPos) : bool {
return $this->haveTokenImmediativelyBefore($startPos, '(')
&& $this->haveTokenImmediatelyAfter($endPos, ')');
}
/**
* Whether the given position is immediately surrounded by braces.
*
* @param int $startPos Start position
* @param int $endPos End position
*
* @return bool
*/
public function haveBraces(int $startPos, int $endPos) : bool {
return $this->haveTokenImmediativelyBefore($startPos, '{')
&& $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 haveTokenImmediativelyBefore(int $pos, $expectedTokenType) : bool {
$tokens = $this->tokens;
$pos--;
for (; $pos >= 0; $pos--) {
$tokenType = $tokens[$pos][0];
if ($tokenType === $expectedTokenType) {
return true;
}
if ($tokenType !== \T_WHITESPACE
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
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 (; $pos < \count($tokens); $pos++) {
$tokenType = $tokens[$pos][0];
if ($tokenType === $expectedTokenType) {
return true;
}
if ($tokenType !== \T_WHITESPACE
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
break;
}
}
return false;
}
public function skipLeft(int $pos, $skipTokenType) {
$tokens = $this->tokens;
$pos = $this->skipLeftWhitespace($pos);
if ($skipTokenType === \T_WHITESPACE) {
return $pos;
}
if ($tokens[$pos][0] !== $skipTokenType) {
// Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token');
}
$pos--;
return $this->skipLeftWhitespace($pos);
}
public function skipRight(int $pos, $skipTokenType) {
$tokens = $this->tokens;
$pos = $this->skipRightWhitespace($pos);
if ($skipTokenType === \T_WHITESPACE) {
return $pos;
}
if ($tokens[$pos][0] !== $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) {
$tokens = $this->tokens;
for (; $pos >= 0; $pos--) {
$type = $tokens[$pos][0];
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
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) {
$tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0];
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
break;
}
}
return $pos;
}
public function findRight($pos, $findTokenType) {
$tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0];
if ($type === $findTokenType) {
return $pos;
}
}
return -1;
}
/**
* 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];
if (\is_array($token)) {
$type = $token[0];
$content = $token[1];
if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) {
$result .= $content;
} else {
// TODO Handle non-space indentation
if ($indent < 0) {
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
} elseif ($indent > 0) {
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
} else {
$result .= $content;
}
}
} else {
$result .= $token;
}
}
return $result;
}
/**
* Precalculate the indentation at every token position.
*
* @return int[] Token position to indentation map
*/
private function calcIndentMap() {
$indentMap = [];
$indent = 0;
foreach ($this->tokens as $token) {
$indentMap[] = $indent;
if ($token[0] === \T_WHITESPACE) {
$content = $token[1];
$newlinePos = \strrpos($content, "\n");
if (false !== $newlinePos) {
$indent = \strlen($content) - $newlinePos - 1;
}
}
}
// Add a sentinel for one past end of the file
$indentMap[] = $indent;
return $indentMap;
}
}

View File

@@ -0,0 +1,101 @@
<?php declare(strict_types=1);
namespace PhpParser;
class JsonDecoder
{
/** @var \ReflectionClass[] Node type to reflection class map */
private $reflectionClassCache;
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);
}
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);
/** @var Node $node */
$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
);
}
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];
}
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

@@ -1,8 +1,7 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Parser\Tokens;
class Lexer
@@ -12,6 +11,7 @@ class Lexer
protected $pos;
protected $line;
protected $filePos;
protected $prevCloseTagHasNewline;
protected $tokenMap;
protected $dropTokens;
@@ -27,85 +27,172 @@ class Lexer
* 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
* first three. For more info see getNextToken() docs.
*/
public function __construct(array $options = array()) {
public function __construct(array $options = []) {
// map from internal tokens to PhpParser tokens
$this->tokenMap = $this->createTokenMap();
// map of tokens to drop while lexing (the map is only used for isset lookup,
// that's why the value is simply set to 1; the value is never actually used.)
$this->dropTokens = array_fill_keys(
array(T_WHITESPACE, T_OPEN_TAG, T_COMMENT, T_DOC_COMMENT), 1
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT], 1
);
// the usedAttributes member is a map of the used attribute names to a dummy
// value (here "true")
$options += array(
'usedAttributes' => array('comments', 'startLine', 'endLine'),
);
$options += [
'usedAttributes' => ['comments', 'startLine', 'endLine'],
];
$this->usedAttributes = array_fill_keys($options['usedAttributes'], true);
}
/**
* Initializes the lexer for lexing the provided source code.
*
* @param string $code The source code to lex
* This function does not throw if lexing errors occur. Instead, errors may be retrieved using
* the getErrors() method.
*
* @throws Error on lexing errors (unterminated comment or unexpected character)
* @param string $code The source code to lex
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
* ErrorHandler\Throwing
*/
public function startLexing($code) {
$scream = ini_set('xdebug.scream', '0');
$this->resetErrors();
$this->tokens = @token_get_all($code);
$this->handleErrors();
if (false !== $scream) {
ini_set('xdebug.scream', $scream);
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
if (null === $errorHandler) {
$errorHandler = new ErrorHandler\Throwing();
}
$this->code = $code; // keep the code around for __halt_compiler() handling
$this->pos = -1;
$this->line = 1;
$this->filePos = 0;
}
protected function resetErrors() {
if (function_exists('error_clear_last')) {
error_clear_last();
} else {
// set error_get_last() to defined state by forcing an undefined variable error
set_error_handler(function() { return false; }, 0);
@$undefinedVariable;
restore_error_handler();
// If inline HTML occurs without preceding code, treat it as if it had a leading newline.
// This ensures proper composability, because having a newline is the "safe" assumption.
$this->prevCloseTagHasNewline = true;
$scream = ini_set('xdebug.scream', '0');
error_clear_last();
$this->tokens = @token_get_all($code);
$this->handleErrors($errorHandler);
if (false !== $scream) {
ini_set('xdebug.scream', $scream);
}
}
protected function handleErrors() {
$error = error_get_last();
if (null === $error) {
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i];
if ($chr === 'b' || $chr === 'B') {
// HHVM does not treat b" tokens correctly, so ignore these
continue;
}
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' => $line,
'endLine' => $line,
'startFilePos' => $i,
'endFilePos' => $i,
]));
}
}
/**
* Check whether comment token is unterminated.
*
* @return bool
*/
private function isUnterminatedComment($token) : bool {
return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT)
&& substr($token[1], 0, 2) === '/*'
&& substr($token[1], -2) !== '*/';
}
/**
* Check whether an error *may* have occurred during tokenization.
*
* @return bool
*/
private function errorMayHaveOccurred() : bool {
if (defined('HHVM_VERSION')) {
// In HHVM token_get_all() does not throw warnings, so we need to conservatively
// assume that an error occurred
return true;
}
return null !== error_get_last();
}
protected function handleErrors(ErrorHandler $errorHandler) {
if (!$this->errorMayHaveOccurred()) {
return;
}
if (preg_match(
'~^Unterminated comment starting line ([0-9]+)$~',
$error['message'], $matches
)) {
throw new Error('Unterminated comment', (int) $matches[1]);
// PHP's error handling for token_get_all() is rather bad, so if we want detailed
// error information we need to compute it ourselves. Invalid character errors are
// detected by finding "gaps" in the token array. Unterminated comments are detected
// by checking if a trailing comment has a "*/" at the end.
$filePos = 0;
$line = 1;
foreach ($this->tokens as $token) {
$tokenValue = \is_string($token) ? $token : $token[1];
$tokenLen = \strlen($tokenValue);
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
// Something is missing, must be an invalid character
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
$this->handleInvalidCharacterRange(
$filePos, $nextFilePos, $line, $errorHandler);
$filePos = (int) $nextFilePos;
}
$filePos += $tokenLen;
$line += substr_count($tokenValue, "\n");
}
if (preg_match(
'~^Unexpected character in input: \'(.)\' \(ASCII=([0-9]+)\)~s',
$error['message'], $matches
)) {
throw new Error(sprintf(
'Unexpected character "%s" (ASCII %d)',
$matches[1], $matches[2]
));
if ($filePos !== \strlen($this->code)) {
if (substr($this->code, $filePos, 2) === '/*') {
// Unlike PHP, HHVM will drop unterminated comments entirely
$comment = substr($this->code, $filePos);
$errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $line,
'endLine' => $line + substr_count($comment, "\n"),
'startFilePos' => $filePos,
'endFilePos' => $filePos + \strlen($comment),
]));
// Emulate the PHP behavior
$isDocComment = isset($comment[3]) && $comment[3] === '*';
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
} else {
// Invalid characters at the end of the input
$this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler);
}
return;
}
// PHP cuts error message after null byte, so need special case
if (preg_match('~^Unexpected character in input: \'$~', $error['message'])) {
throw new Error('Unexpected null byte');
if (count($this->tokens) > 0) {
// Check for unterminated comment
$lastToken = $this->tokens[count($this->tokens) - 1];
if ($this->isUnterminatedComment($lastToken)) {
$errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $line - substr_count($lastToken[1], "\n"),
'endLine' => $line,
'startFilePos' => $filePos - \strlen($lastToken[1]),
'endFilePos' => $filePos,
]));
}
}
}
@@ -131,9 +218,9 @@ class Lexer
*
* @return int Token id
*/
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
$startAttributes = array();
$endAttributes = array();
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int {
$startAttributes = [];
$endAttributes = [];
while (1) {
if (isset($this->tokens[++$this->pos])) {
@@ -166,15 +253,20 @@ class Lexer
} elseif (!isset($this->dropTokens[$token[0]])) {
$value = $token[1];
$id = $this->tokenMap[$token[0]];
if (\T_CLOSE_TAG === $token[0]) {
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n");
} elseif (\T_INLINE_HTML === $token[0]) {
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
}
$this->line += substr_count($value, "\n");
$this->filePos += \strlen($value);
} else {
if (T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0]) {
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if (isset($this->usedAttributes['comments'])) {
$comment = T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1], $this->line, $this->filePos)
: new Comment($token[1], $this->line, $this->filePos);
$comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos)
: new Comment($token[1], $this->line, $this->filePos, $this->pos);
$startAttributes['comments'][] = $comment;
}
}
@@ -210,7 +302,7 @@ class Lexer
*
* @return array Array of tokens in token_get_all() format
*/
public function getTokens() {
public function getTokens() : array {
return $this->tokens;
}
@@ -219,7 +311,7 @@ class Lexer
*
* @return string Remaining text
*/
public function handleHaltCompiler() {
public function handleHaltCompiler() : string {
// text after T_HALT_COMPILER, still including ();
$textAfter = substr($this->code, $this->filePos);
@@ -234,7 +326,7 @@ class Lexer
$this->pos = count($this->tokens);
// return with (); removed
return (string) substr($textAfter, strlen($matches[0])); // (string) converts false to ''
return substr($textAfter, strlen($matches[0]));
}
/**
@@ -246,26 +338,26 @@ class Lexer
*
* @return array The token map
*/
protected function createTokenMap() {
$tokenMap = array();
protected function createTokenMap() : array {
$tokenMap = [];
// 256 is the minimum possible token number, as everything below
// it is an ASCII value
for ($i = 256; $i < 1000; ++$i) {
if (T_DOUBLE_COLON === $i) {
if (\T_DOUBLE_COLON === $i) {
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
} elseif(T_OPEN_TAG_WITH_ECHO === $i) {
} elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
$tokenMap[$i] = Tokens::T_ECHO;
} elseif(T_CLOSE_TAG === $i) {
} elseif(\T_CLOSE_TAG === $i) {
// T_CLOSE_TAG is equivalent to ';'
$tokenMap[$i] = ord(';');
} elseif ('UNKNOWN' !== $name = token_name($i)) {
if ('T_HASHBANG' === $name) {
// HHVM uses a special token for #! hashbang lines
$tokenMap[$i] = Tokens::T_INLINE_HTML;
} else if (defined($name = 'PhpParser\Parser\Tokens::' . $name)) {
} elseif (defined($name = Tokens::class . '::' . $name)) {
// Other tokens can be mapped directly
$tokenMap[$i] = constant($name);
}
@@ -274,11 +366,11 @@ class Lexer
// HHVM uses a special token for numbers that overflow to double
if (defined('T_ONUMBER')) {
$tokenMap[T_ONUMBER] = Tokens::T_DNUMBER;
$tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER;
}
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
if (defined('T_COMPILER_HALT_OFFSET')) {
$tokenMap[T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
$tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
}
return $tokenMap;

View File

@@ -1,205 +1,8 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Lexer;
use PhpParser\Parser\Tokens;
/**
* ATTENTION: This code is WRITE-ONLY. Do not try to read it.
*/
class Emulative extends \PhpParser\Lexer
{
protected $newKeywords;
protected $inObjectAccess;
const T_ELLIPSIS = 1001;
const T_POW = 1002;
const T_POW_EQUAL = 1003;
const T_COALESCE = 1004;
const T_SPACESHIP = 1005;
const T_YIELD_FROM = 1006;
const PHP_7_0 = '7.0.0dev';
const PHP_5_6 = '5.6.0rc1';
const PHP_5_5 = '5.5.0beta1';
public function __construct(array $options = array()) {
parent::__construct($options);
$newKeywordsPerVersion = array(
self::PHP_5_5 => array(
'finally' => Tokens::T_FINALLY,
'yield' => Tokens::T_YIELD,
),
);
$this->newKeywords = array();
foreach ($newKeywordsPerVersion as $version => $newKeywords) {
if (version_compare(PHP_VERSION, $version, '>=')) {
break;
}
$this->newKeywords += $newKeywords;
}
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
return;
}
$this->tokenMap[self::T_COALESCE] = Tokens::T_COALESCE;
$this->tokenMap[self::T_SPACESHIP] = Tokens::T_SPACESHIP;
$this->tokenMap[self::T_YIELD_FROM] = Tokens::T_YIELD_FROM;
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
return;
}
$this->tokenMap[self::T_ELLIPSIS] = Tokens::T_ELLIPSIS;
$this->tokenMap[self::T_POW] = Tokens::T_POW;
$this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL;
}
public function startLexing($code) {
$this->inObjectAccess = false;
$preprocessedCode = $this->preprocessCode($code);
parent::startLexing($preprocessedCode);
if ($preprocessedCode !== $code) {
$this->postprocessTokens();
}
// Set code property back to the original code, so __halt_compiler()
// handling and (start|end)FilePos attributes use the correct offsets
$this->code = $code;
}
/*
* Replaces new features in the code by ~__EMU__{NAME}__{DATA}__~ sequences.
* ~LABEL~ is never valid PHP code, that's why we can (to some degree) safely
* use it here.
* Later when preprocessing the tokens these sequences will either be replaced
* by real tokens or replaced with their original content (e.g. if they occurred
* inside a string, i.e. a place where they don't have a special meaning).
*/
protected function preprocessCode($code) {
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
return $code;
}
$code = str_replace('??', '~__EMU__COALESCE__~', $code);
$code = str_replace('<=>', '~__EMU__SPACESHIP__~', $code);
$code = preg_replace_callback('(yield[ \n\r\t]+from)', function($matches) {
// Encoding $0 in order to preserve exact whitespace
return '~__EMU__YIELDFROM__' . bin2hex($matches[0]) . '__~';
}, $code);
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
return $code;
}
$code = str_replace('...', '~__EMU__ELLIPSIS__~', $code);
$code = preg_replace('((?<!/)\*\*=)', '~__EMU__POWEQUAL__~', $code);
$code = preg_replace('((?<!/)\*\*(?!/))', '~__EMU__POW__~', $code);
return $code;
}
/*
* Replaces the ~__EMU__...~ sequences with real tokens or their original
* value.
*/
protected function postprocessTokens() {
// we need to manually iterate and manage a count because we'll change
// the tokens array on the way
for ($i = 0, $c = count($this->tokens); $i < $c; ++$i) {
// first check that the following tokens are of form ~LABEL~,
// then match the __EMU__... sequence.
if ('~' === $this->tokens[$i]
&& isset($this->tokens[$i + 2])
&& '~' === $this->tokens[$i + 2]
&& T_STRING === $this->tokens[$i + 1][0]
&& preg_match('(^__EMU__([A-Z]++)__(?:([A-Za-z0-9]++)__)?$)', $this->tokens[$i + 1][1], $matches)
) {
if ('ELLIPSIS' === $matches[1]) {
$replace = array(
array(self::T_ELLIPSIS, '...', $this->tokens[$i + 1][2])
);
} else if ('POW' === $matches[1]) {
$replace = array(
array(self::T_POW, '**', $this->tokens[$i + 1][2])
);
} else if ('POWEQUAL' === $matches[1]) {
$replace = array(
array(self::T_POW_EQUAL, '**=', $this->tokens[$i + 1][2])
);
} else if ('COALESCE' === $matches[1]) {
$replace = array(
array(self::T_COALESCE, '??', $this->tokens[$i + 1][2])
);
} else if ('SPACESHIP' === $matches[1]) {
$replace = array(
array(self::T_SPACESHIP, '<=>', $this->tokens[$i + 1][2]),
);
} else if ('YIELDFROM' === $matches[1]) {
$content = hex2bin($matches[2]);
$replace = array(
array(self::T_YIELD_FROM, $content, $this->tokens[$i + 1][2] - substr_count($content, "\n"))
);
} else {
throw new \RuntimeException('Invalid __EMU__ sequence');
}
array_splice($this->tokens, $i, 3, $replace);
$c -= 3 - count($replace);
// for multichar tokens (e.g. strings) replace any ~__EMU__...~ sequences
// in their content with the original character sequence
} elseif (is_array($this->tokens[$i])
&& 0 !== strpos($this->tokens[$i][1], '__EMU__')
) {
$this->tokens[$i][1] = preg_replace_callback(
'(~__EMU__([A-Z]++)__(?:([A-Za-z0-9]++)__)?~)',
array($this, 'restoreContentCallback'),
$this->tokens[$i][1]
);
}
}
}
/*
* This method is a callback for restoring EMU sequences in
* multichar tokens (like strings) to their original value.
*/
public function restoreContentCallback(array $matches) {
if ('ELLIPSIS' === $matches[1]) {
return '...';
} else if ('POW' === $matches[1]) {
return '**';
} else if ('POWEQUAL' === $matches[1]) {
return '**=';
} else if ('COALESCE' === $matches[1]) {
return '??';
} else if ('SPACESHIP' === $matches[1]) {
return '<=>';
} else if ('YIELDFROM' === $matches[1]) {
return hex2bin($matches[2]);
} else {
return $matches[0];
}
}
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
$token = parent::getNextToken($value, $startAttributes, $endAttributes);
// replace new keywords by their respective tokens. This is not done
// if we currently are in an object access (e.g. in $obj->namespace
// "namespace" stays a T_STRING tokens and isn't converted to T_NAMESPACE)
if (Tokens::T_STRING === $token && !$this->inObjectAccess) {
if (isset($this->newKeywords[strtolower($value)])) {
return $this->newKeywords[strtolower($value)];
}
} else {
// keep track of whether we currently are in an object access (after ->)
$this->inObjectAccess = Tokens::T_OBJECT_OPERATOR === $token;
}
return $token;
}
/* No features requiring emulation have been added in PHP > 7.0 */
}

View File

@@ -0,0 +1,285 @@
<?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 $namespace;
/** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
protected $aliases = [];
/** @var Name[][] Same as $aliases but preserving original case */
protected $origAliases = [];
/** @var ErrorHandler Error handler */
protected $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) {
$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 int $type One of Stmt\Use_::TYPE_*
* @param array $errorAttrs Attributes to use to report an error
*/
public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) {
// 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() {
return $this->namespace;
}
/**
* Get resolved name.
*
* @param Name $name Name to resolve
* @param int $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) {
// 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 int $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 int $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, $type) {
$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) {
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) {
$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

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser;
@@ -9,28 +9,88 @@ interface Node
*
* @return string Type of the node
*/
public function getType();
public function getType() : string;
/**
* Gets the names of the sub nodes.
*
* @return array Names of sub nodes
*/
public function getSubNodeNames();
public function getSubNodeNames() : array;
/**
* Gets line the node started in (alias of getStartLine).
*
* @return int Start line (or -1 if not available)
*/
public function getLine() : int;
/**
* Gets line the node started in.
*
* @return int Line
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int Start line (or -1 if not available)
*/
public function getLine();
public function getStartLine() : int;
/**
* Sets line the node started in.
* Gets the line the node ended in.
*
* @param int $line Line
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
*
* @return int End line (or -1 if not available)
*/
public function setLine($line);
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.
@@ -41,13 +101,22 @@ interface Node
*/
public function getDocComment();
/**
* 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);
/**
* Sets an attribute on a node.
*
* @param string $key
* @param mixed $value
*/
public function setAttribute($key, $value);
public function setAttribute(string $key, $value);
/**
* Returns whether an attribute exists.
@@ -56,7 +125,7 @@ interface Node
*
* @return bool
*/
public function hasAttribute($key);
public function hasAttribute(string $key) : bool;
/**
* Returns the value of an attribute.
@@ -66,12 +135,19 @@ interface Node
*
* @return mixed
*/
public function &getAttribute($key, $default = null);
public function getAttribute(string $key, $default = null);
/**
* Returns all attributes for the given node.
* Returns all the attributes of this node.
*
* @return array
*/
public function getAttributes();
}
public function getAttributes() : array;
/**
* Replaces all the attributes of this node.
*
* @param array $attributes
*/
public function setAttributes(array $attributes);
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node;
@@ -21,14 +21,18 @@ class Arg extends NodeAbstract
* @param bool $unpack Whether to unpack the argument
* @param array $attributes Additional attributes
*/
public function __construct(Expr $value, $byRef = false, $unpack = false, array $attributes = array()) {
public function __construct(Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = []) {
parent::__construct($attributes);
$this->value = $value;
$this->byRef = $byRef;
$this->unpack = $unpack;
}
public function getSubNodeNames() {
return array('value', 'byRef', 'unpack');
public function getSubNodeNames() : array {
return ['value', 'byRef', 'unpack'];
}
public function getType() : string {
return 'Arg';
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -18,13 +18,17 @@ class ArrayDimFetch extends Expr
* @param null|Expr $dim Array index / dim
* @param array $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $dim = null, array $attributes = array()) {
public function __construct(Expr $var, Expr $dim = null, array $attributes = []) {
parent::__construct($attributes);
$this->var = $var;
$this->dim = $dim;
}
public function getSubnodeNames() {
return array('var', 'dim');
public function getSubNodeNames() : array {
return ['var', 'dim'];
}
public function getType() : string {
return 'Expr_ArrayDimFetch';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -21,14 +21,18 @@ class ArrayItem extends Expr
* @param bool $byRef Whether to assign by reference
* @param array $attributes Additional attributes
*/
public function __construct(Expr $value, Expr $key = null, $byRef = false, array $attributes = array()) {
public function __construct(Expr $value, Expr $key = null, bool $byRef = false, array $attributes = []) {
parent::__construct($attributes);
$this->key = $key;
$this->value = $value;
$this->byRef = $byRef;
}
public function getSubNodeNames() {
return array('key', 'value', 'byRef');
public function getSubNodeNames() : array {
return ['key', 'value', 'byRef'];
}
public function getType() : string {
return 'Expr_ArrayItem';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -19,12 +19,16 @@ class Array_ extends Expr
* @param ArrayItem[] $items Items of the array
* @param array $attributes Additional attributes
*/
public function __construct(array $items = array(), array $attributes = array()) {
public function __construct(array $items = [], array $attributes = []) {
parent::__construct($attributes);
$this->items = $items;
}
public function getSubNodeNames() {
return array('items');
public function getSubNodeNames() : array {
return ['items'];
}
public function getType() : string {
return 'Expr_Array';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -18,13 +18,17 @@ class Assign extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('var', 'expr');
public function getSubNodeNames() : array {
return ['var', 'expr'];
}
public function getType() : string {
return 'Expr_Assign';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -18,13 +18,13 @@ abstract class AssignOp extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('var', 'expr');
public function getSubNodeNames() : array {
return ['var', 'expr'];
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -18,13 +18,17 @@ class AssignRef extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $var, Expr $expr, array $attributes = array()) {
public function __construct(Expr $var, Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->var = $var;
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('var', 'expr');
public function getSubNodeNames() : array {
return ['var', 'expr'];
}
public function getType() : string {
return 'Expr_AssignRef';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -12,19 +12,29 @@ abstract class BinaryOp extends Expr
public $right;
/**
* Constructs a bitwise and node.
* Constructs a binary operator node.
*
* @param Expr $left The left hand side expression
* @param Expr $right The right hand side expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $left, Expr $right, array $attributes = array()) {
public function __construct(Expr $left, Expr $right, array $attributes = []) {
parent::__construct($attributes);
$this->left = $left;
$this->right = $right;
}
public function getSubNodeNames() {
return array('left', '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.
*
* @return string
*/
abstract public function getOperatorSigil() : string;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
@@ -6,4 +6,11 @@ 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

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
@@ -6,4 +6,11 @@ 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

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\BinaryOp;
@@ -6,4 +6,11 @@ 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,16 @@ class BitwiseNot extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
public function getType() : string {
return 'Expr_BitwiseNot';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,16 @@ class BooleanNot extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
public function getType() : string {
return 'Expr_BooleanNot';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,12 @@ abstract class Cast extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr\Cast;
@@ -6,4 +6,7 @@ use PhpParser\Node\Expr\Cast;
class Double extends Cast
{
public function getType() : string {
return 'Expr_Cast_Double';
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,16 @@ class Clone_ extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
public function getType() : string {
return 'Expr_Clone';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -16,9 +16,9 @@ class Closure extends Expr implements FunctionLike
public $params;
/** @var ClosureUse[] use()s */
public $uses;
/** @var null|string|Node\Name Return type */
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
public $returnType;
/** @var Node[] Statements */
/** @var Node\Stmt[] Statements */
public $stmts;
/**
@@ -33,25 +33,26 @@ class Closure extends Expr implements FunctionLike
* 'stmts' => array(): Statements
* @param array $attributes Additional attributes
*/
public function __construct(array $subNodes = array(), array $attributes = array()) {
public function __construct(array $subNodes = [], array $attributes = []) {
parent::__construct($attributes);
$this->static = isset($subNodes['static']) ? $subNodes['static'] : false;
$this->byRef = isset($subNodes['byRef']) ? $subNodes['byRef'] : false;
$this->params = isset($subNodes['params']) ? $subNodes['params'] : array();
$this->uses = isset($subNodes['uses']) ? $subNodes['uses'] : array();
$this->returnType = isset($subNodes['returnType']) ? $subNodes['returnType'] : null;
$this->stmts = isset($subNodes['stmts']) ? $subNodes['stmts'] : array();
$this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? [];
$this->uses = $subNodes['uses'] ?? [];
$returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->stmts = $subNodes['stmts'] ?? [];
}
public function getSubNodeNames() {
return array('static', 'byRef', 'params', 'uses', 'returnType', 'stmts');
public function getSubNodeNames() : array {
return ['static', 'byRef', 'params', 'uses', 'returnType', 'stmts'];
}
public function returnsByRef() {
public function returnsByRef() : bool {
return $this->byRef;
}
public function getParams() {
public function getParams() : array {
return $this->params;
}
@@ -59,7 +60,12 @@ class Closure extends Expr implements FunctionLike
return $this->returnType;
}
public function getStmts() {
/** @return Node\Stmt[] */
public function getStmts() : array {
return $this->stmts;
}
public function getType() : string {
return 'Expr_Closure';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -6,7 +6,7 @@ use PhpParser\Node\Expr;
class ClosureUse extends Expr
{
/** @var string Name of variable */
/** @var Expr\Variable Variable to use */
public $var;
/** @var bool Whether to use by reference */
public $byRef;
@@ -14,17 +14,21 @@ class ClosureUse extends Expr
/**
* Constructs a closure use node.
*
* @param string $var Name of variable
* @param bool $byRef Whether to use by reference
* @param array $attributes Additional attributes
* @param Expr\Variable $var Variable to use
* @param bool $byRef Whether to use by reference
* @param array $attributes Additional attributes
*/
public function __construct($var, $byRef = false, array $attributes = array()) {
public function __construct(Expr\Variable $var, bool $byRef = false, array $attributes = []) {
parent::__construct($attributes);
$this->var = $var;
$this->byRef = $byRef;
}
public function getSubNodeNames() {
return array('var', 'byRef');
public function getSubNodeNames() : array {
return ['var', 'byRef'];
}
public function getType() : string {
return 'Expr_ClosureUse';
}
}

View File

@@ -1,9 +1,9 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
class ConstFetch extends Expr
{
@@ -16,12 +16,16 @@ class ConstFetch extends Expr
* @param Name $name Constant name
* @param array $attributes Additional attributes
*/
public function __construct(Name $name, array $attributes = array()) {
public function __construct(Name $name, array $attributes = []) {
parent::__construct($attributes);
$this->name = $name;
}
public function getSubNodeNames() {
return array('name');
public function getSubNodeNames() : array {
return ['name'];
}
public function getType() : string {
return 'Expr_ConstFetch';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,16 @@ class Empty_ extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
public function getType() : string {
return 'Expr_Empty';
}
}

View File

@@ -0,0 +1,31 @@
<?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 $attributes Additional attributes
*/
public function __construct(array $attributes = []) {
parent::__construct($attributes);
}
public function getSubNodeNames() : array {
return [];
}
public function getType() : string {
return 'Expr_Error';
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
@@ -15,12 +15,16 @@ class ErrorSuppress extends Expr
* @param Expr $expr Expression
* @param array $attributes Additional attributes
*/
public function __construct(Expr $expr, array $attributes = array()) {
public function __construct(Expr $expr, array $attributes = []) {
parent::__construct($attributes);
$this->expr = $expr;
}
public function getSubNodeNames() {
return array('expr');
public function getSubNodeNames() : array {
return ['expr'];
}
public function getType() : string {
return 'Expr_ErrorSuppress';
}
}

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