composer update

This commit is contained in:
2019-12-01 06:37:45 +00:00
parent fa199eef05
commit 3115ab75a5
3650 changed files with 72361 additions and 147137 deletions

View File

@@ -12,7 +12,7 @@ class Param implements PhpParser\Builder
protected $default = null;
/** @var string|Node\Name|Node\NullableType|null */
/** @var Node\Identifier|Node\Name|Node\NullableType|null */
protected $type = null;
protected $byRef = false;

View File

@@ -37,10 +37,15 @@ class Lexer
// map from internal tokens to PhpParser tokens
$this->tokenMap = $this->createTokenMap();
// Compatibility define for PHP < 7.4
if (!defined('T_BAD_CHARACTER')) {
\define('T_BAD_CHARACTER', -1);
}
// 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], 1
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
);
$defaultAttributes = ['comments', 'startLine', 'endLine'];
@@ -92,13 +97,9 @@ class Lexer
}
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
$tokens = [];
for ($i = $start; $i < $end; $i++) {
$chr = $this->code[$i];
if ($chr === 'b' || $chr === 'B') {
// HHVM does not treat b" tokens correctly, so ignore these
continue;
}
if ($chr === "\0") {
// PHP cuts error message after null byte, so need special case
$errorMsg = 'Unexpected null byte';
@@ -108,6 +109,7 @@ class Lexer
);
}
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
$errorHandler->handleError(new Error($errorMsg, [
'startLine' => $line,
'endLine' => $line,
@@ -115,6 +117,7 @@ class Lexer
'endFilePos' => $i,
]));
}
return $tokens;
}
/**
@@ -140,6 +143,16 @@ class Lexer
return true;
}
if (PHP_VERSION_ID >= 80000) {
// PHP 8 converts the "bad character" case into a parse error, rather than treating
// it as a lexing warning. To preserve previous behavior, we need to assume that an
// error occurred.
// TODO: We should handle this the same way as PHP 8: Only generate T_BAD_CHARACTER
// token here (for older PHP versions) and leave generationg of the actual parse error
// to the parser. This will also save the full token scan on PHP 8 here.
return true;
}
return null !== error_get_last();
}
@@ -155,16 +168,29 @@ class Lexer
$filePos = 0;
$line = 1;
foreach ($this->tokens as $token) {
$numTokens = \count($this->tokens);
for ($i = 0; $i < $numTokens; $i++) {
$token = $this->tokens[$i];
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
// In this case we only need to emit an error.
if ($token[0] === \T_BAD_CHARACTER) {
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
}
$tokenValue = \is_string($token) ? $token : $token[1];
$tokenLen = \strlen($tokenValue);
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
// Something is missing, must be an invalid character
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
$this->handleInvalidCharacterRange(
$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;
@@ -187,8 +213,9 @@ class Lexer
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
} else {
// Invalid characters at the end of the input
$this->handleInvalidCharacterRange(
$badCharTokens = $this->handleInvalidCharacterRange(
$filePos, \strlen($this->code), $line, $errorHandler);
$this->tokens = array_merge($this->tokens, $badCharTokens);
}
return;
}

View File

@@ -7,17 +7,22 @@ use PhpParser\ErrorHandler;
use PhpParser\Lexer;
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface;
use PhpParser\Parser\Tokens;
class Emulative extends Lexer
{
const PHP_7_3 = '7.3.0dev';
const PHP_7_4 = '7.4.0dev';
const T_COALESCE_EQUAL = 1007;
const T_FN = 1008;
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-Z_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
REGEX;
/** @var mixed[] Patches used to reverse changes introduced in the code */
@@ -33,14 +38,12 @@ REGEX;
{
parent::__construct($options);
// prepare token emulators
$this->tokenEmulators[] = new FnTokenEmulator();
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator();
$this->tokenEmulators[] = new NumericLiteralSeparatorEmulator();
// add emulated tokens here
foreach ($this->tokenEmulators as $emulativeToken) {
$this->tokenMap[$emulativeToken->getTokenId()] = $emulativeToken->getParserTokenId();
}
$this->tokenMap[self::T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
$this->tokenMap[self::T_FN] = Tokens::T_FN;
}
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
@@ -57,14 +60,6 @@ REGEX;
// 1. emulation of heredoc and nowdoc new syntax
$preparedCode = $this->processHeredocNowdoc($code);
parent::startLexing($preparedCode, $collector);
// add token emulation
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
$this->tokens = $emulativeToken->emulate($code, $this->tokens);
}
}
$this->fixupTokens();
$errors = $collector->getErrors();
@@ -74,6 +69,13 @@ REGEX;
$errorHandler->handleError($error);
}
}
// add token emulation
foreach ($this->tokenEmulators as $emulativeToken) {
if ($emulativeToken->isEmulationNeeded($code)) {
$this->tokens = $emulativeToken->emulate($code, $this->tokens);
}
}
}
private function isHeredocNowdocEmulationNeeded(string $code): bool

