Updated nikic/PhpParser to version 5.2.0

This commit is contained in:
netkas 2024-09-19 15:44:14 -04:00
parent a1404490bb
commit 923117999b
277 changed files with 12140 additions and 10393 deletions

View file

@ -73,6 +73,7 @@ This update introduces a refactored code-base, code quality improvements, and be
- Updated Symfony/polyfill-ctype to version 1.31.0 - Updated Symfony/polyfill-ctype to version 1.31.0
- Updated Symfony/polyfill-mbstring to version 1.31.0 - Updated Symfony/polyfill-mbstring to version 1.31.0
- Updated Symfony/polyfill-uuid to version 1.31.0 - Updated Symfony/polyfill-uuid to version 1.31.0
- Updated nikic/PhpParser to version 5.2.0
### Fixed ### Fixed
- Fixed Division by zero in PackageManager - Fixed Division by zero in PackageManager

View file

@ -2,8 +2,7 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
interface Builder interface Builder {
{
/** /**
* Returns the built node. * Returns the built node.
* *

View file

@ -6,27 +6,29 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Modifiers;
use PhpParser\Node\Const_; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Identifier; use ncc\ThirdParty\nikic\PhpParser\Node\Const_;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Identifier;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class ClassConst implements PhpParser\Builder class ClassConst implements PhpParser\Builder {
{ protected int $flags = 0;
protected $flags = 0; /** @var array<string, mixed> */
protected $attributes = []; protected array $attributes = [];
protected $constants = []; /** @var list<Const_> */
protected array $constants = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** @var Identifier|Node\Name|Node\ComplexType */ /** @var Identifier|Node\Name|Node\ComplexType|null */
protected $type; protected ?Node $type = null;
/** /**
* Creates a class constant builder * Creates a class constant builder
* *
* @param string|Identifier $name Name * @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value * @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
*/ */
public function __construct($name, $value) { public function __construct($name, $value) {
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))]; $this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
@ -36,7 +38,7 @@ class ClassConst implements PhpParser\Builder
* Add another constant to const group * Add another constant to const group
* *
* @param string|Identifier $name Name * @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value * @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value Value
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
@ -52,7 +54,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -63,7 +65,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -74,7 +76,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -85,7 +87,7 @@ class ClassConst implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }

View file

@ -4,25 +4,27 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Modifiers;
use PhpParser\Node\Name; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Class_ extends Declaration class Class_ extends Declaration {
{ protected string $name;
protected $name; protected ?Name $extends = null;
/** @var list<Name> */
protected $extends = null; protected array $implements = [];
protected $implements = []; protected int $flags = 0;
protected $flags = 0; /** @var list<Stmt\TraitUse> */
protected array $uses = [];
protected $uses = []; /** @var list<Stmt\ClassConst> */
protected $constants = []; protected array $constants = [];
protected $properties = []; /** @var list<Stmt\Property> */
protected $methods = []; protected array $properties = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates a class builder. * Creates a class builder.
@ -67,7 +69,7 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeAbstract() { public function makeAbstract() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT);
return $this; return $this;
} }
@ -78,13 +80,18 @@ class Class_ extends Declaration
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }
/**
* Makes the class readonly.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY);
return $this; return $this;
} }
@ -99,20 +106,18 @@ class Class_ extends Declaration
public function addStmt($stmt) { public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt); $stmt = BuilderHelpers::normalizeNode($stmt);
$targets = [ if ($stmt instanceof Stmt\Property) {
Stmt\TraitUse::class => &$this->uses, $this->properties[] = $stmt;
Stmt\ClassConst::class => &$this->constants, } elseif ($stmt instanceof Stmt\ClassMethod) {
Stmt\Property::class => &$this->properties, $this->methods[] = $stmt;
Stmt\ClassMethod::class => &$this->methods, } elseif ($stmt instanceof Stmt\TraitUse) {
]; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$class = \get_class($stmt); $this->constants[] = $stmt;
if (!isset($targets[$class])) { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
$targets[$class][] = $stmt;
return $this; return $this;
} }

View file

@ -5,16 +5,23 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
abstract class Declaration implements PhpParser\Builder abstract class Declaration implements PhpParser\Builder {
{ /** @var array<string, mixed> */
protected $attributes = []; protected array $attributes = [];
/**
* Adds a statement.
*
* @param \ncc\ThirdParty\nikic\PhpParser\Node\Stmt|PhpParser\Builder $stmt The statement to add
*
* @return $this The builder instance (for fluid interface)
*/
abstract public function addStmt($stmt); abstract public function addStmt($stmt);
/** /**
* Adds multiple statements. * Adds multiple statements.
* *
* @param array $stmts The statements to add * @param (\ncc\ThirdParty\nikic\PhpParser\Node\Stmt|PhpParser\Builder)[] $stmts The statements to add
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */

View file

@ -6,18 +6,19 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Identifier; use ncc\ThirdParty\nikic\PhpParser\Node\Identifier;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class EnumCase implements PhpParser\Builder class EnumCase implements PhpParser\Builder {
{ /** @var Identifier|string */
protected $name; protected $name;
protected $value = null; protected ?Node\Expr $value = null;
protected $attributes = []; /** @var array<string, mixed> */
protected array $attributes = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** /**
* Creates an enum case builder. * Creates an enum case builder.

View file

@ -4,25 +4,26 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Identifier; use ncc\ThirdParty\nikic\PhpParser\Node\Identifier;
use PhpParser\Node\Name; use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Enum_ extends Declaration class Enum_ extends Declaration {
{ protected string $name;
protected $name; protected ?Identifier $scalarType = null;
protected $scalarType = null; /** @var list<Name> */
protected array $implements = [];
protected $implements = []; /** @var list<Stmt\TraitUse> */
protected array $uses = [];
protected $uses = []; /** @var list<Stmt\EnumCase> */
protected $enumCases = []; protected array $enumCases = [];
protected $constants = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an enum builder. * Creates an enum builder.
@ -36,7 +37,7 @@ class Enum_ extends Declaration
/** /**
* Sets the scalar type. * Sets the scalar type.
* *
* @param string|Identifier $type * @param string|Identifier $scalarType
* *
* @return $this * @return $this
*/ */
@ -71,20 +72,18 @@ class Enum_ extends Declaration
public function addStmt($stmt) { public function addStmt($stmt) {
$stmt = BuilderHelpers::normalizeNode($stmt); $stmt = BuilderHelpers::normalizeNode($stmt);
$targets = [ if ($stmt instanceof Stmt\EnumCase) {
Stmt\TraitUse::class => &$this->uses, $this->enumCases[] = $stmt;
Stmt\EnumCase::class => &$this->enumCases, } elseif ($stmt instanceof Stmt\ClassMethod) {
Stmt\ClassConst::class => &$this->constants, $this->methods[] = $stmt;
Stmt\ClassMethod::class => &$this->methods, } elseif ($stmt instanceof Stmt\TraitUse) {
]; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$class = \get_class($stmt); $this->constants[] = $stmt;
if (!isset($targets[$class])) { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
$targets[$class][] = $stmt;
return $this; return $this;
} }

View file

@ -5,13 +5,13 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers; use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers;
use ncc\ThirdParty\nikic\PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
abstract class FunctionLike extends Declaration abstract class FunctionLike extends Declaration {
{ protected bool $returnByRef = false;
protected $returnByRef = false; /** @var Node\Param[] */
protected $params = []; protected array $params = [];
/** @var string|Node\Name|Node\NullableType|null */ /** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected $returnType = null; protected ?Node $returnType = null;
/** /**
* Make the function return by reference. * Make the function return by reference.
@ -46,7 +46,7 @@ abstract class FunctionLike extends Declaration
/** /**
* Adds multiple parameters. * Adds multiple parameters.
* *
* @param array $params The parameters to add * @param (Node\Param|Param)[] $params The parameters to add
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */

View file

@ -4,16 +4,16 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Function_ extends FunctionLike class Function_ extends FunctionLike {
{ protected string $name;
protected $name; /** @var list<Stmt> */
protected $stmts = []; protected array $stmts = [];
/** @var Node\AttributeGroup[] */ /** @var list<Node\AttributeGroup> */
protected $attributeGroups = []; protected array $attributeGroups = [];
/** /**
* Creates a function builder. * Creates a function builder.

View file

@ -4,19 +4,20 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Name; use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Interface_ extends Declaration class Interface_ extends Declaration {
{ protected string $name;
protected $name; /** @var list<Name> */
protected $extends = []; protected array $extends = [];
protected $constants = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\ClassMethod> */
/** @var Node\AttributeGroup[] */ protected array $methods = [];
protected $attributeGroups = []; /** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an interface builder. * Creates an interface builder.

View file

@ -4,19 +4,20 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Modifiers;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Method extends FunctionLike class Method extends FunctionLike {
{ protected string $name;
protected $name;
protected $flags = 0;
/** @var array|null */ protected int $flags = 0;
protected $stmts = [];
/** @var Node\AttributeGroup[] */ /** @var list<Stmt>|null */
protected $attributeGroups = []; protected ?array $stmts = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates a method builder. * Creates a method builder.
@ -33,7 +34,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -44,7 +45,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -55,7 +56,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -66,7 +67,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeStatic() { public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this; return $this;
} }
@ -81,7 +82,7 @@ class Method extends FunctionLike
throw new \LogicException('Cannot make method with statements abstract'); throw new \LogicException('Cannot make method with statements abstract');
} }
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
$this->stmts = null; // abstract methods don't have statements $this->stmts = null; // abstract methods don't have statements
return $this; return $this;
@ -93,7 +94,7 @@ class Method extends FunctionLike
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeFinal() { public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this; return $this;
} }

View file

@ -4,13 +4,13 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Namespace_ extends Declaration class Namespace_ extends Declaration {
{ private ?Node\Name $name;
private $name; /** @var Stmt[] */
private $stmts = []; private array $stmts = [];
/** /**
* Creates a namespace builder. * Creates a namespace builder.

View file

@ -4,25 +4,19 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Modifiers;
use ncc\ThirdParty\nikic\PhpParser\Node;
class Param implements PhpParser\Builder class Param implements PhpParser\Builder {
{ protected string $name;
protected $name; protected ?Node\Expr $default = null;
/** @var Node\Identifier|Node\Name|Node\ComplexType|null */
protected $default = null; protected ?Node $type = null;
protected bool $byRef = false;
/** @var Node\Identifier|Node\Name|Node\NullableType|null */ protected int $flags = 0;
protected $type = null; protected bool $variadic = false;
/** @var list<Node\AttributeGroup> */
protected $byRef = false; protected array $attributeGroups = [];
protected $variadic = false;
protected $flags = 0;
/** @var Node\AttributeGroup[] */
protected $attributeGroups = [];
/** /**
* Creates a parameter builder. * Creates a parameter builder.
@ -62,19 +56,6 @@ class Param implements PhpParser\Builder
return $this; return $this;
} }
/**
* Sets type for the parameter.
*
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
*
* @return $this The builder instance (for fluid interface)
*
* @deprecated Use setType() instead
*/
public function setTypeHint($type) {
return $this->setType($type);
}
/** /**
* Make the parameter accept the value by reference. * Make the parameter accept the value by reference.
* *
@ -103,7 +84,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -114,7 +95,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -125,7 +106,7 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -136,7 +117,29 @@ class Param implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this;
}
/**
* Gives the promoted property private(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivateSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
return $this;
}
/**
* Gives the promoted property protected(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtectedSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
return $this; return $this;
} }

View file

@ -4,25 +4,27 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use PhpParser\Modifiers;
use PhpParser\Node\Identifier; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Name; use ncc\ThirdParty\nikic\PhpParser\Node\Identifier;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use PhpParser\Node\ComplexType; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
use ncc\ThirdParty\nikic\PhpParser\Node\ComplexType;
class Property implements PhpParser\Builder class Property implements PhpParser\Builder {
{ protected string $name;
protected $name;
protected $flags = 0; protected int $flags = 0;
protected $default = null;
protected $attributes = [];
/** @var null|Identifier|Name|NullableType */ protected ?Node\Expr $default = null;
protected $type; /** @var array<string, mixed> */
protected array $attributes = [];
/** @var Node\AttributeGroup[] */ /** @var null|Identifier|Name|ComplexType */
protected $attributeGroups = []; protected ?Node $type = null;
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** @var list<Node\PropertyHook> */
protected array $hooks = [];
/** /**
* Creates a property builder. * Creates a property builder.
@ -39,7 +41,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC);
return $this; return $this;
} }
@ -50,7 +52,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED);
return $this; return $this;
} }
@ -61,7 +63,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE);
return $this; return $this;
} }
@ -72,7 +74,7 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeStatic() { public function makeStatic() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC);
return $this; return $this;
} }
@ -83,7 +85,51 @@ class Property implements PhpParser\Builder
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeReadonly() { public function makeReadonly() {
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY); $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY);
return $this;
}
/**
* Makes the property abstract. Requires at least one property hook to be specified as well.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeAbstract() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT);
return $this;
}
/**
* Makes the property final.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeFinal() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL);
return $this;
}
/**
* Gives the property private(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makePrivateSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE_SET);
return $this;
}
/**
* Gives the property protected(set) visibility.
*
* @return $this The builder instance (for fluid interface)
*/
public function makeProtectedSet() {
$this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED_SET);
return $this; return $this;
} }
@ -142,20 +188,36 @@ class Property implements PhpParser\Builder
return $this; return $this;
} }
/**
* Adds a property hook.
*
* @return $this The builder instance (for fluid interface)
*/
public function addHook(Node\PropertyHook $hook) {
$this->hooks[] = $hook;
return $this;
}
/** /**
* Returns the built class node. * Returns the built class node.
* *
* @return Stmt\Property The built property node * @return Stmt\Property The built property node
*/ */
public function getNode(): PhpParser\Node { public function getNode(): PhpParser\Node {
if ($this->flags & Modifiers::ABSTRACT && !$this->hooks) {
throw new PhpParser\Error('Only hooked properties may be declared abstract');
}
return new Stmt\Property( return new Stmt\Property(
$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC, $this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
[ [
new Stmt\PropertyProperty($this->name, $this->default) new Node\PropertyItem($this->name, $this->default)
], ],
$this->attributes, $this->attributes,
$this->type, $this->type,
$this->attributeGroups $this->attributeGroups,
$this->hooks
); );
} }
} }

View file