View File

@@ -3,22 +3,9 @@
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
use PhpParser\Parser\Tokens;
final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface
{
const T_COALESCE_EQUAL = 1007;
public function getTokenId(): int
{
return self::T_COALESCE_EQUAL;
}
public function getParserTokenId(): int
{
return Tokens::T_COALESCE_EQUAL;
}
public function isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
@@ -38,7 +25,7 @@ final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface
if (isset($tokens[$i + 1])) {
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
array_splice($tokens, $i, 2, [
[self::T_COALESCE_EQUAL, '??=', $line]
[Emulative::T_COALESCE_EQUAL, '??=', $line]
]);
$c--;
continue;

View File

@@ -3,22 +3,9 @@
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
use PhpParser\Parser\Tokens;
final class FnTokenEmulator implements TokenEmulatorInterface
{
const T_FN = 1008;
public function getTokenId(): int
{
return self::T_FN;
}
public function getParserTokenId(): int
{
return Tokens::T_FN;
}
public function isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
@@ -40,7 +27,7 @@ final class FnTokenEmulator implements TokenEmulatorInterface
continue;
}
$tokens[$i][0] = self::T_FN;
$tokens[$i][0] = Emulative::T_FN;
}
}

View File

@@ -0,0 +1,98 @@
<?php declare(strict_types=1);
namespace PhpParser\Lexer\TokenEmulator;
use PhpParser\Lexer\Emulative;
final class NumericLiteralSeparatorEmulator implements TokenEmulatorInterface
{
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 isEmulationNeeded(string $code) : bool
{
// skip version where this is supported
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
return false;
}
return preg_match('~[0-9a-f]_[0-9a-f]~i', $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
$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;
}
}

View File

@@ -2,12 +2,9 @@
namespace PhpParser\Lexer\TokenEmulator;
/** @internal */
interface TokenEmulatorInterface
{
public function getTokenId(): int;
public function getParserTokenId(): int;
public function isEmulationNeeded(string $code): bool;
/**

View File

@@ -17,7 +17,7 @@ class ArrowFunction extends Expr implements FunctionLike
/** @var Node\Param[] */
public $params = [];
/** @var null|Node\Identifier|Node\Name|Node\NullableType */
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType */
public $returnType;
/** @var Expr */

View File

@@ -16,7 +16,7 @@ class Closure extends Expr implements FunctionLike
public $params;
/** @var ClosureUse[] use()s */
public $uses;
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
public $returnType;
/** @var Node\Stmt[] Statements */
public $stmts;

View File

@@ -23,7 +23,7 @@ interface FunctionLike extends Node
/**
* Get the declared return type or null
*
* @return null|Identifier|Node\Name|Node\NullableType
* @return null|Identifier|Node\Name|Node\NullableType|Node\UnionType
*/
public function getReturnType();

View File

@@ -6,7 +6,7 @@ use PhpParser\NodeAbstract;
class Param extends NodeAbstract
{
/** @var null|Identifier|Name|NullableType Type declaration */
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
public $type;
/** @var bool Whether parameter is passed by reference */
public $byRef;
@@ -20,12 +20,12 @@ class Param extends NodeAbstract
/**
* Constructs a parameter node.
*
* @param Expr\Variable|Expr\Error $var Parameter variable
* @param null|Expr $default Default value
* @param null|string|Identifier|Name|NullableType $type Type declaration
* @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument
* @param array $attributes Additional attributes
* @param Expr\Variable|Expr\Error $var Parameter variable
* @param null|Expr $default Default value
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
* @param bool $byRef Whether is passed by reference
* @param bool $variadic Whether this is a variadic argument
* @param array $attributes Additional attributes
*/
public function __construct(
$var, Expr $default = null, $type = null,

View File

@@ -34,6 +34,8 @@ class DNumber extends Scalar
* @return float The parsed number
*/
public static function parse(string $str) : float {
$str = str_replace('_', '', $str);
// if string contains any of .eE just cast it to float
if (false !== strpbrk($str, '.eE')) {
return (float) $str;

View File

@@ -41,6 +41,8 @@ class LNumber extends Scalar
* @return LNumber The constructed LNumber, including kind attribute
*/
public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber {
$str = str_replace('_', '', $str);
if ('0' !== $str[0] || '0' === $str) {
$attributes['kind'] = LNumber::KIND_DEC;
return new LNumber((int) $str, $attributes);

View File

@@ -100,7 +100,7 @@ class String_ extends Scalar
if (isset(self::$replacements[$str])) {
return self::$replacements[$str];
} elseif ('x' === $str[0] || 'X' === $str[0]) {
return chr(hexdec($str));
return chr(hexdec(substr($str, 1)));
} elseif ('u' === $str[0]) {
return self::codePointToUtf8(hexdec($matches[2]));
} else {
@@ -134,7 +134,7 @@ class String_ extends Scalar
}
throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large');
}
public function getType() : string {
return 'Scalar_String';
}

View File

@@ -14,6 +14,45 @@ abstract class ClassLike extends Node\Stmt
/** @var Node\Stmt[] Statements */
public $stmts;
/**
* @return TraitUse[]
*/
public function getTraitUses() : array {
$traitUses = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof TraitUse) {
$traitUses[] = $stmt;
}
}
return $traitUses;
}
/**
* @return ClassConst[]
*/
public function getConstants() : array {
$constants = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof ClassConst) {
$constants[] = $stmt;
}
}
return $constants;
}
/**
* @return Property[]
*/
public function getProperties() : array {
$properties = [];
foreach ($this->stmts as $stmt) {
if ($stmt instanceof Property) {
$properties[] = $stmt;
}
}
return $properties;
}
/**
* Gets all methods defined directly in this class/interface/trait
*

View File

@@ -15,7 +15,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
public $name;
/** @var Node\Param[] Parameters */
public $params;
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
public $returnType;
/** @var Node\Stmt[]|null Statements */
public $stmts;

View File

@@ -98,7 +98,7 @@ class Class_ extends ClassLike
throw new Error('Cannot use the final modifier on an abstract class member');
}
}
public function getType() : string {
return 'Stmt_Class';
}

View File

@@ -16,7 +16,7 @@ class Function_ extends Node\Stmt implements FunctionLike
public $name;
/** @var Node\Param[] Parameters */
public $params;
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
public $returnType;
/** @var Node\Stmt[] Statements */
public $stmts;

View File

@@ -28,7 +28,7 @@ class Interface_ extends ClassLike
public function getSubNodeNames() : array {
return ['name', 'extends', 'stmts'];
}
public function getType() : string {
return 'Stmt_Interface';
}

View File