@ -7,10 +7,11 @@ use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers;
use ncc\ThirdParty\nikic\PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class TraitUse implements Builder class TraitUse implements Builder {
{ /** @var Node\Name[] */
protected $traits = []; protected array $traits = [];
protected $adaptations = []; /** @var Stmt\TraitUseAdaptation[] */
protected array $adaptations = [];
/** /**
* Creates a trait use builder. * Creates a trait use builder.

View file

@ -4,31 +4,28 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser\Builder; use ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers; use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers;
use ncc\ThirdParty\nikic\PhpParser\Modifiers;
use ncc\ThirdParty\nikic\PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class TraitUseAdaptation implements Builder class TraitUseAdaptation implements Builder {
{ private const TYPE_UNDEFINED = 0;
const TYPE_UNDEFINED = 0; private const TYPE_ALIAS = 1;
const TYPE_ALIAS = 1; private const TYPE_PRECEDENCE = 2;
const TYPE_PRECEDENCE = 2;
/** @var int Type of building adaptation */ protected int $type;
protected $type; protected ?Node\Name $trait;
protected Node\Identifier $method;
protected $trait; protected ?int $modifier = null;
protected $method; protected ?Node\Identifier $alias = null;
/** @var Node\Name[] */
protected $modifier = null; protected array $insteadof = [];
protected $alias = null;
protected $insteadof = [];
/** /**
* Creates a trait use adaptation builder. * Creates a trait use adaptation builder.
* *
* @param Node\Name|string|null $trait Name of adaptated trait * @param Node\Name|string|null $trait Name of adapted trait
* @param Node\Identifier|string $method Name of adaptated method * @param Node\Identifier|string $method Name of adapted method
*/ */
public function __construct($trait, $method) { public function __construct($trait, $method) {
$this->type = self::TYPE_UNDEFINED; $this->type = self::TYPE_UNDEFINED;
@ -40,7 +37,7 @@ class TraitUseAdaptation implements Builder
/** /**
* Sets alias of method. * Sets alias of method.
* *
* @param Node\Identifier|string $alias Alias for adaptated method * @param Node\Identifier|string $alias Alias for adapted method
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
@ -53,37 +50,37 @@ class TraitUseAdaptation implements Builder
throw new \LogicException('Cannot set alias for not alias adaptation buider'); throw new \LogicException('Cannot set alias for not alias adaptation buider');
} }
$this->alias = $alias; $this->alias = BuilderHelpers::normalizeIdentifier($alias);
return $this; return $this;
} }
/** /**
* Sets adaptated method public. * Sets adapted method public.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePublic() { public function makePublic() {
$this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); $this->setModifier(Modifiers::PUBLIC);
return $this; return $this;
} }
/** /**
* Sets adaptated method protected. * Sets adapted method protected.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makeProtected() { public function makeProtected() {
$this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); $this->setModifier(Modifiers::PROTECTED);
return $this; return $this;
} }
/** /**
* Sets adaptated method private. * Sets adapted method private.
* *
* @return $this The builder instance (for fluid interface) * @return $this The builder instance (for fluid interface)
*/ */
public function makePrivate() { public function makePrivate() {
$this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); $this->setModifier(Modifiers::PRIVATE);
return $this; return $this;
} }
@ -114,7 +111,7 @@ class TraitUseAdaptation implements Builder
return $this; return $this;
} }
protected function setModifier(int $modifier) { protected function setModifier(int $modifier): void {
if ($this->type === self::TYPE_UNDEFINED) { if ($this->type === self::TYPE_UNDEFINED) {
$this->type = self::TYPE_ALIAS; $this->type = self::TYPE_ALIAS;
} }
@ -138,9 +135,9 @@ class TraitUseAdaptation implements Builder
public function getNode(): Node { public function getNode(): Node {
switch ($this->type) { switch ($this->type) {
case self::TYPE_ALIAS: case self::TYPE_ALIAS:
return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias); return new \ncc\ThirdParty\nikic\PhpParser\Node\Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias);
case self::TYPE_PRECEDENCE: case self::TYPE_PRECEDENCE:
return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof); return new \ncc\ThirdParty\nikic\PhpParser\Node\Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof);
default: default:
throw new \LogicException('Type of adaptation is not defined'); throw new \LogicException('Type of adaptation is not defined');
} }

View file

@ -4,18 +4,21 @@ namespace ncc\ThirdParty\nikic\PhpParser\Builder;
use ncc\ThirdParty\nikic\PhpParser; use ncc\ThirdParty\nikic\PhpParser;
use PhpParser\BuilderHelpers; use PhpParser\BuilderHelpers;
use PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Trait_ extends Declaration class Trait_ extends Declaration {
{ protected string $name;
protected $name; /** @var list<Stmt\TraitUse> */
protected $uses = []; protected array $uses = [];
protected $properties = []; /** @var list<Stmt\ClassConst> */
protected $methods = []; protected array $constants = [];
/** @var list<Stmt\Property> */
/** @var Node\AttributeGroup[] */ protected array $properties = [];
protected $attributeGroups = []; /** @var list<Stmt\ClassMethod> */
protected array $methods = [];
/** @var list<Node\AttributeGroup> */
protected array $attributeGroups = [];
/** /**
* Creates an interface builder. * Creates an interface builder.
@ -42,6 +45,8 @@ class Trait_ extends Declaration
$this->methods[] = $stmt; $this->methods[] = $stmt;
} elseif ($stmt instanceof Stmt\TraitUse) { } elseif ($stmt instanceof Stmt\TraitUse) {
$this->uses[] = $stmt; $this->uses[] = $stmt;
} elseif ($stmt instanceof Stmt\ClassConst) {
$this->constants[] = $stmt;
} else { } else {
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
} }
@ -70,7 +75,7 @@ class Trait_ extends Declaration
public function getNode(): PhpParser\Node { public function getNode(): PhpParser\Node {
return new Stmt\Trait_( return new Stmt\Trait_(
$this->name, [ $this->name, [
'stmts' => array_merge($this->uses, $this->properties, $this->methods), 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
'attrGroups' => $this->attributeGroups, 'attrGroups' => $this->attributeGroups,
], $this->attributes ], $this->attributes
); );

View file

@ -7,17 +7,17 @@ use ncc\ThirdParty\nikic\PhpParser\BuilderHelpers;
use ncc\ThirdParty\nikic\PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class Use_ implements Builder class Use_ implements Builder {
{ protected Node\Name $name;
protected $name; /** @var Stmt\Use_::TYPE_* */
protected $type; protected int $type;
protected $alias = null; protected ?string $alias = null;
/** /**
* Creates a name use (alias) builder. * Creates a name use (alias) builder.
* *
* @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias * @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 * @param Stmt\Use_::TYPE_* $type One of the Stmt\Use_::TYPE_* constants
*/ */
public function __construct($name, int $type) { public function __construct($name, int $type) {
$this->name = BuilderHelpers::normalizeName($name); $this->name = BuilderHelpers::normalizeName($name);
@ -43,7 +43,7 @@ class Use_ implements Builder
*/ */
public function getNode(): Node { public function getNode(): Node {
return new Stmt\Use_([ return new Stmt\Use_([
new Stmt\UseUse($this->name, $this->alias) new Node\UseItem($this->name, $this->alias)
], $this->type); ], $this->type);
} }
} }

View file

@ -10,18 +10,15 @@ use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use ncc\ThirdParty\nikic\PhpParser\Node\Scalar\String_; use ncc\ThirdParty\nikic\PhpParser\Node\Scalar\String_;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt\Use_; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt\Use_;
class BuilderFactory class BuilderFactory {
{
/** /**
* Creates an attribute node. * Creates an attribute node.
* *
* @param string|Name $name Name of the attribute * @param string|Name $name Name of the attribute
* @param array $args Attribute named arguments * @param array $args Attribute named arguments
*
* @return Node\Attribute
*/ */
public function attribute($name, array $args = []) : Node\Attribute { public function attribute($name, array $args = []): ncc\ThirdParty\nikic\PhpParser\Node\Attribute {
return new Node\Attribute( return new ncc\ThirdParty\nikic\PhpParser\Node\Attribute(
BuilderHelpers::normalizeName($name), BuilderHelpers::normalizeName($name),
$this->args($args) $this->args($args)
); );
@ -30,12 +27,12 @@ class BuilderFactory
/** /**
* Creates a namespace builder. * Creates a namespace builder.
* *
* @param null|string|Node\Name $name Name of the namespace * @param null|string|ncc\ThirdParty\nikic\PhpParser\Node\Name $name Name of the namespace
* *
* @return Builder\Namespace_ The created namespace builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Namespace_ The created namespace builder
*/ */
public function namespace($name) : Builder\Namespace_ { public function namespace($name): ncc\ThirdParty\nikic\PhpParser\Builder\Namespace_ {
return new Builder\Namespace_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Namespace_($name);
} }
/** /**
@ -43,10 +40,10 @@ class BuilderFactory
* *
* @param string $name Name of the class * @param string $name Name of the class
* *
* @return Builder\Class_ The created class builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Class_ The created class builder
*/ */
public function class(string $name) : Builder\Class_ { public function class(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Class_ {
return new Builder\Class_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Class_($name);
} }
/** /**
@ -54,10 +51,10 @@ class BuilderFactory
* *
* @param string $name Name of the interface * @param string $name Name of the interface
* *
* @return Builder\Interface_ The created interface builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Interface_ The created interface builder
*/ */
public function interface(string $name) : Builder\Interface_ { public function interface(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Interface_ {
return new Builder\Interface_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Interface_($name);
} }
/** /**
@ -65,10 +62,10 @@ class BuilderFactory
* *
* @param string $name Name of the trait * @param string $name Name of the trait
* *
* @return Builder\Trait_ The created trait builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Trait_ The created trait builder
*/ */
public function trait(string $name) : Builder\Trait_ { public function trait(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Trait_ {
return new Builder\Trait_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Trait_($name);
} }
/** /**
@ -76,38 +73,38 @@ class BuilderFactory
* *
* @param string $name Name of the enum * @param string $name Name of the enum
* *
* @return Builder\Enum_ The created enum builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Enum_ The created enum builder
*/ */
public function enum(string $name) : Builder\Enum_ { public function enum(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Enum_ {
return new Builder\Enum_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Enum_($name);
} }
/** /**
* Creates a trait use builder. * Creates a trait use builder.
* *
* @param Node\Name|string ...$traits Trait names * @param ncc\ThirdParty\nikic\PhpParser\Node\Name|string ...$traits Trait names
* *
* @return Builder\TraitUse The create trait use builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\TraitUse The created trait use builder
*/ */
public function useTrait(...$traits) : Builder\TraitUse { public function useTrait(...$traits): ncc\ThirdParty\nikic\PhpParser\Builder\TraitUse {
return new Builder\TraitUse(...$traits); return new ncc\ThirdParty\nikic\PhpParser\Builder\TraitUse(...$traits);
} }
/** /**
* Creates a trait use adaptation builder. * Creates a trait use adaptation builder.
* *
* @param Node\Name|string|null $trait Trait name * @param ncc\ThirdParty\nikic\PhpParser\Node\Name|string|null $trait Trait name
* @param Node\Identifier|string $method Method name * @param ncc\ThirdParty\nikic\PhpParser\Node\Identifier|string $method Method name
* *
* @return Builder\TraitUseAdaptation The create trait use adaptation builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\TraitUseAdaptation The created trait use adaptation builder
*/ */
public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation { public function traitUseAdaptation($trait, $method = null): ncc\ThirdParty\nikic\PhpParser\Builder\TraitUseAdaptation {
if ($method === null) { if ($method === null) {
$method = $trait; $method = $trait;
$trait = null; $trait = null;
} }
return new Builder\TraitUseAdaptation($trait, $method); return new ncc\ThirdParty\nikic\PhpParser\Builder\TraitUseAdaptation($trait, $method);
} }
/** /**
@ -115,10 +112,10 @@ class BuilderFactory
* *
* @param string $name Name of the method * @param string $name Name of the method
* *
* @return Builder\Method The created method builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Method The created method builder
*/ */
public function method(string $name) : Builder\Method { public function method(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Method {
return new Builder\Method($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Method($name);
} }
/** /**
@ -126,10 +123,10 @@ class BuilderFactory
* *
* @param string $name Name of the parameter * @param string $name Name of the parameter
* *
* @return Builder\Param The created parameter builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Param The created parameter builder
*/ */
public function param(string $name) : Builder\Param { public function param(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Param {
return new Builder\Param($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Param($name);
} }
/** /**
@ -137,10 +134,10 @@ class BuilderFactory
* *
* @param string $name Name of the property * @param string $name Name of the property
* *
* @return Builder\Property The created property builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Property The created property builder
*/ */
public function property(string $name) : Builder\Property { public function property(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Property {
return new Builder\Property($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Property($name);
} }
/** /**
@ -148,55 +145,55 @@ class BuilderFactory
* *
* @param string $name Name of the function * @param string $name Name of the function
* *
* @return Builder\Function_ The created function builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Function_ The created function builder
*/ */
public function function(string $name) : Builder\Function_ { public function function(string $name): ncc\ThirdParty\nikic\PhpParser\Builder\Function_ {
return new Builder\Function_($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\Function_($name);
} }
/** /**
* Creates a namespace/class use builder. * Creates a namespace/class use builder.
* *
* @param Node\Name|string $name Name of the entity (namespace or class) to alias * @param ncc\ThirdParty\nikic\PhpParser\Node\Name|string $name Name of the entity (namespace or class) to alias
* *
* @return Builder\Use_ The created use builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Use_ The created use builder
*/ */
public function use($name) : Builder\Use_ { public function use($name): ncc\ThirdParty\nikic\PhpParser\Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_NORMAL); return new ncc\ThirdParty\nikic\PhpParser\Builder\Use_($name, Use_::TYPE_NORMAL);
} }
/** /**
* Creates a function use builder. * Creates a function use builder.
* *
* @param Node\Name|string $name Name of the function to alias * @param ncc\ThirdParty\nikic\PhpParser\Node\Name|string $name Name of the function to alias
* *
* @return Builder\Use_ The created use function builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Use_ The created use function builder
*/ */
public function useFunction($name) : Builder\Use_ { public function useFunction($name): ncc\ThirdParty\nikic\PhpParser\Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_FUNCTION); return new ncc\ThirdParty\nikic\PhpParser\Builder\Use_($name, Use_::TYPE_FUNCTION);
} }
/** /**
* Creates a constant use builder. * Creates a constant use builder.
* *
* @param Node\Name|string $name Name of the const to alias * @param ncc\ThirdParty\nikic\PhpParser\Node\Name|string $name Name of the const to alias
* *
* @return Builder\Use_ The created use const builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\Use_ The created use const builder
*/ */
public function useConst($name) : Builder\Use_ { public function useConst($name): ncc\ThirdParty\nikic\PhpParser\Builder\Use_ {
return new Builder\Use_($name, Use_::TYPE_CONSTANT); return new ncc\ThirdParty\nikic\PhpParser\Builder\Use_($name, Use_::TYPE_CONSTANT);
} }
/** /**
* Creates a class constant builder. * Creates a class constant builder.
* *
* @param string|Identifier $name Name * @param string|Identifier $name Name
* @param Node\Expr|bool|null|int|float|string|array $value Value * @param ncc\ThirdParty\nikic\PhpParser\Node\Expr|bool|null|int|float|string|array $value Value
* *
* @return Builder\ClassConst The created use const builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\ClassConst The created use const builder
*/ */
public function classConst($name, $value) : Builder\ClassConst { public function classConst($name, $value): ncc\ThirdParty\nikic\PhpParser\Builder\ClassConst {
return new Builder\ClassConst($name, $value); return new ncc\ThirdParty\nikic\PhpParser\Builder\ClassConst($name, $value);
} }
/** /**
@ -204,18 +201,16 @@ class BuilderFactory
* *
* @param string|Identifier $name Name * @param string|Identifier $name Name
* *
* @return Builder\EnumCase The created use const builder * @return ncc\ThirdParty\nikic\PhpParser\Builder\EnumCase The created use const builder
*/ */
public function enumCase($name) : Builder\EnumCase { public function enumCase($name): ncc\ThirdParty\nikic\PhpParser\Builder\EnumCase {
return new Builder\EnumCase($name); return new ncc\ThirdParty\nikic\PhpParser\Builder\EnumCase($name);
} }
/** /**
* Creates node a for a literal value. * Creates node a for a literal value.
* *
* @param Expr|bool|null|int|float|string|array $value $value * @param Expr|bool|null|int|float|string|array|\UnitEnum $value $value
*
* @return Expr
*/ */
public function val($value): Expr { public function val($value): Expr {
return BuilderHelpers::normalizeValue($value); return BuilderHelpers::normalizeValue($value);
@ -225,8 +220,6 @@ class BuilderFactory
* Creates variable node. * Creates variable node.
* *
* @param string|Expr $name Name * @param string|Expr $name Name
*
* @return Expr\Variable
*/ */
public function var($name): Expr\Variable { public function var($name): Expr\Variable {
if (!\is_string($name) && !$name instanceof Expr) { if (!\is_string($name) && !$name instanceof Expr) {
@ -243,7 +236,7 @@ class BuilderFactory
* *
* @param array $args List of arguments to normalize * @param array $args List of arguments to normalize
* *
* @return Arg[] * @return list<Arg>
*/ */
public function args(array $args): array { public function args(array $args): array {
$normalizedArgs = []; $normalizedArgs = [];
@ -264,8 +257,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $name Function name * @param string|Name|Expr $name Function name
* @param array $args Function arguments * @param array $args Function arguments
*
* @return Expr\FuncCall
*/ */
public function funcCall($name, array $args = []): Expr\FuncCall { public function funcCall($name, array $args = []): Expr\FuncCall {
return new Expr\FuncCall( return new Expr\FuncCall(
@ -280,8 +271,6 @@ class BuilderFactory
* @param Expr $var Variable the method is called on * @param Expr $var Variable the method is called on
* @param string|Identifier|Expr $name Method name * @param string|Identifier|Expr $name Method name
* @param array $args Method arguments * @param array $args Method arguments
*
* @return Expr\MethodCall
*/ */
public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall { public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall {
return new Expr\MethodCall( return new Expr\MethodCall(
@ -297,8 +286,6 @@ class BuilderFactory
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Method name * @param string|Identifier|Expr $name Method name
* @param array $args Method arguments * @param array $args Method arguments
*
* @return Expr\StaticCall
*/ */
public function staticCall($class, $name, array $args = []): Expr\StaticCall { public function staticCall($class, $name, array $args = []): Expr\StaticCall {
return new Expr\StaticCall( return new Expr\StaticCall(
@ -313,8 +300,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param array $args Constructor arguments * @param array $args Constructor arguments
*
* @return Expr\New_
*/ */
public function new($class, array $args = []): Expr\New_ { public function new($class, array $args = []): Expr\New_ {
return new Expr\New_( return new Expr\New_(
@ -327,8 +312,6 @@ class BuilderFactory
* Creates a constant fetch node. * Creates a constant fetch node.
* *
* @param string|Name $name Constant name * @param string|Name $name Constant name
*
* @return Expr\ConstFetch
*/ */
public function constFetch($name): Expr\ConstFetch { public function constFetch($name): Expr\ConstFetch {
return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
@ -339,8 +322,6 @@ class BuilderFactory
* *
* @param Expr $var Variable holding object * @param Expr $var Variable holding object
* @param string|Identifier|Expr $name Property name * @param string|Identifier|Expr $name Property name
*
* @return Expr\PropertyFetch
*/ */
public function propertyFetch(Expr $var, $name): Expr\PropertyFetch { public function propertyFetch(Expr $var, $name): Expr\PropertyFetch {
return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
@ -351,8 +332,6 @@ class BuilderFactory
* *
* @param string|Name|Expr $class Class name * @param string|Name|Expr $class Class name
* @param string|Identifier|Expr $name Constant name * @param string|Identifier|Expr $name Constant name
*
* @return Expr\ClassConstFetch
*/ */
public function classConstFetch($class, $name): Expr\ClassConstFetch { public function classConstFetch($class, $name): Expr\ClassConstFetch {
return new Expr\ClassConstFetch( return new Expr\ClassConstFetch(
@ -365,8 +344,6 @@ class BuilderFactory
* Creates nested Concat nodes from a list of expressions. * Creates nested Concat nodes from a list of expressions.
* *
* @param Expr|string ...$exprs Expressions or literal strings * @param Expr|string ...$exprs Expressions or literal strings
*
* @return Concat
*/ */
public function concat(...$exprs): Concat { public function concat(...$exprs): Concat {
$numExprs = count($exprs); $numExprs = count($exprs);
@ -383,7 +360,6 @@ class BuilderFactory
/** /**
* @param string|Expr $expr * @param string|Expr $expr
* @return Expr
*/ */
private function normalizeStringExpr($expr): Expr { private function normalizeStringExpr($expr): Expr {
if ($expr instanceof Expr) { if ($expr instanceof Expr) {

View file

@ -6,6 +6,7 @@ use ncc\ThirdParty\nikic\PhpParser\Node\ComplexType;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Identifier; use ncc\ThirdParty\nikic\PhpParser\Node\Identifier;
use ncc\ThirdParty\nikic\PhpParser\Node\Name; use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use ncc\ThirdParty\nikic\PhpParser\Node\Name\FullyQualified;
use ncc\ThirdParty\nikic\PhpParser\Node\NullableType; use ncc\ThirdParty\nikic\PhpParser\Node\NullableType;
use ncc\ThirdParty\nikic\PhpParser\Node\Scalar; use ncc\ThirdParty\nikic\PhpParser\Node\Scalar;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
@ -15,8 +16,7 @@ use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
* *
* @internal * @internal
*/ */
final class BuilderHelpers final class BuilderHelpers {
{
/** /**
* Normalizes a node: Converts builder objects to nodes. * Normalizes a node: Converts builder objects to nodes.
* *
@ -215,12 +215,12 @@ final class BuilderHelpers
* Normalizes a value: Converts nulls, booleans, integers, * Normalizes a value: Converts nulls, booleans, integers,
* floats, strings and arrays into their respective nodes * floats, strings and arrays into their respective nodes
* *
* @param Node\Expr|bool|null|int|float|string|array $value The value to normalize * @param ncc\ThirdParty\nikic\PhpParser\Node\Expr|bool|null|int|float|string|array|\UnitEnum $value The value to normalize
* *
* @return Expr The normalized value * @return Expr The normalized value
*/ */
public static function normalizeValue($value): Expr { public static function normalizeValue($value): Expr {
if ($value instanceof Node\Expr) { if ($value instanceof ncc\ThirdParty\nikic\PhpParser\Node\Expr) {
return $value; return $value;
} }
@ -237,11 +237,11 @@ final class BuilderHelpers
} }
if (is_int($value)) { if (is_int($value)) {
return new Scalar\LNumber($value); return new Scalar\Int_($value);
} }
if (is_float($value)) { if (is_float($value)) {
return new Scalar\DNumber($value); return new Scalar\Float_($value);
} }
if (is_string($value)) { if (is_string($value)) {
@ -254,12 +254,12 @@ final class BuilderHelpers
foreach ($value as $itemKey => $itemValue) { foreach ($value as $itemKey => $itemValue) {
// for consecutive, numeric keys don't generate keys // for consecutive, numeric keys don't generate keys
if (null !== $lastKey && ++$lastKey === $itemKey) { if (null !== $lastKey && ++$lastKey === $itemKey) {
$items[] = new Expr\ArrayItem( $items[] = new ncc\ThirdParty\nikic\PhpParser\Node\ArrayItem(
self::normalizeValue($itemValue) self::normalizeValue($itemValue)
); );
} else { } else {
$lastKey = null; $lastKey = null;
$items[] = new Expr\ArrayItem( $items[] = new ncc\ThirdParty\nikic\PhpParser\Node\ArrayItem(
self::normalizeValue($itemValue), self::normalizeValue($itemValue),
self::normalizeValue($itemKey) self::normalizeValue($itemKey)
); );
@ -269,6 +269,10 @@ final class BuilderHelpers
return new Expr\Array_($items); return new Expr\Array_($items);
} }
if ($value instanceof \UnitEnum) {
return new Expr\ClassConstFetch(new FullyQualified(\get_class($value)), new Identifier($value->name));
}
throw new \LogicException('Invalid value'); throw new \LogicException('Invalid value');
} }
@ -294,21 +298,20 @@ final class BuilderHelpers
/** /**
* Normalizes a attribute: Converts attribute to the Attribute Group if needed. * Normalizes a attribute: Converts attribute to the Attribute Group if needed.
* *
* @param Node\Attribute|Node\AttributeGroup $attribute * @param ncc\ThirdParty\nikic\PhpParser\Node\Attribute|ncc\ThirdParty\nikic\PhpParser\Node\AttributeGroup $attribute
* *
* @return Node\AttributeGroup The Attribute Group * @return ncc\ThirdParty\nikic\PhpParser\Node\AttributeGroup The Attribute Group
*/ */
public static function normalizeAttribute($attribute) : Node\AttributeGroup public static function normalizeAttribute($attribute): ncc\ThirdParty\nikic\PhpParser\Node\AttributeGroup {
{ if ($attribute instanceof ncc\ThirdParty\nikic\PhpParser\Node\AttributeGroup) {
if ($attribute instanceof Node\AttributeGroup) {
return $attribute; return $attribute;
} }
if (!($attribute instanceof Node\Attribute)) { if (!($attribute instanceof ncc\ThirdParty\nikic\PhpParser\Node\Attribute)) {
throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup'); throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
} }
return new Node\AttributeGroup([$attribute]); return new ncc\ThirdParty\nikic\PhpParser\Node\AttributeGroup([$attribute]);
} }
/** /**
@ -320,7 +323,7 @@ final class BuilderHelpers
* @return int New modifiers * @return int New modifiers
*/ */
public static function addModifier(int $modifiers, int $modifier): int { public static function addModifier(int $modifiers, int $modifier): int {
Stmt\Class_::verifyModifier($modifiers, $modifier); Modifiers::verifyModifier($modifiers, $modifier);
return $modifiers | $modifier; return $modifiers | $modifier;
} }
@ -329,7 +332,7 @@ final class BuilderHelpers
* @return int New modifiers * @return int New modifiers
*/ */
public static function addClassModifier(int $existingModifiers, int $modifierToSet): int { public static function addClassModifier(int $existingModifiers, int $modifierToSet): int {
Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet); Modifiers::verifyClassModifier($existingModifiers, $modifierToSet);
return $existingModifiers | $modifierToSet; return $existingModifiers | $modifierToSet;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,14 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
class Comment implements \JsonSerializable class Comment implements \JsonSerializable {
{ protected string $text;
protected $text; protected int $startLine;
protected $startLine; protected int $startFilePos;
protected $startFilePos; protected int $startTokenPos;
protected $startTokenPos; protected int $endLine;
protected $endLine; protected int $endFilePos;
protected $endFilePos; protected int $endTokenPos;
protected $endTokenPos;
/** /**
* Constructs a comment node. * Constructs a comment node.
@ -47,6 +46,7 @@ class Comment implements \JsonSerializable
* Gets the line number the comment started on. * Gets the line number the comment started on.
* *
* @return int Line number (or -1 if not available) * @return int Line number (or -1 if not available)
* @phpstan-return -1|positive-int
*/ */
public function getStartLine(): int { public function getStartLine(): int {
return $this->startLine; return $this->startLine;
@ -74,6 +74,7 @@ class Comment implements \JsonSerializable
* Gets the line number the comment ends on. * Gets the line number the comment ends on.
* *
* @return int Line number (or -1 if not available) * @return int Line number (or -1 if not available)
* @phpstan-return -1|positive-int
*/ */
public function getEndLine(): int { public function getEndLine(): int {
return $this->endLine; return $this->endLine;
@ -97,39 +98,6 @@ class Comment implements \JsonSerializable
return $this->endTokenPos; return $this->endTokenPos;
} }
/**
* Gets the line number the comment started on.
*
* @deprecated Use getStartLine() instead
*
* @return int Line number
*/
public function getLine() : int {
return $this->startLine;
}
/**
* Gets the file offset the comment started on.
*
* @deprecated Use getStartFilePos() instead
*
* @return int File offset
*/
public function getFilePos() : int {
return $this->startFilePos;
}
/**
* Gets the token offset the comment started on.
*
* @deprecated Use getStartTokenPos() instead
*
* @return int Token offset
*/
public function getTokenPos() : int {
return $this->startTokenPos;
}
/** /**
* Gets the comment text. * Gets the comment text.
* *
@ -144,18 +112,19 @@ class Comment implements \JsonSerializable
* *
* "Reformatted" here means that we try to clean up the whitespace at the * "Reformatted" here means that we try to clean up the whitespace at the
* starts of the lines. This is necessary because we receive the comments * starts of the lines. This is necessary because we receive the comments
* without trailing whitespace on the first line, but with trailing whitespace * without leading whitespace on the first line, but with leading whitespace
* on all subsequent lines. * on all subsequent lines.
* *
* @return mixed|string * Additionally, this normalizes CRLF newlines to LF newlines.
*/ */
public function getReformattedText() { public function getReformattedText(): string {
$text = trim($this->text); $text = str_replace("\r\n", "\n", $this->text);
$newlinePos = strpos($text, "\n"); $newlinePos = strpos($text, "\n");
if (false === $newlinePos) { if (false === $newlinePos) {
// Single line comments don't need further processing // Single line comments don't need further processing
return $text; return $text;
} elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) { }
if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* // /*
@ -164,8 +133,9 @@ class Comment implements \JsonSerializable
// */ // */
// //
// is handled by replacing the whitespace sequences before the * by a single space // is handled by replacing the whitespace sequences before the * by a single space
return preg_replace('(^\s+\*)m', ' *', $this->text); return preg_replace('(^\s+\*)m', ' *', $text);
} elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) { }
if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* // /*
@ -177,7 +147,8 @@ class Comment implements \JsonSerializable
// */ on all lines. So if the last line is " */", then " " is removed at the // */ on all lines. So if the last line is " */", then " " is removed at the
// start of all lines. // start of all lines.
return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text); return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
} elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) { }
if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
// Multi line comment of the type // Multi line comment of the type
// //
// /* Some text. // /* Some text.
@ -206,7 +177,7 @@ class Comment implements \JsonSerializable
*/ */
private function getShortestWhitespacePrefixLen(string $str): int { private function getShortestWhitespacePrefixLen(string $str): int {
$lines = explode("\n", $str); $lines = explode("\n", $str);
$shortestPrefixLen = \INF; $shortestPrefixLen = \PHP_INT_MAX;
foreach ($lines as $line) { foreach ($lines as $line) {
preg_match('(^\s*)', $line, $matches); preg_match('(^\s*)', $line, $matches);
$prefixLen = strlen($matches[0]); $prefixLen = strlen($matches[0]);
@ -218,8 +189,7 @@ class Comment implements \JsonSerializable
} }
/** /**
* @return array * @return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
* @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
*/ */
public function jsonSerialize(): array { public function jsonSerialize(): array {
// Technically not a node, but we make it look like one anyway // Technically not a node, but we make it look like one anyway

View file

@ -2,6 +2,5 @@
namespace ncc\ThirdParty\nikic\PhpParser\Comment; namespace ncc\ThirdParty\nikic\PhpParser\Comment;
class Doc extends \ncc\ThirdParty\nikic\PhpParser\Comment class Doc extends \ncc\ThirdParty\nikic\PhpParser\Comment {
{
} }

View file

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

View file

@ -1,11 +1,12 @@
<?php <?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
use function array_merge;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Scalar; use ncc\ThirdParty\nikic\PhpParser\Node\Scalar;
use function array_merge;
/** /**
* Evaluates constant expressions. * Evaluates constant expressions.
* *
@ -25,8 +26,8 @@ use ncc\ThirdParty\nikic\PhpParser\Node\Scalar;
* point to string conversions are affected by the precision ini setting. Secondly, they are also * point to string conversions are affected by the precision ini setting. Secondly, they are also
* affected by the LC_NUMERIC locale. * affected by the LC_NUMERIC locale.
*/ */
class ConstExprEvaluator class ConstExprEvaluator {
{ /** @var callable|null */
private $fallbackEvaluator; private $fallbackEvaluator;
/** /**
@ -37,7 +38,7 @@ class ConstExprEvaluator
* *
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated * @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
*/ */
public function __construct(callable $fallbackEvaluator = null) { public function __construct(?callable $fallbackEvaluator = null) {
$this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) { $this->fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) {
throw new ConstExprEvaluationException( throw new ConstExprEvaluationException(
"Expression of type {$expr->getType()} cannot be evaluated" "Expression of type {$expr->getType()} cannot be evaluated"
@ -101,9 +102,10 @@ class ConstExprEvaluator
return $this->evaluate($expr); return $this->evaluate($expr);
} }
/** @return mixed */
private function evaluate(Expr $expr) { private function evaluate(Expr $expr) {
if ($expr instanceof Scalar\LNumber if ($expr instanceof Scalar\Int_
|| $expr instanceof Scalar\DNumber || $expr instanceof Scalar\Float_
|| $expr instanceof Scalar\String_ || $expr instanceof Scalar\String_
) { ) {
return $expr->value; return $expr->value;
@ -146,7 +148,7 @@ class ConstExprEvaluator
return ($this->fallbackEvaluator)($expr); return ($this->fallbackEvaluator)($expr);
} }
private function evaluateArray(Expr\Array_ $expr) { private function evaluateArray(Expr\Array_ $expr): array {
$array = []; $array = [];
foreach ($expr->items as $item) { foreach ($expr->items as $item) {
if (null !== $item->key) { if (null !== $item->key) {
@ -160,6 +162,7 @@ class ConstExprEvaluator
return $array; return $array;
} }
/** @return mixed */
private function evaluateTernary(Expr\Ternary $expr) { private function evaluateTernary(Expr\Ternary $expr) {
if (null === $expr->if) { if (null === $expr->if) {
return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
@ -170,6 +173,7 @@ class ConstExprEvaluator
: $this->evaluate($expr->else); : $this->evaluate($expr->else);
} }
/** @return mixed */
private function evaluateBinaryOp(Expr\BinaryOp $expr) { private function evaluateBinaryOp(Expr\BinaryOp $expr) {
if ($expr instanceof Expr\BinaryOp\Coalesce if ($expr instanceof Expr\BinaryOp\Coalesce
&& $expr->left instanceof Expr\ArrayDimFetch && $expr->left instanceof Expr\ArrayDimFetch
@ -216,6 +220,7 @@ class ConstExprEvaluator
throw new \Exception('Should not happen'); throw new \Exception('Should not happen');
} }
/** @return mixed */
private function evaluateConstFetch(Expr\ConstFetch $expr) { private function evaluateConstFetch(Expr\ConstFetch $expr) {
$name = $expr->name->toLowerString(); $name = $expr->name->toLowerString();
switch ($name) { switch ($name) {

View file

@ -2,25 +2,20 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
class Error extends \RuntimeException class Error extends \RuntimeException {
{ protected string $rawMessage;
protected $rawMessage; /** @var array<string, mixed> */
protected $attributes; protected array $attributes;
/** /**
* Creates an Exception signifying a parse error. * Creates an Exception signifying a parse error.
* *
* @param string $message Error message * @param string $message Error message
* @param array|int $attributes Attributes of node/token where error occurred * @param array<string, mixed> $attributes Attributes of node/token where error occurred
* (or start line of error -- deprecated)
*/ */
public function __construct(string $message, $attributes = []) { public function __construct(string $message, array $attributes = []) {
$this->rawMessage = $message; $this->rawMessage = $message;
if (is_array($attributes)) {
$this->attributes = $attributes; $this->attributes = $attributes;
} else {
$this->attributes = ['startLine' => $attributes];
}
$this->updateMessage(); $this->updateMessage();
} }
@ -37,6 +32,7 @@ class Error extends \RuntimeException
* Gets the line the error starts in. * Gets the line the error starts in.
* *
* @return int Error start line * @return int Error start line
* @phpstan-return -1|positive-int
*/ */
public function getStartLine(): int { public function getStartLine(): int {
return $this->attributes['startLine'] ?? -1; return $this->attributes['startLine'] ?? -1;
@ -46,6 +42,7 @@ class Error extends \RuntimeException
* Gets the line the error ends in. * Gets the line the error ends in.
* *
* @return int Error end line * @return int Error end line
* @phpstan-return -1|positive-int
*/ */
public function getEndLine(): int { public function getEndLine(): int {
return $this->attributes['endLine'] ?? -1; return $this->attributes['endLine'] ?? -1;
@ -54,7 +51,7 @@ class Error extends \RuntimeException
/** /**
* Gets the attributes of the node/token the error occurred at. * Gets the attributes of the node/token the error occurred at.
* *
* @return array * @return array<string, mixed>
*/ */
public function getAttributes(): array { public function getAttributes(): array {
return $this->attributes; return $this->attributes;
@ -63,9 +60,9 @@ class Error extends \RuntimeException
/** /**
* Sets the attributes of the node/token the error occurred at. * Sets the attributes of the node/token the error occurred at.
* *
* @param array $attributes * @param array<string, mixed> $attributes
*/ */
public function setAttributes(array $attributes) { public function setAttributes(array $attributes): void {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->updateMessage(); $this->updateMessage();
} }
@ -75,7 +72,7 @@ class Error extends \RuntimeException
* *
* @param string $message Error message * @param string $message Error message
*/ */
public function setRawMessage(string $message) { public function setRawMessage(string $message): void {
$this->rawMessage = $message; $this->rawMessage = $message;
$this->updateMessage(); $this->updateMessage();
} }
@ -85,7 +82,7 @@ class Error extends \RuntimeException
* *
* @param int $line Error start line * @param int $line Error start line
*/ */
public function setStartLine(int $line) { public function setStartLine(int $line): void {
$this->attributes['startLine'] = $line; $this->attributes['startLine'] = $line;
$this->updateMessage(); $this->updateMessage();
} }
@ -94,8 +91,6 @@ class Error extends \RuntimeException
* Returns whether the error has start and end column information. * Returns whether the error has start and end column information.
* *
* For column information enable the startFilePos and endFilePos in the lexer options. * For column information enable the startFilePos and endFilePos in the lexer options.
*
* @return bool
*/ */
public function hasColumnInfo(): bool { public function hasColumnInfo(): bool {
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
@ -105,7 +100,6 @@ class Error extends \RuntimeException
* Gets the start column (1-based) into the line where the error started. * Gets the start column (1-based) into the line where the error started.
* *
* @param string $code Source code of the file * @param string $code Source code of the file
* @return int
*/ */
public function getStartColumn(string $code): int { public function getStartColumn(string $code): int {
if (!$this->hasColumnInfo()) { if (!$this->hasColumnInfo()) {
@ -119,7 +113,6 @@ class Error extends \RuntimeException
* Gets the end column (1-based) into the line where the error ended. * Gets the end column (1-based) into the line where the error ended.
* *
* @param string $code Source code of the file * @param string $code Source code of the file
* @return int
*/ */
public function getEndColumn(string $code): int { public function getEndColumn(string $code): int {
if (!$this->hasColumnInfo()) { if (!$this->hasColumnInfo()) {
@ -168,7 +161,7 @@ class Error extends \RuntimeException
/** /**
* Updates the exception message after a change to rawMessage or rawLine. * Updates the exception message after a change to rawMessage or rawLine.
*/ */
protected function updateMessage() { protected function updateMessage(): void {
$this->message = $this->rawMessage; $this->message = $this->rawMessage;
if (-1 === $this->getStartLine()) { if (-1 === $this->getStartLine()) {

View file

@ -2,12 +2,11 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
interface ErrorHandler interface ErrorHandler {
{
/** /**
* Handle an error generated during lexing, parsing or some other operation. * Handle an error generated during lexing, parsing or some other operation.
* *
* @param Error $error The error that needs to be handled * @param Error $error The error that needs to be handled
*/ */
public function handleError(Error $error); public function handleError(Error $error): void;
} }

View file

@ -10,12 +10,11 @@ use ncc\ThirdParty\nikic\PhpParser\ErrorHandler;
* *
* This allows graceful handling of errors. * This allows graceful handling of errors.
*/ */
class Collecting implements ErrorHandler class Collecting implements ErrorHandler {
{
/** @var Error[] Collected errors */ /** @var Error[] Collected errors */
private $errors = []; private array $errors = [];
public function handleError(Error $error) { public function handleError(Error $error): void {
$this->errors[] = $error; $this->errors[] = $error;
} }
@ -30,8 +29,6 @@ class Collecting implements ErrorHandler
/** /**
* Check whether there are any errors. * Check whether there are any errors.
*
* @return bool
*/ */
public function hasErrors(): bool { public function hasErrors(): bool {
return !empty($this->errors); return !empty($this->errors);
@ -40,7 +37,7 @@ class Collecting implements ErrorHandler
/** /**
* Reset/clear collected errors. * Reset/clear collected errors.
*/ */
public function clearErrors() { public function clearErrors(): void {
$this->errors = []; $this->errors = [];
} }
} }

View file

@ -10,9 +10,8 @@ use ncc\ThirdParty\nikic\PhpParser\ErrorHandler;
* *
* This is the default strategy used by all components. * This is the default strategy used by all components.
*/ */
class Throwing implements ErrorHandler class Throwing implements ErrorHandler {
{ public function handleError(Error $error): void {
public function handleError(Error $error) {
throw $error; throw $error;
} }
} }

View file

@ -5,20 +5,24 @@ namespace ncc\ThirdParty\nikic\PhpParser\Internal;
/** /**
* @internal * @internal
*/ */
class DiffElem class DiffElem {
{ public const TYPE_KEEP = 0;
const TYPE_KEEP = 0; public const TYPE_REMOVE = 1;
const TYPE_REMOVE = 1; public const TYPE_ADD = 2;
const TYPE_ADD = 2; public const TYPE_REPLACE = 3;
const TYPE_REPLACE = 3;
/** @var int One of the TYPE_* constants */ /** @var int One of the TYPE_* constants */
public $type; public int $type;
/** @var mixed Is null for add operations */ /** @var mixed Is null for add operations */
public $old; public $old;
/** @var mixed Is null for remove operations */ /** @var mixed Is null for remove operations */
public $new; public $new;
/**
* @param int $type One of the TYPE_* constants
* @param mixed $old Is null for add operations
* @param mixed $new Is null for remove operations
*/
public function __construct(int $type, $old, $new) { public function __construct(int $type, $old, $new) {
$this->type = $type; $this->type = $type;
$this->old = $old; $this->old = $old;

View file

@ -8,16 +8,17 @@ namespace ncc\ThirdParty\nikic\PhpParser\Internal;
* Myers, Eugene W. "An O (ND) difference algorithm and its variations." * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
* Algorithmica 1.1 (1986): 251-266. * Algorithmica 1.1 (1986): 251-266.
* *
* @template T
* @internal * @internal
*/ */
class Differ class Differ {
{ /** @var callable(T, T): bool */
private $isEqual; private $isEqual;
/** /**
* Create differ over the given equality relation. * Create differ over the given equality relation.
* *
* @param callable $isEqual Equality relation with signature function($a, $b) : bool * @param callable(T, T): bool $isEqual Equality relation
*/ */
public function __construct(callable $isEqual) { public function __construct(callable $isEqual) {
$this->isEqual = $isEqual; $this->isEqual = $isEqual;
@ -26,12 +27,14 @@ class Differ
/** /**
* Calculate diff (edit script) from $old to $new. * Calculate diff (edit script) from $old to $new.
* *
* @param array $old Original array * @param T[] $old Original array
* @param array $new New array * @param T[] $new New array
* *
* @return DiffElem[] Diff (edit script) * @return DiffElem[] Diff (edit script)
*/ */
public function diff(array $old, array $new) { public function diff(array $old, array $new): array {
$old = \array_values($old);
$new = \array_values($new);
list($trace, $x, $y) = $this->calculateTrace($old, $new); list($trace, $x, $y) = $this->calculateTrace($old, $new);
return $this->extractDiff($trace, $x, $y, $old, $new); return $this->extractDiff($trace, $x, $y, $old, $new);
} }
@ -42,18 +45,23 @@ class Differ
* If a sequence of remove operations is followed by the same number of add operations, these * If a sequence of remove operations is followed by the same number of add operations, these
* will be coalesced into replace operations. * will be coalesced into replace operations.
* *
* @param array $old Original array * @param T[] $old Original array
* @param array $new New array * @param T[] $new New array
* *
* @return DiffElem[] Diff (edit script), including replace operations * @return DiffElem[] Diff (edit script), including replace operations
*/ */
public function diffWithReplacements(array $old, array $new) { public function diffWithReplacements(array $old, array $new): array {
return $this->coalesceReplacements($this->diff($old, $new)); return $this->coalesceReplacements($this->diff($old, $new));
} }
private function calculateTrace(array $a, array $b) { /**
$n = \count($a); * @param T[] $old
$m = \count($b); * @param T[] $new
* @return array{array<int, array<int, int>>, int, int}
*/
private function calculateTrace(array $old, array $new): array {
$n = \count($old);
$m = \count($new);
$max = $n + $m; $max = $n + $m;
$v = [1 => 0]; $v = [1 => 0];
$trace = []; $trace = [];
@ -67,7 +75,7 @@ class Differ
} }
$y = $x - $k; $y = $x - $k;
while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
$x++; $x++;
$y++; $y++;
} }
@ -81,7 +89,13 @@ class Differ
throw new \Exception('Should not happen'); throw new \Exception('Should not happen');
} }
private function extractDiff(array $trace, int $x, int $y, array $a, array $b) { /**
* @param array<int, array<int, int>> $trace
* @param T[] $old
* @param T[] $new
* @return DiffElem[]
*/
private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array {
$result = []; $result = [];
for ($d = \count($trace) - 1; $d >= 0; $d--) { for ($d = \count($trace) - 1; $d >= 0; $d--) {
$v = $trace[$d]; $v = $trace[$d];
@ -97,7 +111,7 @@ class Differ
$prevY = $prevX - $prevK; $prevY = $prevX - $prevK;
while ($x > $prevX && $y > $prevY) { while ($x > $prevX && $y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]); $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
$x--; $x--;
$y--; $y--;
} }
@ -107,12 +121,12 @@ class Differ
} }
while ($x > $prevX) { while ($x > $prevX) {
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null); $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
$x--; $x--;
} }
while ($y > $prevY) { while ($y > $prevY) {
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]); $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
$y--; $y--;
} }
} }
@ -125,7 +139,7 @@ class Differ
* @param DiffElem[] $diff * @param DiffElem[] $diff
* @return DiffElem[] * @return DiffElem[]
*/ */
private function coalesceReplacements(array $diff) { private function coalesceReplacements(array $diff): array {
$newDiff = []; $newDiff = [];
$c = \count($diff); $c = \count($diff);
for ($i = 0; $i < $c; $i++) { for ($i = 0; $i < $c; $i++) {

View file

@ -15,23 +15,30 @@ use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
* *
* @internal * @internal
*/ */
class PrintableNewAnonClassNode extends Expr class PrintableNewAnonClassNode extends Expr {
{
/** @var Node\AttributeGroup[] PHP attribute groups */ /** @var Node\AttributeGroup[] PHP attribute groups */
public $attrGroups; public array $attrGroups;
/** @var int Modifiers */ /** @var int Modifiers */
public $flags; public int $flags;
/** @var Node\Arg[] Arguments */ /** @var (Node\Arg|Node\VariadicPlaceholder)[] Arguments */
public $args; public array $args;
/** @var null|Node\Name Name of extended class */ /** @var null|Node\Name Name of extended class */
public $extends; public ?Node\Name $extends;
/** @var Node\Name[] Names of implemented interfaces */ /** @var Node\Name[] Names of implemented interfaces */
public $implements; public array $implements;
/** @var Node\Stmt[] Statements */ /** @var Node\Stmt[] Statements */
public $stmts; public array $stmts;
/**
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
* @param (Node\Arg|Node\VariadicPlaceholder)[] $args Arguments
* @param Node\Name|null $extends Name of extended class
* @param Node\Name[] $implements Names of implemented interfaces
* @param Node\Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Attributes
*/
public function __construct( public function __construct(
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements, array $attrGroups, int $flags, array $args, ?Node\Name $extends, array $implements,
array $stmts, array $attributes array $stmts, array $attributes
) { ) {
parent::__construct($attributes); parent::__construct($attributes);
@ -43,7 +50,7 @@ class PrintableNewAnonClassNode extends Expr
$this->stmts = $stmts; $this->stmts = $stmts;
} }
public static function fromNewNode(Expr\New_ $newNode) { public static function fromNewNode(Expr\New_ $newNode): self {
$class = $newNode->class; $class = $newNode->class;
assert($class instanceof Node\Stmt\Class_); assert($class instanceof Node\Stmt\Class_);
// We don't assert that $class->name is null here, to allow consumers to assign unique names // We don't assert that $class->name is null here, to allow consumers to assign unique names

View file

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

View file

@ -2,22 +2,23 @@
namespace ncc\ThirdParty\nikic\PhpParser\Internal; namespace ncc\ThirdParty\nikic\PhpParser\Internal;
use ncc\ThirdParty\nikic\PhpParser\Token;
/** /**
* Provides operations on token streams, for use by pretty printer. * Provides operations on token streams, for use by pretty printer.
* *
* @internal * @internal
*/ */
class TokenStream class TokenStream {
{ /** @var Token[] Tokens (in PhpToken::tokenize() format) */
/** @var array Tokens (in token_get_all format) */ private array $tokens;
private $tokens;
/** @var int[] Map from position to indentation */ /** @var int[] Map from position to indentation */
private $indentMap; private array $indentMap;
/** /**
* Create token stream instance. * Create token stream instance.
* *
* @param array $tokens Tokens in token_get_all() format * @param Token[] $tokens Tokens in PhpToken::tokenize() format
*/ */
public function __construct(array $tokens) { public function __construct(array $tokens) {
$this->tokens = $tokens; $this->tokens = $tokens;
@ -29,8 +30,6 @@ class TokenStream
* *
* @param int $startPos Start position * @param int $startPos Start position
* @param int $endPos End position * @param int $endPos End position
*
* @return bool
*/ */
public function haveParens(int $startPos, int $endPos): bool { public function haveParens(int $startPos, int $endPos): bool {
return $this->haveTokenImmediatelyBefore($startPos, '(') return $this->haveTokenImmediatelyBefore($startPos, '(')
@ -42,8 +41,6 @@ class TokenStream
* *
* @param int $startPos Start position * @param int $startPos Start position
* @param int $endPos End position * @param int $endPos End position
*
* @return bool
*/ */
public function haveBraces(int $startPos, int $endPos): bool { public function haveBraces(int $startPos, int $endPos): bool {
return ($this->haveTokenImmediatelyBefore($startPos, '{') return ($this->haveTokenImmediatelyBefore($startPos, '{')
@ -65,12 +62,11 @@ class TokenStream
$tokens = $this->tokens; $tokens = $this->tokens;
$pos--; $pos--;
for (; $pos >= 0; $pos--) { for (; $pos >= 0; $pos--) {
$tokenType = $tokens[$pos][0]; $token = $tokens[$pos];
if ($tokenType === $expectedTokenType) { if ($token->is($expectedTokenType)) {
return true; return true;
} }
if ($tokenType !== \T_WHITESPACE if (!$token->isIgnorable()) {
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
break; break;
} }
} }
@ -90,20 +86,20 @@ class TokenStream
public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool { public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos++; $pos++;
for (; $pos < \count($tokens); $pos++) { for ($c = \count($tokens); $pos < $c; $pos++) {
$tokenType = $tokens[$pos][0]; $token = $tokens[$pos];
if ($tokenType === $expectedTokenType) { if ($token->is($expectedTokenType)) {
return true; return true;
} }
if ($tokenType !== \T_WHITESPACE if (!$token->isIgnorable()) {
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
break; break;
} }
} }
return false; return false;
} }
public function skipLeft(int $pos, $skipTokenType) { /** @param int|string|(int|string)[] $skipTokenType */
public function skipLeft(int $pos, $skipTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos = $this->skipLeftWhitespace($pos); $pos = $this->skipLeftWhitespace($pos);
@ -111,7 +107,7 @@ class TokenStream
return $pos; return $pos;
} }
if ($tokens[$pos][0] !== $skipTokenType) { if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there // Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token'); throw new \Exception('Encountered unexpected token');
} }
@ -120,7 +116,8 @@ class TokenStream
return $this->skipLeftWhitespace($pos); return $this->skipLeftWhitespace($pos);
} }
public function skipRight(int $pos, $skipTokenType) { /** @param int|string|(int|string)[] $skipTokenType */
public function skipRight(int $pos, $skipTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
$pos = $this->skipRightWhitespace($pos); $pos = $this->skipRightWhitespace($pos);
@ -128,7 +125,7 @@ class TokenStream
return $pos; return $pos;
} }
if ($tokens[$pos][0] !== $skipTokenType) { if (!$tokens[$pos]->is($skipTokenType)) {
// Shouldn't happen. The skip token MUST be there // Shouldn't happen. The skip token MUST be there
throw new \Exception('Encountered unexpected token'); throw new \Exception('Encountered unexpected token');
} }
@ -143,11 +140,10 @@ class TokenStream
* @param int $pos Token position * @param int $pos Token position
* @return int Non-whitespace token position * @return int Non-whitespace token position
*/ */
public function skipLeftWhitespace(int $pos) { public function skipLeftWhitespace(int $pos): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for (; $pos >= 0; $pos--) { for (; $pos >= 0; $pos--) {
$type = $tokens[$pos][0]; if (!$tokens[$pos]->isIgnorable()) {
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
break; break;
} }
} }
@ -160,22 +156,21 @@ class TokenStream
* @param int $pos Token position * @param int $pos Token position
* @return int Non-whitespace token position * @return int Non-whitespace token position
*/ */
public function skipRightWhitespace(int $pos) { public function skipRightWhitespace(int $pos): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) { for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0]; if (!$tokens[$pos]->isIgnorable()) {
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
break; break;
} }
} }
return $pos; return $pos;
} }
public function findRight(int $pos, $findTokenType) { /** @param int|string|(int|string)[] $findTokenType */
public function findRight(int $pos, $findTokenType): int {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($count = \count($tokens); $pos < $count; $pos++) { for ($count = \count($tokens); $pos < $count; $pos++) {
$type = $tokens[$pos][0]; if ($tokens[$pos]->is($findTokenType)) {
if ($type === $findTokenType) {
return $pos; return $pos;
} }
} }
@ -190,22 +185,16 @@ class TokenStream
* @param int|string $tokenType Token type to look for * @param int|string $tokenType Token type to look for
* @return bool Whether the token occurs in the given range * @return bool Whether the token occurs in the given range
*/ */
public function haveTokenInRange(int $startPos, int $endPos, $tokenType) { public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool {
$tokens = $this->tokens; $tokens = $this->tokens;
for ($pos = $startPos; $pos < $endPos; $pos++) { for ($pos = $startPos; $pos < $endPos; $pos++) {
if ($tokens[$pos][0] === $tokenType) { if ($tokens[$pos]->is($tokenType)) {
return true; return true;
} }
} }
return false; return false;
} }
public function haveBracesInRange(int $startPos, int $endPos) {
return $this->haveTokenInRange($startPos, $endPos, '{')
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|| $this->haveTokenInRange($startPos, $endPos, '}');
}
public function haveTagInRange(int $startPos, int $endPos): bool { public function haveTagInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG) return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG); || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
@ -236,24 +225,20 @@ class TokenStream
$result = ''; $result = '';
for ($pos = $from; $pos < $to; $pos++) { for ($pos = $from; $pos < $to; $pos++) {
$token = $tokens[$pos]; $token = $tokens[$pos];
if (\is_array($token)) { $id = $token->id;
$type = $token[0]; $text = $token->text;
$content = $token[1]; if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) {
if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { $result .= $text;
$result .= $content;
} else { } else {
// TODO Handle non-space indentation // TODO Handle non-space indentation
if ($indent < 0) { if ($indent < 0) {
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content); $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text);
} elseif ($indent > 0) { } elseif ($indent > 0) {
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content); $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text);
} else { } else {
$result .= $content; $result .= $text;
} }
} }
} else {
$result .= $token;
}
} }
return $result; return $result;
} }
@ -263,17 +248,21 @@ class TokenStream
* *
* @return int[] Token position to indentation map * @return int[] Token position to indentation map
*/ */
private function calcIndentMap() { private function calcIndentMap(): array {
$indentMap = []; $indentMap = [];
$indent = 0; $indent = 0;
foreach ($this->tokens as $token) { foreach ($this->tokens as $i => $token) {
$indentMap[] = $indent; $indentMap[] = $indent;
if ($token[0] === \T_WHITESPACE) { if ($token->id === \T_WHITESPACE) {
$content = $token[1]; $content = $token->text;
$newlinePos = \strrpos($content, "\n"); $newlinePos = \strrpos($content, "\n");
if (false !== $newlinePos) { if (false !== $newlinePos) {
$indent = \strlen($content) - $newlinePos - 1; $indent = \strlen($content) - $newlinePos - 1;
} elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG &&
$this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") {
// Special case: Newline at the end of opening tag followed by whitespace.
$indent = \strlen($content);
} }
} }
} }

View file

@ -2,11 +2,11 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
class JsonDecoder class JsonDecoder {
{ /** @var \ReflectionClass<Node>[] Node type to reflection class map */
/** @var \ReflectionClass[] Node type to reflection class map */ private array $reflectionClassCache;
private $reflectionClassCache;
/** @return mixed */
public function decode(string $json) { public function decode(string $json) {
$value = json_decode($json, true); $value = json_decode($json, true);
if (json_last_error()) { if (json_last_error()) {
@ -16,6 +16,10 @@ class JsonDecoder
return $this->decodeRecursive($value); return $this->decodeRecursive($value);
} }
/**
* @param mixed $value
* @return mixed
*/
private function decodeRecursive($value) { private function decodeRecursive($value) {
if (\is_array($value)) { if (\is_array($value)) {
if (isset($value['nodeType'])) { if (isset($value['nodeType'])) {
@ -44,7 +48,6 @@ class JsonDecoder
} }
$reflectionClass = $this->reflectionClassFromNodeType($nodeType); $reflectionClass = $this->reflectionClassFromNodeType($nodeType);
/** @var Node $node */
$node = $reflectionClass->newInstanceWithoutConstructor(); $node = $reflectionClass->newInstanceWithoutConstructor();
if (isset($value['attributes'])) { if (isset($value['attributes'])) {
@ -79,6 +82,7 @@ class JsonDecoder
); );
} }
/** @return \ReflectionClass<Node> */
private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass { private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass {
if (!isset($this->reflectionClassCache[$nodeType])) { if (!isset($this->reflectionClassCache[$nodeType])) {
$className = $this->classNameFromNodeType($nodeType); $className = $this->classNameFromNodeType($nodeType);
@ -87,6 +91,7 @@ class JsonDecoder
return $this->reflectionClassCache[$nodeType]; return $this->reflectionClassCache[$nodeType];
} }
/** @return class-string<Node> */
private function classNameFromNodeType(string $nodeType): string { private function classNameFromNodeType(string $nodeType): string {
$className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
if (class_exists($className)) { if (class_exists($className)) {

View file

@ -2,101 +2,44 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
use ncc\ThirdParty\nikic\PhpParser\Parser\Tokens; require __DIR__ . '/compatibility_tokens.php';
class Lexer
{
protected $code;
protected $tokens;
protected $pos;
protected $line;
protected $filePos;
protected $prevCloseTagHasNewline;
protected $tokenMap;
protected $dropTokens;
protected $identifierTokens;
private $attributeStartLineUsed;
private $attributeEndLineUsed;
private $attributeStartTokenPosUsed;
private $attributeEndTokenPosUsed;
private $attributeStartFilePosUsed;
private $attributeEndFilePosUsed;
private $attributeCommentsUsed;
class Lexer {
/** /**
* Creates a Lexer. * Tokenize the provided source code.
* *
* @param array $options Options array. Currently only the 'usedAttributes' option is supported, * The token array is in the same format as provided by the PhpToken::tokenize() method in
* which is an array of attributes to add to the AST nodes. Possible * PHP 8.0. The tokens are instances of PhpParser\Token, to abstract over a polyfill
* attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos', * implementation in earlier PHP version.
* 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
* first three. For more info see getNextToken() docs.
*/
public function __construct(array $options = []) {
// Create Map from internal tokens to PhpParser tokens.
$this->defineCompatibilityTokens();
$this->tokenMap = $this->createTokenMap();
$this->identifierTokens = $this->createIdentifierTokenMap();
// 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(
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
);
$defaultAttributes = ['comments', 'startLine', 'endLine'];
$usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
// Create individual boolean properties to make these checks faster.
$this->attributeStartLineUsed = isset($usedAttributes['startLine']);
$this->attributeEndLineUsed = isset($usedAttributes['endLine']);
$this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
$this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
$this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
$this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
$this->attributeCommentsUsed = isset($usedAttributes['comments']);
}
/**
* Initializes the lexer for lexing the provided source code.
* *
* This function does not throw if lexing errors occur. Instead, errors may be retrieved using * The token array is terminated by a sentinel token with token ID 0.
* the getErrors() method. * The token array does not discard any tokens (i.e. whitespace and comments are included).
* The token position attributes are against this token array.
* *
* @param string $code The source code to lex * @param string $code The source code to tokenize.
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
* ErrorHandler\Throwing * ErrorHandler\Throwing.
* @return Token[] Tokens
*/ */
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
if (null === $errorHandler) { if (null === $errorHandler) {
$errorHandler = new ErrorHandler\Throwing(); $errorHandler = new ErrorHandler\Throwing();
} }
$this->code = $code; // keep the code around for __halt_compiler() handling
$this->pos = -1;
$this->line = 1;
$this->filePos = 0;
// 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'); $scream = ini_set('xdebug.scream', '0');
$this->tokens = @token_get_all($code); $tokens = @Token::tokenize($code);
$this->postprocessTokens($errorHandler); $this->postprocessTokens($tokens, $errorHandler);
if (false !== $scream) { if (false !== $scream) {
ini_set('xdebug.scream', $scream); ini_set('xdebug.scream', $scream);
} }
return $tokens;
} }
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) { private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void {
$tokens = []; $chr = $token->text;
for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i];
if ($chr === "\0") { if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case // PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte'; $errorMsg = 'Unexpected null byte';
@ -106,455 +49,68 @@ class Lexer
); );
} }
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
$errorHandler->handleError(new Error($errorMsg, [ $errorHandler->handleError(new Error($errorMsg, [
'startLine' => $line, 'startLine' => $token->line,
'endLine' => $line, 'endLine' => $token->line,
'startFilePos' => $i, 'startFilePos' => $token->pos,
'endFilePos' => $i, 'endFilePos' => $token->pos,
])); ]));
} }
return $tokens;
private function isUnterminatedComment(Token $token): bool {
return $token->is([\T_COMMENT, \T_DOC_COMMENT])
&& substr($token->text, 0, 2) === '/*'
&& substr($token->text, -2) !== '*/';
} }
/** /**
* Check whether comment token is unterminated. * @param list<Token> $tokens
*
* @return bool
*/ */
private function isUnterminatedComment($token) : bool { protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void {
return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) // This function reports errors (bad characters and unterminated comments) in the token
&& substr($token[1], 0, 2) === '/*' // array, and performs certain canonicalizations:
&& substr($token[1], -2) !== '*/';
}
protected function postprocessTokens(ErrorHandler $errorHandler) {
// 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.
//
// Additionally, we perform a number of canonicalizations here:
// * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore.
// * Use PHP 8.0 T_NAME_* tokens.
// * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
// T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types. // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
// * Add a sentinel token with ID 0.
$numTokens = \count($tokens);
if ($numTokens === 0) {
// Empty input edge case: Just add the sentinel token.
$tokens[] = new Token(0, "\0", 1, 0);
return;
}
$filePos = 0;
$line = 1;
$numTokens = \count($this->tokens);
for ($i = 0; $i < $numTokens; $i++) { for ($i = 0; $i < $numTokens; $i++) {
$token = $this->tokens[$i]; $token = $tokens[$i];
if ($token->id === \T_BAD_CHARACTER) {
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token. $this->handleInvalidCharacter($token, $errorHandler);
// In this case we only need to emit an error.
if ($token[0] === \T_BAD_CHARACTER) {
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
} }
if ($token[0] === \T_COMMENT && substr($token[1], 0, 2) !== '/*' if ($token->id === \ord('&')) {
&& preg_match('/(\r\n|\n|\r)$/D', $token[1], $matches)) {
$trailingNewline = $matches[0];
$token[1] = substr($token[1], 0, -strlen($trailingNewline));
$this->tokens[$i] = $token;
if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) {
// Move trailing newline into following T_WHITESPACE token, if it already exists.
$this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1];
$this->tokens[$i + 1][2]--;
} else {
// Otherwise, we need to create a new T_WHITESPACE token.
array_splice($this->tokens, $i + 1, 0, [
[\T_WHITESPACE, $trailingNewline, $line],
]);
$numTokens++;
}
}
// Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING
// into a single token.
if (\is_array($token)
&& ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) {
$lastWasSeparator = $token[0] === \T_NS_SEPARATOR;
$text = $token[1];
for ($j = $i + 1; isset($this->tokens[$j]); $j++) {
if ($lastWasSeparator) {
if (!isset($this->identifierTokens[$this->tokens[$j][0]])) {
break;
}
$lastWasSeparator = false;
} else {
if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) {
break;
}
$lastWasSeparator = true;
}
$text .= $this->tokens[$j][1];
}
if ($lastWasSeparator) {
// Trailing separator is not part of the name.
$j--;
$text = substr($text, 0, -1);
}
if ($j > $i + 1) {
if ($token[0] === \T_NS_SEPARATOR) {
$type = \T_NAME_FULLY_QUALIFIED;
} else if ($token[0] === \T_NAMESPACE) {
$type = \T_NAME_RELATIVE;
} else {
$type = \T_NAME_QUALIFIED;
}
$token = [$type, $text, $line];
array_splice($this->tokens, $i, $j - $i, [$token]);
$numTokens -= $j - $i - 1;
}
}
if ($token === '&') {
$next = $i + 1; $next = $i + 1;
while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) { while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) {
$next++; $next++;
} }
$followedByVarOrVarArg = isset($this->tokens[$next]) && $followedByVarOrVarArg = isset($tokens[$next]) &&
($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS); $tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]);
$this->tokens[$i] = $token = [ $token->id = $followedByVarOrVarArg
$followedByVarOrVarArg
? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
: \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
'&', }
$line,
];
} }
$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);
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, $nextFilePos, $line, $errorHandler);
$filePos = (int) $nextFilePos;
array_splice($this->tokens, $i, 0, $badCharTokens);
$numTokens += \count($badCharTokens);
$i += \count($badCharTokens);
}
$filePos += $tokenLen;
$line += substr_count($tokenValue, "\n");
}
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
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler);
$this->tokens = array_merge($this->tokens, $badCharTokens);
}
return;
}
if (count($this->tokens) > 0) {
// Check for unterminated comment // Check for unterminated comment
$lastToken = $this->tokens[count($this->tokens) - 1]; $lastToken = $tokens[$numTokens - 1];
if ($this->isUnterminatedComment($lastToken)) { if ($this->isUnterminatedComment($lastToken)) {
$errorHandler->handleError(new Error('Unterminated comment', [ $errorHandler->handleError(new Error('Unterminated comment', [
'startLine' => $line - substr_count($lastToken[1], "\n"), 'startLine' => $lastToken->line,
'endLine' => $line, 'endLine' => $lastToken->getEndLine(),
'startFilePos' => $filePos - \strlen($lastToken[1]), 'startFilePos' => $lastToken->pos,
'endFilePos' => $filePos, 'endFilePos' => $lastToken->getEndPos(),
])); ]));
} }
}
}
/** // Add sentinel token.
* Fetches the next token. $tokens[] = new Token(0, "\0", $lastToken->getEndLine(), $lastToken->getEndPos());
*
* The available attributes are determined by the 'usedAttributes' option, which can
* be specified in the constructor. The following attributes are supported:
*
* * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances,
* representing all comments that occurred between the previous
* non-discarded token and the current one.
* * 'startLine' => Line in which the node starts.
* * 'endLine' => Line in which the node ends.
* * 'startTokenPos' => Offset into the token array of the first token in the node.
* * 'endTokenPos' => Offset into the token array of the last token in the node.
* * 'startFilePos' => Offset into the code string of the first character that is part of the node.
* * 'endFilePos' => Offset into the code string of the last character that is part of the node.
*
* @param mixed $value Variable to store token content in
* @param mixed $startAttributes Variable to store start attributes in
* @param mixed $endAttributes Variable to store end attributes in
*
* @return int Token id
*/
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int {
$startAttributes = [];
$endAttributes = [];
while (1) {
if (isset($this->tokens[++$this->pos])) {
$token = $this->tokens[$this->pos];
} else {
// EOF token with ID 0
$token = "\0";
}
if ($this->attributeStartLineUsed) {
$startAttributes['startLine'] = $this->line;
}
if ($this->attributeStartTokenPosUsed) {
$startAttributes['startTokenPos'] = $this->pos;
}
if ($this->attributeStartFilePosUsed) {
$startAttributes['startFilePos'] = $this->filePos;
}
if (\is_string($token)) {
$value = $token;
if (isset($token[1])) {
// bug in token_get_all
$this->filePos += 2;
$id = ord('"');
} else {
$this->filePos += 1;
$id = ord($token);
}
} 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")
|| false !== strpos($token[1], "\r");
} elseif (\T_INLINE_HTML === $token[0]) {
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
}
$this->line += substr_count($value, "\n");
$this->filePos += \strlen($value);
} else {
$origLine = $this->line;
$origFilePos = $this->filePos;
$this->line += substr_count($token[1], "\n");
$this->filePos += \strlen($token[1]);
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
if ($this->attributeCommentsUsed) {
$comment = \T_DOC_COMMENT === $token[0]
? new Comment\Doc($token[1],
$origLine, $origFilePos, $this->pos,
$this->line, $this->filePos - 1, $this->pos)
: new Comment($token[1],
$origLine, $origFilePos, $this->pos,
$this->line, $this->filePos - 1, $this->pos);
$startAttributes['comments'][] = $comment;
}
}
continue;
}
if ($this->attributeEndLineUsed) {
$endAttributes['endLine'] = $this->line;
}
if ($this->attributeEndTokenPosUsed) {
$endAttributes['endTokenPos'] = $this->pos;
}
if ($this->attributeEndFilePosUsed) {
$endAttributes['endFilePos'] = $this->filePos - 1;
}
return $id;
}
throw new \RuntimeException('Reached end of lexer loop');
}
/**
* Returns the token array for current code.
*
* The token array is in the same format as provided by the
* token_get_all() function and does not discard tokens (i.e.
* whitespace and comments are included). The token position
* attributes are against this token array.
*
* @return array Array of tokens in token_get_all() format
*/
public function getTokens() : array {
return $this->tokens;
}
/**
* Handles __halt_compiler() by returning the text after it.
*
* @return string Remaining text
*/
public function handleHaltCompiler() : string {
// text after T_HALT_COMPILER, still including ();
$textAfter = substr($this->code, $this->filePos);
// ensure that it is followed by ();
// this simplifies the situation, by not allowing any comments
// in between of the tokens.
if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
throw new Error('__HALT_COMPILER must be followed by "();"');
}
// prevent the lexer from returning any further tokens
$this->pos = count($this->tokens);
// return with (); removed
return substr($textAfter, strlen($matches[0]));
}
private function defineCompatibilityTokens() {
static $compatTokensDefined = false;
if ($compatTokensDefined) {
return;
}
$compatTokens = [
// PHP 7.4
'T_BAD_CHARACTER',
'T_FN',
'T_COALESCE_EQUAL',
// PHP 8.0
'T_NAME_QUALIFIED',
'T_NAME_FULLY_QUALIFIED',
'T_NAME_RELATIVE',
'T_MATCH',
'T_NULLSAFE_OBJECT_OPERATOR',
'T_ATTRIBUTE',
// PHP 8.1
'T_ENUM',
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
'T_READONLY',
];
// PHP-Parser might be used together with another library that also emulates some or all
// of these tokens. Perform a sanity-check that all already defined tokens have been
// assigned a unique ID.
$usedTokenIds = [];
foreach ($compatTokens as $token) {
if (\defined($token)) {
$tokenId = \constant($token);
$clashingToken = $usedTokenIds[$tokenId] ?? null;
if ($clashingToken !== null) {
throw new \Error(sprintf(
'Token %s has same ID as token %s, ' .
'you may be using a library with broken token emulation',
$token, $clashingToken
));
}
$usedTokenIds[$tokenId] = $token;
}
}
// Now define any tokens that have not yet been emulated. Try to assign IDs from -1
// downwards, but skip any IDs that may already be in use.
$newTokenId = -1;
foreach ($compatTokens as $token) {
if (!\defined($token)) {
while (isset($usedTokenIds[$newTokenId])) {
$newTokenId--;
}
\define($token, $newTokenId);
$newTokenId--;
}
}
$compatTokensDefined = true;
}
/**
* Creates the token map.
*
* The token map maps the PHP internal token identifiers
* to the identifiers used by the Parser. Additionally it
* maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
*
* @return array The token map
*/
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) {
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
} elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
$tokenMap[$i] = Tokens::T_ECHO;
} 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;
} elseif (defined($name = Tokens::class . '::' . $name)) {
// Other tokens can be mapped directly
$tokenMap[$i] = constant($name);
}
}
}
// HHVM uses a special token for numbers that overflow to double
if (defined('T_ONUMBER')) {
$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;
}
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
$tokenMap[\T_FN] = Tokens::T_FN;
$tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
$tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED;
$tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED;
$tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
$tokenMap[\T_READONLY] = Tokens::T_READONLY;
return $tokenMap;
}
private function createIdentifierTokenMap(): array {
// Based on semi_reserved production.
return array_fill_keys([
\T_STRING,
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
\T_MATCH,
], true);
} }
} }

View file

@ -5,61 +5,48 @@ namespace ncc\ThirdParty\nikic\PhpParser\Lexer;
use ncc\ThirdParty\nikic\PhpParser\Error; use ncc\ThirdParty\nikic\PhpParser\Error;
use ncc\ThirdParty\nikic\PhpParser\ErrorHandler; use ncc\ThirdParty\nikic\PhpParser\ErrorHandler;
use ncc\ThirdParty\nikic\PhpParser\Lexer; use ncc\ThirdParty\nikic\PhpParser\Lexer;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\AsymmetricVisibilityTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\AttributeEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\AttributeEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\EnumTokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\MatchTokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReverseEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\ReverseEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\TokenEmulator; use ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
use ncc\ThirdParty\nikic\PhpParser\Token;
class Emulative extends Lexer class Emulative extends Lexer {
{ /** @var array{int, string, string}[] Patches used to reverse changes introduced in the code */
const PHP_7_3 = '7.3dev'; private array $patches = [];
const PHP_7_4 = '7.4dev';
const PHP_8_0 = '8.0dev';
const PHP_8_1 = '8.1dev';
const PHP_8_2 = '8.2dev';
/** @var mixed[] Patches used to reverse changes introduced in the code */ /** @var list<TokenEmulator> */
private $patches = []; private array $emulators = [];
/** @var TokenEmulator[] */ private PhpVersion $targetPhpVersion;
private $emulators = [];
/** @var string */ private PhpVersion $hostPhpVersion;
private $targetPhpVersion;
/** /**
* @param mixed[] $options Lexer options. In addition to the usual options, * @param PhpVersion|null $phpVersion PHP version to emulate. Defaults to newest supported.
* accepts a 'phpVersion' string that specifies the
* version to emulate. Defaults to newest supported.
*/ */
public function __construct(array $options = []) public function __construct(?PhpVersion $phpVersion = null) {
{ $this->targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported();
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2; $this->hostPhpVersion = PhpVersion::getHostVersion();
unset($options['phpVersion']);
parent::__construct($options);
$emulators = [ $emulators = [
new FlexibleDocStringEmulator(),
new FnTokenEmulator(),
new MatchTokenEmulator(), new MatchTokenEmulator(),
new CoaleseEqualTokenEmulator(),
new NumericLiteralSeparatorEmulator(),
new NullsafeTokenEmulator(), new NullsafeTokenEmulator(),
new AttributeEmulator(), new AttributeEmulator(),
new EnumTokenEmulator(), new EnumTokenEmulator(),
new ReadonlyTokenEmulator(), new ReadonlyTokenEmulator(),
new ExplicitOctalEmulator(), new ExplicitOctalEmulator(),
new ReadonlyFunctionTokenEmulator(), new ReadonlyFunctionTokenEmulator(),
new PropertyTokenEmulator(),
new AsymmetricVisibilityTokenEmulator(),
]; ];
// Collect emulators that are relevant for the PHP version we're running // Collect emulators that are relevant for the PHP version we're running
@ -74,15 +61,18 @@ class Emulative extends Lexer
} }
} }
public function startLexing(string $code, ErrorHandler $errorHandler = null) { public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array {
$emulators = array_filter($this->emulators, function ($emulator) use ($code) { $emulators = array_filter($this->emulators, function ($emulator) use ($code) {
return $emulator->isEmulationNeeded($code); return $emulator->isEmulationNeeded($code);
}); });
if (empty($emulators)) { if (empty($emulators)) {
// Nothing to emulate, yay // Nothing to emulate, yay
parent::startLexing($code, $errorHandler); return parent::tokenize($code, $errorHandler);
return; }
if ($errorHandler === null) {
$errorHandler = new ErrorHandler\Throwing();
} }
$this->patches = []; $this->patches = [];
@ -91,9 +81,9 @@ class Emulative extends Lexer
} }
$collector = new ErrorHandler\Collecting(); $collector = new ErrorHandler\Collecting();
parent::startLexing($code, $collector); $tokens = parent::tokenize($code, $collector);
$this->sortPatches(); $this->sortPatches();
$this->fixupTokens(); $tokens = $this->fixupTokens($tokens);
$errors = $collector->getErrors(); $errors = $collector->getErrors();
if (!empty($errors)) { if (!empty($errors)) {
@ -104,22 +94,23 @@ class Emulative extends Lexer
} }
foreach ($emulators as $emulator) { foreach ($emulators as $emulator) {
$this->tokens = $emulator->emulate($code, $this->tokens); $tokens = $emulator->emulate($code, $tokens);
}
} }
private function isForwardEmulationNeeded(string $emulatorPhpVersion): bool { return $tokens;
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '<')
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>=');
} }
private function isReverseEmulationNeeded(string $emulatorPhpVersion): bool { private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=') return $this->hostPhpVersion->older($emulatorPhpVersion)
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<'); && $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion);
} }
private function sortPatches() private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool {
{ return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion)
&& $this->targetPhpVersion->older($emulatorPhpVersion);
}
private function sortPatches(): void {
// Patches may be contributed by different emulators. // Patches may be contributed by different emulators.
// Make sure they are sorted by increasing patch position. // Make sure they are sorted by increasing patch position.
usort($this->patches, function ($p1, $p2) { usort($this->patches, function ($p1, $p2) {
@ -127,67 +118,56 @@ class Emulative extends Lexer
}); });
} }
private function fixupTokens() /**
{ * @param list<Token> $tokens
* @return list<Token>
*/
private function fixupTokens(array $tokens): array {
if (\count($this->patches) === 0) { if (\count($this->patches) === 0) {
return; return $tokens;
} }
// Load first patch // Load first patch
$patchIdx = 0; $patchIdx = 0;
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// We use a manual loop over the tokens, because we modify the array on the fly // We use a manual loop over the tokens, because we modify the array on the fly
$pos = 0;
for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) {
$token = $this->tokens[$i];
if (\is_string($token)) {
if ($patchPos === $pos) {
// Only support replacement for string tokens.
assert($patchType === 'replace');
$this->tokens[$i] = $patchText;
// Fetch the next patch
$patchIdx++;
if ($patchIdx >= \count($this->patches)) {
// No more patches, we're done
return;
}
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
}
$pos += \strlen($token);
continue;
}
$len = \strlen($token[1]);
$posDelta = 0; $posDelta = 0;
$lineDelta = 0;
for ($i = 0, $c = \count($tokens); $i < $c; $i++) {
$token = $tokens[$i];
$pos = $token->pos;
$token->pos += $posDelta;
$token->line += $lineDelta;
$localPosDelta = 0;
$len = \strlen($token->text);
while ($patchPos >= $pos && $patchPos < $pos + $len) { while ($patchPos >= $pos && $patchPos < $pos + $len) {
$patchTextLen = \strlen($patchText); $patchTextLen = \strlen($patchText);
if ($patchType === 'remove') { if ($patchType === 'remove') {
if ($patchPos === $pos && $patchTextLen === $len) { if ($patchPos === $pos && $patchTextLen === $len) {
// Remove token entirely // Remove token entirely
array_splice($this->tokens, $i, 1, []); array_splice($tokens, $i, 1, []);
$i--; $i--;
$c--; $c--;
} else { } else {
// Remove from token string // Remove from token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], '', $patchPos - $pos + $posDelta, $patchTextLen $token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen
); );
$posDelta -= $patchTextLen; $localPosDelta -= $patchTextLen;
} }
$lineDelta -= \substr_count($patchText, "\n");
} elseif ($patchType === 'add') { } elseif ($patchType === 'add') {
// Insert into the token string // Insert into the token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], $patchText, $patchPos - $pos + $posDelta, 0 $token->text, $patchText, $patchPos - $pos + $localPosDelta, 0
); );
$posDelta += $patchTextLen; $localPosDelta += $patchTextLen;
$lineDelta += \substr_count($patchText, "\n");
} elseif ($patchType === 'replace') { } elseif ($patchType === 'replace') {
// Replace inside the token string // Replace inside the token string
$this->tokens[$i][1] = substr_replace( $token->text = substr_replace(
$token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen $token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen
); );
} else { } else {
assert(false); assert(false);
@ -196,22 +176,17 @@ class Emulative extends Lexer
// Fetch the next patch // Fetch the next patch
$patchIdx++; $patchIdx++;
if ($patchIdx >= \count($this->patches)) { if ($patchIdx >= \count($this->patches)) {
// No more patches, we're done // No more patches. However, we still need to adjust position.
return; $patchPos = \PHP_INT_MAX;
break;
} }
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
// Multiple patches may apply to the same token. Reload the current one to check
// If the new patch applies
$token = $this->tokens[$i];
} }
$pos += $len; $posDelta += $localPosDelta;
} }
return $tokens;
// A patch did not apply
assert(false);
} }
/** /**
@ -219,7 +194,7 @@ class Emulative extends Lexer
* *
* @param Error[] $errors * @param Error[] $errors
*/ */
private function fixupErrors(array $errors) { private function fixupErrors(array $errors): void {
foreach ($errors as $error) { foreach ($errors as $error) {
$attrs = $error->getAttributes(); $attrs = $error->getAttributes();

View file

@ -0,0 +1,93 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
use ncc\ThirdParty\nikic\PhpParser\Token;
final class AsymmetricVisibilityTokenEmulator extends TokenEmulator {
public function getPhpVersion(): PhpVersion {
return PhpVersion::fromComponents(8, 4);
}
public function isEmulationNeeded(string $code): bool {
$code = strtolower($code);
return strpos($code, 'public(set)') !== false ||
strpos($code, 'protected(set)') !== false ||
strpos($code, 'private(set)') !== false;
}
public function emulate(string $code, array $tokens): array {
$map = [
\T_PUBLIC => \T_PUBLIC_SET,
\T_PROTECTED => \T_PROTECTED_SET,
\T_PRIVATE => \T_PRIVATE_SET,
];
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if (isset($map[$token->id]) && $i + 3 < $c && $tokens[$i + 1]->text === '(' &&
$tokens[$i + 2]->id === \T_STRING && \strtolower($tokens[$i + 2]->text) === 'set' &&
$tokens[$i + 3]->text === ')' &&
$this->isKeywordContext($tokens, $i)
) {
array_splice($tokens, $i, 4, [
new Token(
$map[$token->id], $token->text . '(' . $tokens[$i + 2]->text . ')',
$token->line, $token->pos),
]);
$c -= 3;
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array {
$reverseMap = [
\T_PUBLIC_SET => \T_PUBLIC,
\T_PROTECTED_SET => \T_PROTECTED,
\T_PRIVATE_SET => \T_PRIVATE,
];
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
if (isset($reverseMap[$token->id]) &&
\preg_match('/(public|protected|private)\((set)\)/i', $token->text, $matches)
) {
[, $modifier, $set] = $matches;
$modifierLen = \strlen($modifier);
array_splice($tokens, $i, 1, [
new Token($reverseMap[$token->id], $modifier, $token->line, $token->pos),
new Token(\ord('('), '(', $token->line, $token->pos + $modifierLen),
new Token(\T_STRING, $set, $token->line, $token->pos + $modifierLen + 1),
new Token(\ord(')'), ')', $token->line, $token->pos + $modifierLen + 4),
]);
$i += 3;
$c += 3;
}
}
return $tokens;
}
/** @param Token[] $tokens */
protected function isKeywordContext(array $tokens, int $pos): bool {
$prevToken = $this->getPreviousNonSpaceToken($tokens, $pos);
if ($prevToken === null) {
return false;
}
return $prevToken->id !== \T_OBJECT_OPERATOR
&& $prevToken->id !== \T_NULLSAFE_OBJECT_OPERATOR;
}
/** @param Token[] $tokens */
private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token {
for ($i = $start - 1; $i >= 0; --$i) {
if ($tokens[$i]->id === T_WHITESPACE) {
continue;
}
return $tokens[$i];
}
return null;
}
}

View file

@ -2,43 +2,36 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative; use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
use ncc\ThirdParty\nikic\PhpParser\Token;
final class AttributeEmulator extends TokenEmulator final class AttributeEmulator extends TokenEmulator {
{ public function getPhpVersion(): PhpVersion {
public function getPhpVersion(): string return PhpVersion::fromComponents(8, 0);
{
return Emulative::PHP_8_0;
} }
public function isEmulationNeeded(string $code) : bool public function isEmulationNeeded(string $code): bool {
{
return strpos($code, '#[') !== false; return strpos($code, '#[') !== false;
} }
public function emulate(string $code, array $tokens): array public function emulate(string $code, array $tokens): array {
{
// We need to manually iterate and manage a count because we'll change // We need to manually iterate and manage a count because we'll change
// the tokens array on the way. // the tokens array on the way.
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) { for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') { $token = $tokens[$i];
if ($token->text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') {
array_splice($tokens, $i, 2, [ array_splice($tokens, $i, 2, [
[\T_ATTRIBUTE, '#[', $line] new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos),
]); ]);
$c--; $c--;
continue; continue;
} }
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
} }
return $tokens; return $tokens;
} }
public function reverseEmulate(string $code, array $tokens): array public function reverseEmulate(string $code, array $tokens): array {
{
// TODO // TODO
return $tokens; return $tokens;
} }

View file

@ -1,47 +0,0 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative;
final class CoaleseEqualTokenEmulator extends TokenEmulator
{
public function getPhpVersion(): string
{
return Emulative::PHP_7_4;
}
public function isEmulationNeeded(string $code): bool
{
return strpos($code, '??=') !== false;
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$line = 1;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if (isset($tokens[$i + 1])) {
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
array_splice($tokens, $i, 2, [
[\T_COALESCE_EQUAL, '??=', $line]
]);
$c--;
continue;
}
}
if (\is_array($tokens[$i])) {
$line += substr_count($tokens[$i][1], "\n");
}
}
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// ??= was not valid code previously, don't bother.
return $tokens;
}
}

View file

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

View file

@ -2,11 +2,12 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative; use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
use ncc\ThirdParty\nikic\PhpParser\Token;
class ExplicitOctalEmulator extends TokenEmulator { class ExplicitOctalEmulator extends TokenEmulator {
public function getPhpVersion(): string { public function getPhpVersion(): PhpVersion {
return Emulative::PHP_8_1; return PhpVersion::fromComponents(8, 1);
} }
public function isEmulationNeeded(string $code): bool { public function isEmulationNeeded(string $code): bool {
@ -15,13 +16,14 @@ class ExplicitOctalEmulator extends TokenEmulator {
public function emulate(string $code, array $tokens): array { public function emulate(string $code, array $tokens): array {
for ($i = 0, $c = count($tokens); $i < $c; ++$i) { for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
if ($tokens[$i][0] == \T_LNUMBER && $tokens[$i][1] === '0' && $token = $tokens[$i];
isset($tokens[$i + 1]) && $tokens[$i + 1][0] == \T_STRING && if ($token->id == \T_LNUMBER && $token->text === '0' &&
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1][1]) isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING &&
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text)
) { ) {
$tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1][1]); $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text);
array_splice($tokens, $i, 2, [ array_splice($tokens, $i, 2, [
[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]], new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos),
]); ]);
$c--; $c--;
} }
@ -29,8 +31,7 @@ class ExplicitOctalEmulator extends TokenEmulator {
return $tokens; return $tokens;
} }
private function resolveIntegerOrFloatToken(string $str): int private function resolveIntegerOrFloatToken(string $str): int {
{
$str = substr($str, 1); $str = substr($str, 1);
$str = str_replace('_', '', $str); $str = str_replace('_', '', $str);
$num = octdec($str); $num = octdec($str);

View file

@ -1,76 +0,0 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative;
final class FlexibleDocStringEmulator extends TokenEmulator
{
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
(?:.*\r?\n)*?
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
public function getPhpVersion(): string
{
return Emulative::PHP_7_3;
}
public function isEmulationNeeded(string $code) : bool
{
return strpos($code, '<<<') !== false;
}
public function emulate(string $code, array $tokens): array
{
// Handled by preprocessing + fixup.
return $tokens;
}
public function reverseEmulate(string $code, array $tokens): array
{
// Not supported.
return $tokens;
}
public function preprocessCode(string $code, array &$patches): string {
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
// No heredoc/nowdoc found
return $code;
}
// Keep track of how much we need to adjust string offsets due to the modifications we
// already made
$posDelta = 0;
foreach ($matches as $match) {
$indentation = $match['indentation'][0];
$indentationStart = $match['indentation'][1];
$separator = $match['separator'][0];
$separatorStart = $match['separator'][1];
if ($indentation === '' && $separator !== '') {
// Ordinary heredoc/nowdoc
continue;
}
if ($indentation !== '') {
// Remove indentation
$indentationLen = strlen($indentation);
$code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen);
$patches[] = [$indentationStart + $posDelta, 'add', $indentation];
$posDelta -= $indentationLen;
}
if ($separator === '') {
// Insert newline as separator
$code = substr_replace($code, "\n", $separatorStart + $posDelta, 0);
$patches[] = [$separatorStart + $posDelta, 'remove', "\n"];
$posDelta += 1;
}
}
return $code;
}
}

View file

@ -1,23 +0,0 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative;
final class FnTokenEmulator extends KeywordEmulator
{
public function getPhpVersion(): string
{
return Emulative::PHP_7_4;
}
public function getKeywordString(): string
{
return 'fn';
}
public function getKeywordToken(): int
{
return \T_FN;
}
}

View file

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

View file

@ -2,22 +2,18 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative; use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
final class MatchTokenEmulator extends KeywordEmulator final class MatchTokenEmulator extends KeywordEmulator {
{ public function getPhpVersion(): PhpVersion {
public function getPhpVersion(): string return PhpVersion::fromComponents(8, 0);
{
return Emulative::PHP_8_0;
} }
public function getKeywordString(): string public function getKeywordString(): string {
{
return 'match'; return 'match';
} }
public function getKeywordToken(): int public function getKeywordToken(): int {
{
return \T_MATCH; return \T_MATCH;
} }
} }

View file

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

View file

@ -1,105 +0,0 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative;
final class NumericLiteralSeparatorEmulator extends TokenEmulator
{
const BIN = '(?:0b[01]+(?:_[01]+)*)';
const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)';
const DEC = '(?:[0-9]+(?:_[0-9]+)*)';
const SIMPLE_FLOAT = '(?:' . self::DEC . '\.' . self::DEC . '?|\.' . self::DEC . ')';
const EXP = '(?:e[+-]?' . self::DEC . ')';
const FLOAT = '(?:' . self::SIMPLE_FLOAT . self::EXP . '?|' . self::DEC . self::EXP . ')';
const NUMBER = '~' . self::FLOAT . '|' . self::BIN . '|' . self::HEX . '|' . self::DEC . '~iA';
public function getPhpVersion(): string
{
return Emulative::PHP_7_4;
}
public function isEmulationNeeded(string $code) : bool
{
return preg_match('~[0-9]_[0-9]~', $code)
|| preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code);
}
public function emulate(string $code, array $tokens): array
{
// We need to manually iterate and manage a count because we'll change
// the tokens array on the way
$codeOffset = 0;
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
$token = $tokens[$i];
$tokenLen = \strlen(\is_array($token) ? $token[1] : $token);
if ($token[0] !== T_LNUMBER && $token[0] !== T_DNUMBER) {
$codeOffset += $tokenLen;
continue;
}
$res = preg_match(self::NUMBER, $code, $matches, 0, $codeOffset);
assert($res, "No number at number token position");
$match = $matches[0];
$matchLen = \strlen($match);
if ($matchLen === $tokenLen) {
// Original token already holds the full number.
$codeOffset += $tokenLen;
continue;
}
$tokenKind = $this->resolveIntegerOrFloatToken($match);
$newTokens = [[$tokenKind, $match, $token[2]]];
$numTokens = 1;
$len = $tokenLen;
while ($matchLen > $len) {
$nextToken = $tokens[$i + $numTokens];
$nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken;
$nextTokenLen = \strlen($nextTokenText);
$numTokens++;
if ($matchLen < $len + $nextTokenLen) {
// Split trailing characters into a partial token.
assert(is_array($nextToken), "Partial token should be an array token");
$partialText = substr($nextTokenText, $matchLen - $len);
$newTokens[] = [$nextToken[0], $partialText, $nextToken[2]];
break;
}
$len += $nextTokenLen;
}
array_splice($tokens, $i, $numTokens, $newTokens);
$c -= $numTokens - \count($newTokens);
$codeOffset += $matchLen;
}
return $tokens;
}
private function resolveIntegerOrFloatToken(string $str): int
{
$str = str_replace('_', '', $str);
if (stripos($str, '0b') === 0) {
$num = bindec($str);
} elseif (stripos($str, '0x') === 0) {
$num = hexdec($str);
} elseif (stripos($str, '0') === 0 && ctype_digit($str)) {
$num = octdec($str);
} else {
$num = +$str;
}
return is_float($num) ? T_DNUMBER : T_LNUMBER;
}
public function reverseEmulate(string $code, array $tokens): array
{
// Numeric separators were not legal code previously, don't bother.
return $tokens;
}
}

View file

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

View file

@ -2,7 +2,7 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\Lexer\Emulative; use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
/* /*
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with * In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
@ -20,8 +20,8 @@ class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
return \T_READONLY; return \T_READONLY;
} }
public function getPhpVersion(): string { public function getPhpVersion(): PhpVersion {
return Emulative::PHP_8_2; return PhpVersion::fromComponents(8, 2);
} }
public function reverseEmulate(string $code, array $tokens): array { public function reverseEmulate(string $code, array $tokens): array {

View file

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

View file

@ -2,19 +2,20 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
/** /**
* Reverses emulation direction of the inner emulator. * Reverses emulation direction of the inner emulator.
*/ */
final class ReverseEmulator extends TokenEmulator final class ReverseEmulator extends TokenEmulator {
{
/** @var TokenEmulator Inner emulator */ /** @var TokenEmulator Inner emulator */
private $emulator; private TokenEmulator $emulator;
public function __construct(TokenEmulator $emulator) { public function __construct(TokenEmulator $emulator) {
$this->emulator = $emulator; $this->emulator = $emulator;
} }
public function getPhpVersion(): string { public function getPhpVersion(): PhpVersion {
return $this->emulator->getPhpVersion(); return $this->emulator->getPhpVersion();
} }

View file

@ -2,23 +2,28 @@
namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator; namespace ncc\ThirdParty\nikic\PhpParser\Lexer\TokenEmulator;
use ncc\ThirdParty\nikic\PhpParser\PhpVersion;
use ncc\ThirdParty\nikic\PhpParser\Token;
/** @internal */ /** @internal */
abstract class TokenEmulator abstract class TokenEmulator {
{ abstract public function getPhpVersion(): PhpVersion;
abstract public function getPhpVersion(): string;
abstract public function isEmulationNeeded(string $code): bool; abstract public function isEmulationNeeded(string $code): bool;
/** /**
* @return array Modified Tokens * @param Token[] $tokens Original tokens
* @return Token[] Modified Tokens
*/ */
abstract public function emulate(string $code, array $tokens): array; abstract public function emulate(string $code, array $tokens): array;
/** /**
* @return array Modified Tokens * @param Token[] $tokens Original tokens
* @return Token[] Modified Tokens
*/ */
abstract public function reverseEmulate(string $code, array $tokens): array; abstract public function reverseEmulate(string $code, array $tokens): array;
/** @param array{int, string, string}[] $patches */
public function preprocessCode(string $code, array &$patches): string { public function preprocessCode(string $code, array &$patches): string {
return $code; return $code;
} }

View file

@ -0,0 +1,85 @@
<?php declare(strict_types=1);
namespace ncc\ThirdParty\nikic\PhpParser;
/**
* Modifiers used (as a bit mask) by various flags subnodes, for example on classes, functions,
* properties and constants.
*/
final class Modifiers {
public const PUBLIC = 1;
public const PROTECTED = 2;
public const PRIVATE = 4;
public const STATIC = 8;
public const ABSTRACT = 16;
public const FINAL = 32;
public const READONLY = 64;
public const PUBLIC_SET = 128;
public const PROTECTED_SET = 256;
public const PRIVATE_SET = 512;
public const VISIBILITY_MASK = self::PUBLIC | self::PROTECTED | self::PRIVATE;
public const VISIBILITY_SET_MASK = self::PUBLIC_SET | self::PROTECTED_SET | self::PRIVATE_SET;
private const TO_STRING_MAP = [
self::PUBLIC => 'public',
self::PROTECTED => 'protected',
self::PRIVATE => 'private',
self::STATIC => 'static',
self::ABSTRACT => 'abstract',
self::FINAL => 'final',
self::READONLY => 'readonly',
self::PUBLIC_SET => 'public(set)',
self::PROTECTED_SET => 'protected(set)',
self::PRIVATE_SET => 'private(set)',
];
public static function toString(int $modifier): string {
if (!isset(self::TO_STRING_MAP[$modifier])) {
throw new \InvalidArgumentException("Unknown modifier $modifier");
}
return self::TO_STRING_MAP[$modifier];
}
private static function isValidModifier(int $modifier): bool {
$isPow2 = ($modifier & ($modifier - 1)) == 0 && $modifier != 0;
return $isPow2 && $modifier <= self::PRIVATE_SET;
}
/**
* @internal
*/
public static function verifyClassModifier(int $a, int $b): void {
assert(self::isValidModifier($b));
if (($a & $b) != 0) {
throw new Error(
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
}
if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class');
}
}
/**
* @internal
*/
public static function verifyModifier(int $a, int $b): void {
assert(self::isValidModifier($b));
if (($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) ||
($a & Modifiers::VISIBILITY_SET_MASK && $b & Modifiers::VISIBILITY_SET_MASK)
) {
throw new Error('Multiple access type modifiers are not allowed');
}
if (($a & $b) != 0) {
throw new Error(
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
}
if ($a & 48 && $b & 48) {
throw new Error('Cannot use the final modifier on an abstract class member');
}
}
}

View file

@ -6,19 +6,18 @@ use ncc\ThirdParty\nikic\PhpParser\Node\Name;
use ncc\ThirdParty\nikic\PhpParser\Node\Name\FullyQualified; use ncc\ThirdParty\nikic\PhpParser\Node\Name\FullyQualified;
use ncc\ThirdParty\nikic\PhpParser\Node\Stmt; use ncc\ThirdParty\nikic\PhpParser\Node\Stmt;
class NameContext class NameContext {
{
/** @var null|Name Current namespace */ /** @var null|Name Current namespace */
protected $namespace; protected ?Name $namespace;
/** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */ /** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
protected $aliases = []; protected array $aliases = [];
/** @var Name[][] Same as $aliases but preserving original case */ /** @var Name[][] Same as $aliases but preserving original case */
protected $origAliases = []; protected array $origAliases = [];
/** @var ErrorHandler Error handler */ /** @var ErrorHandler Error handler */
protected $errorHandler; protected ErrorHandler $errorHandler;
/** /**
* Create a name context. * Create a name context.
@ -36,7 +35,7 @@ class NameContext
* *
* @param Name|null $namespace Null is the global namespace * @param Name|null $namespace Null is the global namespace
*/ */
public function startNamespace(Name $namespace = null) { public function startNamespace(?Name $namespace = null): void {
$this->namespace = $namespace; $this->namespace = $namespace;
$this->origAliases = $this->aliases = [ $this->origAliases = $this->aliases = [
Stmt\Use_::TYPE_NORMAL => [], Stmt\Use_::TYPE_NORMAL => [],
@ -50,10 +49,10 @@ class NameContext
* *
* @param Name $name Original name * @param Name $name Original name
* @param string $aliasName Aliased name * @param string $aliasName Aliased name
* @param int $type One of Stmt\Use_::TYPE_* * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
* @param array $errorAttrs Attributes to use to report an error * @param array<string, mixed> $errorAttrs Attributes to use to report an error
*/ */
public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) { public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void {
// Constant names are case sensitive, everything else case insensitive // Constant names are case sensitive, everything else case insensitive
if ($type === Stmt\Use_::TYPE_CONSTANT) { if ($type === Stmt\Use_::TYPE_CONSTANT) {
$aliasLookupName = $aliasName; $aliasLookupName = $aliasName;
@ -87,7 +86,7 @@ class NameContext
* *
* @return null|Name Namespace (or null if global namespace) * @return null|Name Namespace (or null if global namespace)
*/ */
public function getNamespace() { public function getNamespace(): ?Name {
return $this->namespace; return $this->namespace;
} }
@ -95,11 +94,11 @@ class NameContext
* Get resolved name. * Get resolved name.
* *
* @param Name $name Name to resolve * @param Name $name Name to resolve
* @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT}
* *
* @return null|Name Resolved name, or null if static resolution is not possible * @return null|Name Resolved name, or null if static resolution is not possible
*/ */
public function getResolvedName(Name $name, int $type) { public function getResolvedName(Name $name, int $type): ?Name {
// don't resolve special class names // don't resolve special class names
if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) {
if (!$name->isUnqualified()) { if (!$name->isUnqualified()) {
@ -150,7 +149,7 @@ class NameContext
* Get possible ways of writing a fully qualified name (e.g., by making use of aliases). * 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 string $name Fully-qualified name (without leading namespace separator)
* @param int $type One of Stmt\Use_::TYPE_* * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
* *
* @return Name[] Possible representations of the name * @return Name[] Possible representations of the name
*/ */
@ -186,7 +185,7 @@ class NameContext
// Check for relevant type-specific use statements // Check for relevant type-specific use statements
foreach ($this->origAliases[$type] as $alias => $orig) { foreach ($this->origAliases[$type] as $alias => $orig) {
if ($type === Stmt\Use_::TYPE_CONSTANT) { if ($type === Stmt\Use_::TYPE_CONSTANT) {
// Constants are are complicated-sensitive // Constants are complicated-sensitive
$normalizedOrig = $this->normalizeConstName($orig->toString()); $normalizedOrig = $this->normalizeConstName($orig->toString());
if ($normalizedOrig === $this->normalizeConstName($name)) { if ($normalizedOrig === $this->normalizeConstName($name)) {
$possibleNames[] = new Name($alias); $possibleNames[] = new Name($alias);
@ -206,7 +205,7 @@ class NameContext
* Get shortest representation of this fully-qualified name. * Get shortest representation of this fully-qualified name.
* *
* @param string $name Fully-qualified name (without leading namespace separator) * @param string $name Fully-qualified name (without leading namespace separator)
* @param int $type One of Stmt\Use_::TYPE_* * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
* *
* @return Name Shortest representation * @return Name Shortest representation
*/ */
@ -227,7 +226,7 @@ class NameContext
return $shortestName; return $shortestName;
} }
private function resolveAlias(Name $name, $type) { private function resolveAlias(Name $name, int $type): ?FullyQualified {
$firstPart = $name->getFirst(); $firstPart = $name->getFirst();
if ($name->isQualified()) { if ($name->isQualified()) {
@ -250,7 +249,7 @@ class NameContext
return null; return null;
} }
private function getNamespaceRelativeName(string $name, string $lcName, int $type) { private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name {
if (null === $this->namespace) { if (null === $this->namespace) {
return new Name($name); return new Name($name);
} }
@ -271,7 +270,7 @@ class NameContext
return null; return null;
} }
private function normalizeConstName(string $name) { private function normalizeConstName(string $name): string {
$nsSep = strrpos($name, '\\'); $nsSep = strrpos($name, '\\');
if (false === $nsSep) { if (false === $nsSep) {
return $name; return $name;

View file

@ -2,11 +2,11 @@
namespace ncc\ThirdParty\nikic\PhpParser; namespace ncc\ThirdParty\nikic\PhpParser;
interface Node interface Node {
{
/** /**
* Gets the type of the node. * Gets the type of the node.
* *
* @psalm-return non-empty-string
* @return string Type of the node * @return string Type of the node
*/ */
public function getType(): string; public function getType(): string;
@ -14,7 +14,7 @@ interface Node
/** /**
* Gets the names of the sub nodes. * Gets the names of the sub nodes.
* *
* @return array Names of sub nodes * @return string[] Names of sub nodes
*/ */
public function getSubNodeNames(): array; public function getSubNodeNames(): array;
@ -22,6 +22,9 @@ interface Node
* Gets line the node started in (alias of getStartLine). * Gets line the node started in (alias of getStartLine).
* *
* @return int Start line (or -1 if not available) * @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*
* @deprecated Use getStartLine() instead
*/ */
public function getLine(): int; public function getLine(): int;
@ -31,6 +34,7 @@ interface Node
* Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default).
* *
* @return int Start line (or -1 if not available) * @return int Start line (or -1 if not available)
* @phpstan-return -1|positive-int
*/ */
public function getStartLine(): int; public function getStartLine(): int;
@ -40,6 +44,7 @@ interface Node
* Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default).
* *
* @return int End line (or -1 if not available) * @return int End line (or -1 if not available)
* @phpstan-return -1|positive-int
*/ */
public function getEndLine(): int; public function getEndLine(): int;
@ -97,7 +102,7 @@ interface Node
* *
* @return null|Comment\Doc Doc comment object or null * @return null|Comment\Doc Doc comment object or null
*/ */
public function getDocComment(); public function getDocComment(): ?Comment\Doc;
/** /**
* Sets the doc comment of the node. * Sets the doc comment of the node.
@ -106,29 +111,23 @@ interface Node
* *
* @param Comment\Doc $docComment Doc comment to set * @param Comment\Doc $docComment Doc comment to set
*/ */
public function setDocComment(Comment\Doc $docComment); public function setDocComment(Comment\Doc $docComment): void;
/** /**
* Sets an attribute on a node. * Sets an attribute on a node.
* *
* @param string $key
* @param mixed $value * @param mixed $value
*/ */
public function setAttribute(string $key, $value); public function setAttribute(string $key, $value): void;
/** /**
* Returns whether an attribute exists. * Returns whether an attribute exists.
*
* @param string $key
*
* @return bool
*/ */
public function hasAttribute(string $key): bool; public function hasAttribute(string $key): bool;
/** /**
* Returns the value of an attribute. * Returns the value of an attribute.
* *
* @param string $key
* @param mixed $default * @param mixed $default
* *
* @return mixed * @return mixed
@ -138,14 +137,14 @@ interface Node
/** /**
* Returns all the attributes of this node. * Returns all the attributes of this node.
* *
* @return array * @return array<string, mixed>
*/ */
public function getAttributes(): array; public function getAttributes(): array;
/** /**
* Replaces all the attributes of this node. * Replaces all the attributes of this node.
* *
* @param array $attributes * @param array<string, mixed> $attributes
*/ */
public function setAttributes(array $attributes); public function setAttributes(array $attributes): void;
} }

View file

@ -2,19 +2,17 @@
namespace ncc\ThirdParty\nikic\PhpParser\Node; namespace ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\VariadicPlaceholder;
use ncc\ThirdParty\nikic\PhpParser\NodeAbstract; use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
class Arg extends NodeAbstract class Arg extends NodeAbstract {
{
/** @var Identifier|null Parameter name (for named parameters) */ /** @var Identifier|null Parameter name (for named parameters) */
public $name; public ?Identifier $name;
/** @var Expr Value to pass */ /** @var Expr Value to pass */
public $value; public Expr $value;
/** @var bool Whether to pass by ref */ /** @var bool Whether to pass by ref */
public $byRef; public bool $byRef;
/** @var bool Whether to unpack the argument */ /** @var bool Whether to unpack the argument */
public $unpack; public bool $unpack;
/** /**
* Constructs a function call argument node. * Constructs a function call argument node.
@ -22,12 +20,12 @@ class Arg extends NodeAbstract
* @param Expr $value Value to pass * @param Expr $value Value to pass
* @param bool $byRef Whether to pass by ref * @param bool $byRef Whether to pass by ref
* @param bool $unpack Whether to unpack the argument * @param bool $unpack Whether to unpack the argument
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
* @param Identifier|null $name Parameter name (for named parameters) * @param Identifier|null $name Parameter name (for named parameters)
*/ */
public function __construct( public function __construct(
Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [], Expr $value, bool $byRef = false, bool $unpack = false, array $attributes = [],
Identifier $name = null ?Identifier $name = null
) { ) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->name = $name; $this->name = $name;

View file

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

View file

@ -5,18 +5,17 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node; use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeAbstract; use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
class Attribute extends NodeAbstract class Attribute extends NodeAbstract {
{
/** @var Name Attribute name */ /** @var Name Attribute name */
public $name; public Name $name;
/** @var Arg[] Attribute arguments */ /** @var list<Arg> Attribute arguments */
public $args; public array $args;
/** /**
* @param Node\Name $name Attribute name * @param Node\Name $name Attribute name
* @param Arg[] $args Attribute arguments * @param list<Arg> $args Attribute arguments
* @param array $attributes Additional node attributes * @param array<string, mixed> $attributes Additional node attributes
*/ */
public function __construct(Name $name, array $args = [], array $attributes = []) { public function __construct(Name $name, array $args = [], array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

@ -2,17 +2,15 @@
namespace ncc\ThirdParty\nikic\PhpParser\Node; namespace ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeAbstract; use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
class AttributeGroup extends NodeAbstract class AttributeGroup extends NodeAbstract {
{
/** @var Attribute[] Attributes */ /** @var Attribute[] Attributes */
public $attrs; public array $attrs;
/** /**
* @param Attribute[] $attrs PHP attributes * @param Attribute[] $attrs PHP attributes
* @param array $attributes Additional node attributes * @param array<string, mixed> $attributes Additional node attributes
*/ */
public function __construct(array $attrs, array $attributes = []) { public function __construct(array $attrs, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

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

View file

@ -9,6 +9,5 @@ use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
* *
* It does not provide any shared behavior and exists only for type-checking purposes. * It does not provide any shared behavior and exists only for type-checking purposes.
*/ */
abstract class ComplexType extends NodeAbstract abstract class ComplexType extends NodeAbstract {
{
} }

View file

@ -4,22 +4,21 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeAbstract; use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
class Const_ extends NodeAbstract class Const_ extends NodeAbstract {
{
/** @var Identifier Name */ /** @var Identifier Name */
public $name; public Identifier $name;
/** @var Expr Value */ /** @var Expr Value */
public $value; public Expr $value;
/** @var Name|null Namespaced name (if using NameResolver) */ /** @var Name|null Namespaced name (if using NameResolver) */
public $namespacedName; public ?Name $namespacedName;
/** /**
* Constructs a const node for use in class const and const statements. * Constructs a const node for use in class const and const statements.
* *
* @param string|Identifier $name Name * @param string|Identifier $name Name
* @param Expr $value Value * @param Expr $value Value
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct($name, Expr $value, array $attributes = []) { public function __construct($name, Expr $value, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

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

View file

@ -4,6 +4,5 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\NodeAbstract; use ncc\ThirdParty\nikic\PhpParser\NodeAbstract;
abstract class Expr extends NodeAbstract abstract class Expr extends NodeAbstract {
{
} }

View file

@ -4,21 +4,20 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
class ArrayDimFetch extends Expr class ArrayDimFetch extends Expr {
{
/** @var Expr Variable */ /** @var Expr Variable */
public $var; public Expr $var;
/** @var null|Expr Array index / dim */ /** @var null|Expr Array index / dim */
public $dim; public ?Expr $dim;
/** /**
* Constructs an array index fetch node. * Constructs an array index fetch node.
* *
* @param Expr $var Variable * @param Expr $var Variable
* @param null|Expr $dim Array index / dim * @param null|Expr $dim Array index / dim
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $dim = null, array $attributes = []) { public function __construct(Expr $var, ?Expr $dim = null, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->var = $var; $this->var = $var;
$this->dim = $dim; $this->dim = $dim;

View file

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

View file

@ -2,22 +2,22 @@
namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr; namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\ArrayItem;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
class Array_ extends Expr class Array_ extends Expr {
{
// For use in "kind" attribute // For use in "kind" attribute
const KIND_LONG = 1; // array() syntax public const KIND_LONG = 1; // array() syntax
const KIND_SHORT = 2; // [] syntax public const KIND_SHORT = 2; // [] syntax
/** @var (ArrayItem|null)[] Items */ /** @var ArrayItem[] Items */
public $items; public array $items;
/** /**
* Constructs an array node. * Constructs an array node.
* *
* @param (ArrayItem|null)[] $items Items of the array * @param ArrayItem[] $items Items of the array
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(array $items = [], array $attributes = []) { public function __construct(array $items = [], array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

@ -6,42 +6,47 @@ use ncc\ThirdParty\nikic\PhpParser\Node;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\FunctionLike; use ncc\ThirdParty\nikic\PhpParser\Node\FunctionLike;
class ArrowFunction extends Expr implements FunctionLike class ArrowFunction extends Expr implements FunctionLike {
{ /** @var bool Whether the closure is static */
/** @var bool */ public bool $static;
public $static;
/** @var bool */ /** @var bool Whether to return by reference */
public $byRef; public bool $byRef;
/** @var Node\Param[] */ /** @var Node\Param[] */
public $params = []; public array $params = [];
/** @var null|Node\Identifier|Node\Name|Node\ComplexType */ /** @var null|Node\Identifier|Node\Name|Node\ComplexType */
public $returnType; public ?Node $returnType;
/** @var Expr */ /** @var Expr Expression body */
public $expr; public Expr $expr;
/** @var Node\AttributeGroup[] */ /** @var Node\AttributeGroup[] */
public $attrGroups; public array $attrGroups;
/** /**
* @param array $subNodes Array of the following optional subnodes: * @param array{
* expr: Expr,
* static?: bool,
* byRef?: bool,
* params?: Node\Param[],
* returnType?: null|Node\Identifier|Node\Name|Node\ComplexType,
* attrGroups?: Node\AttributeGroup[]
* } $subNodes Array of the following subnodes:
* 'expr' : Expression body
* 'static' => false : Whether the closure is static * 'static' => false : Whether the closure is static
* 'byRef' => false : Whether to return by reference * 'byRef' => false : Whether to return by reference
* 'params' => array() : Parameters * 'params' => array() : Parameters
* 'returnType' => null : Return type * 'returnType' => null : Return type
* 'expr' => Expr : Expression body
* 'attrGroups' => array() : PHP attribute groups * 'attrGroups' => array() : PHP attribute groups
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(array $subNodes = [], array $attributes = []) { public function __construct(array $subNodes, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->static = $subNodes['static'] ?? false; $this->static = $subNodes['static'] ?? false;
$this->byRef = $subNodes['byRef'] ?? false; $this->byRef = $subNodes['byRef'] ?? false;
$this->params = $subNodes['params'] ?? []; $this->params = $subNodes['params'] ?? [];
$returnType = $subNodes['returnType'] ?? null; $this->returnType = $subNodes['returnType'] ?? null;
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
$this->expr = $subNodes['expr']; $this->expr = $subNodes['expr'];
$this->attrGroups = $subNodes['attrGroups'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? [];
} }

View file

@ -4,19 +4,18 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
class Assign extends Expr class Assign extends Expr {
{
/** @var Expr Variable */ /** @var Expr Variable */
public $var; public Expr $var;
/** @var Expr Expression */ /** @var Expr Expression */
public $expr; public Expr $expr;
/** /**
* Constructs an assignment node. * Constructs an assignment node.
* *
* @param Expr $var Variable * @param Expr $var Variable
* @param Expr $expr Expression * @param Expr $expr Expression
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

@ -4,19 +4,18 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
abstract class AssignOp extends Expr abstract class AssignOp extends Expr {
{
/** @var Expr Variable */ /** @var Expr Variable */
public $var; public Expr $var;
/** @var Expr Expression */ /** @var Expr Expression */
public $expr; public Expr $expr;
/** /**
* Constructs a compound assignment operation node. * Constructs a compound assignment operation node.
* *
* @param Expr $var Variable * @param Expr $var Variable
* @param Expr $expr Expression * @param Expr $expr Expression
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class BitwiseAnd extends AssignOp class BitwiseAnd extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_BitwiseAnd'; return 'Expr_AssignOp_BitwiseAnd';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class BitwiseOr extends AssignOp class BitwiseOr extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_BitwiseOr'; return 'Expr_AssignOp_BitwiseOr';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class BitwiseXor extends AssignOp class BitwiseXor extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_BitwiseXor'; return 'Expr_AssignOp_BitwiseXor';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Coalesce extends AssignOp class Coalesce extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Coalesce'; return 'Expr_AssignOp_Coalesce';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Concat extends AssignOp class Concat extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Concat'; return 'Expr_AssignOp_Concat';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Div extends AssignOp class Div extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Div'; return 'Expr_AssignOp_Div';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Minus extends AssignOp class Minus extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Minus'; return 'Expr_AssignOp_Minus';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Mod extends AssignOp class Mod extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Mod'; return 'Expr_AssignOp_Mod';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Mul extends AssignOp class Mul extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Mul'; return 'Expr_AssignOp_Mul';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Plus extends AssignOp class Plus extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Plus'; return 'Expr_AssignOp_Plus';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class Pow extends AssignOp class Pow extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_Pow'; return 'Expr_AssignOp_Pow';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class ShiftLeft extends AssignOp class ShiftLeft extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_ShiftLeft'; return 'Expr_AssignOp_ShiftLeft';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\AssignOp;
class ShiftRight extends AssignOp class ShiftRight extends AssignOp {
{
public function getType(): string { public function getType(): string {
return 'Expr_AssignOp_ShiftRight'; return 'Expr_AssignOp_ShiftRight';
} }

View file

@ -4,19 +4,18 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
class AssignRef extends Expr class AssignRef extends Expr {
{
/** @var Expr Variable reference is assigned to */ /** @var Expr Variable reference is assigned to */
public $var; public Expr $var;
/** @var Expr Variable which is referenced */ /** @var Expr Variable which is referenced */
public $expr; public Expr $expr;
/** /**
* Constructs an assignment node. * Constructs an assignment node.
* *
* @param Expr $var Variable * @param Expr $var Variable
* @param Expr $expr Expression * @param Expr $expr Expression
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(Expr $var, Expr $expr, array $attributes = []) { public function __construct(Expr $var, Expr $expr, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;

View file

@ -4,19 +4,18 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr; use ncc\ThirdParty\nikic\PhpParser\Node\Expr;
abstract class BinaryOp extends Expr abstract class BinaryOp extends Expr {
{
/** @var Expr The left hand side expression */ /** @var Expr The left hand side expression */
public $left; public Expr $left;
/** @var Expr The right hand side expression */ /** @var Expr The right hand side expression */
public $right; public Expr $right;
/** /**
* Constructs a binary operator node. * Constructs a binary operator node.
* *
* @param Expr $left The left hand side expression * @param Expr $left The left hand side expression
* @param Expr $right The right hand side expression * @param Expr $right The right hand side expression
* @param array $attributes Additional attributes * @param array<string, mixed> $attributes Additional attributes
*/ */
public function __construct(Expr $left, Expr $right, array $attributes = []) { public function __construct(Expr $left, Expr $right, array $attributes = []) {
$this->attributes = $attributes; $this->attributes = $attributes;
@ -33,8 +32,6 @@ abstract class BinaryOp extends Expr
* *
* In the case there are multiple possible sigils for an operator, this method does not * In the case there are multiple possible sigils for an operator, this method does not
* necessarily return the one used in the parsed code. * necessarily return the one used in the parsed code.
*
* @return string
*/ */
abstract public function getOperatorSigil(): string; abstract public function getOperatorSigil(): string;
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class BitwiseAnd extends BinaryOp class BitwiseAnd extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '&'; return '&';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class BitwiseOr extends BinaryOp class BitwiseOr extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '|'; return '|';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class BitwiseXor extends BinaryOp class BitwiseXor extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '^'; return '^';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class BooleanAnd extends BinaryOp class BooleanAnd extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '&&'; return '&&';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class BooleanOr extends BinaryOp class BooleanOr extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '||'; return '||';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Coalesce extends BinaryOp class Coalesce extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '??'; return '??';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Concat extends BinaryOp class Concat extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '.'; return '.';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Div extends BinaryOp class Div extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '/'; return '/';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Equal extends BinaryOp class Equal extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '=='; return '==';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Greater extends BinaryOp class Greater extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '>'; return '>';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class GreaterOrEqual extends BinaryOp class GreaterOrEqual extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '>='; return '>=';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class Identical extends BinaryOp class Identical extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return '==='; return '===';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class LogicalAnd extends BinaryOp class LogicalAnd extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return 'and'; return 'and';
} }

View file

@ -4,8 +4,7 @@ namespace ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp; use ncc\ThirdParty\nikic\PhpParser\Node\Expr\BinaryOp;
class LogicalOr extends BinaryOp class LogicalOr extends BinaryOp {
{
public function getOperatorSigil(): string { public function getOperatorSigil(): string {
return 'or'; return 'or';
} }

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