@@ -6,6 +6,7 @@ use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\UnionType;
class Property extends Node\Stmt
{
@@ -13,16 +14,16 @@ class Property extends Node\Stmt
public $flags;
/** @var PropertyProperty[] Properties */
public $props;
/** @var null|Identifier|Name|NullableType Type declaration */
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
public $type;
/**
* Constructs a class property list node.
*
* @param int $flags Modifiers
* @param PropertyProperty[] $props Properties
* @param array $attributes Additional attributes
* @param null|string|Identifier|Name|NullableType $type Type declaration
* @param int $flags Modifiers
* @param PropertyProperty[] $props Properties
* @param array $attributes Additional attributes
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
*/
public function __construct(int $flags, array $props, array $attributes = [], $type = null) {
$this->attributes = $attributes;

View File

@@ -23,7 +23,7 @@ class Trait_ extends ClassLike
public function getSubNodeNames() : array {
return ['name', 'stmts'];
}
public function getType() : string {
return 'Stmt_Trait';
}

View File

@@ -18,7 +18,7 @@ class TryCatch extends Node\Stmt
*
* @param Node\Stmt[] $stmts Statements
* @param Catch_[] $catches Catches
* @param null|Finally_ $finally Optionaly finally node
* @param null|Finally_ $finally Optional finally node
* @param array $attributes Additional attributes
*/
public function __construct(array $stmts, array $catches, Finally_ $finally = null, array $attributes = []) {
@@ -31,7 +31,7 @@ class TryCatch extends Node\Stmt
public function getSubNodeNames() : array {
return ['stmts', 'catches', 'finally'];
}
public function getType() : string {
return 'Stmt_TryCatch';
}

View File

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

View File

@@ -91,6 +91,7 @@ class NameResolver extends NodeVisitorAbstract
$this->resolveSignature($node);
} elseif ($node instanceof Stmt\ClassMethod
|| $node instanceof Expr\Closure
|| $node instanceof Expr\ArrowFunction
) {
$this->resolveSignature($node);
} elseif ($node instanceof Stmt\Property) {
@@ -161,12 +162,18 @@ class NameResolver extends NodeVisitorAbstract
}
private function resolveType($node) {
if ($node instanceof Name) {
return $this->resolveClassName($node);
}
if ($node instanceof Node\NullableType) {
$node->type = $this->resolveType($node->type);
return $node;
}
if ($node instanceof Name) {
return $this->resolveClassName($node);
if ($node instanceof Node\UnionType) {
foreach ($node->types as &$type) {
$type = $this->resolveType($type);
}
return $node;
}
return $node;
}

File diff suppressed because it is too large Load Diff

View File

@@ -656,6 +656,8 @@ abstract class ParserAbstract implements Parser
'iterable' => true,
'void' => true,
'object' => true,
'null' => true,
'false' => true,
];
if (!$name->isUnqualified()) {

View File

@@ -37,6 +37,10 @@ class Standard extends PrettyPrinterAbstract
return '?' . $this->p($node->type);
}
protected function pUnionType(Node\UnionType $node) {
return $this->pImplode($node->types, '|');
}
protected function pIdentifier(Node\Identifier $node) {
return $node->name;
}
@@ -159,8 +163,13 @@ class Standard extends PrettyPrinterAbstract
return (string) $node->value;
}
$sign = $node->value < 0 ? '-' : '';
$str = (string) $node->value;
if ($node->value < 0) {
$sign = '-';
$str = (string) -$node->value;
} else {
$sign = '';
$str = (string) $node->value;
}
switch ($kind) {
case Scalar\LNumber::KIND_BIN:
return $sign . '0b' . base_convert($str, 10, 2);

View File

@@ -1295,6 +1295,7 @@ abstract class PrettyPrinterAbstract
//'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
//'Scalar_Encapsed->parts' => '',
'Stmt_Catch->types' => '|',
'UnionType->types' => '|',
'Stmt_If->elseifs' => ' ',
'Stmt_TryCatch->catches' => ' ',
@@ -1396,6 +1397,7 @@ abstract class PrettyPrinterAbstract
* Stmt_TraitUseAdaptation_Precedence->insteadof
* Stmt_Unset->vars
* Stmt_Use->uses
* UnionType->types
*/
/* TODO