修复登陆日志入库错误,升级部分composer依赖项;
临时修复登陆通知邮件不能多发的问题;
This commit is contained in:
@@ -28,7 +28,7 @@ class ClassConst implements PhpParser\Builder {
|
||||
* Creates a class constant builder
|
||||
*
|
||||
* @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) {
|
||||
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
|
||||
@@ -38,7 +38,7 @@ class ClassConst implements PhpParser\Builder {
|
||||
* Add another constant to const group
|
||||
*
|
||||
* @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)
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,6 @@ use PhpParser\Node\Stmt;
|
||||
class EnumCase implements PhpParser\Builder {
|
||||
/** @var Identifier|string */
|
||||
protected $name;
|
||||
/** @var ?Node\Expr */
|
||||
protected ?Node\Expr $value = null;
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attributes = [];
|
||||
|
||||
@@ -122,6 +122,28 @@ class Param implements PhpParser\Builder {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
|
||||
+63
-1
@@ -23,6 +23,8 @@ class Property implements PhpParser\Builder {
|
||||
protected ?Node $type = null;
|
||||
/** @var list<Node\AttributeGroup> */
|
||||
protected array $attributeGroups = [];
|
||||
/** @var list<Node\PropertyHook> */
|
||||
protected array $hooks = [];
|
||||
|
||||
/**
|
||||
* Creates a property builder.
|
||||
@@ -88,6 +90,50 @@ class Property implements PhpParser\Builder {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default value for the property.
|
||||
*
|
||||
@@ -142,12 +188,27 @@ class Property implements PhpParser\Builder {
|
||||
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.
|
||||
*
|
||||
* @return Stmt\Property The built property 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(
|
||||
$this->flags !== 0 ? $this->flags : Modifiers::PUBLIC,
|
||||
[
|
||||
@@ -155,7 +216,8 @@ class Property implements PhpParser\Builder {
|
||||
],
|
||||
$this->attributes,
|
||||
$this->type,
|
||||
$this->attributeGroups
|
||||
$this->attributeGroups,
|
||||
$this->hooks
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ class BuilderFactory {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function val($value): Expr {
|
||||
return BuilderHelpers::normalizeValue($value);
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpParser\Node\ComplexType;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
@@ -214,7 +215,7 @@ final class BuilderHelpers {
|
||||
* Normalizes a value: Converts nulls, booleans, integers,
|
||||
* floats, strings and arrays into their respective nodes
|
||||
*
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value The value to normalize
|
||||
* @param Node\Expr|bool|null|int|float|string|array|\UnitEnum $value The value to normalize
|
||||
*
|
||||
* @return Expr The normalized value
|
||||
*/
|
||||
@@ -268,6 +269,10 @@ final class BuilderHelpers {
|
||||
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');
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ class TokenStream {
|
||||
*
|
||||
* @param Token[] $tokens Tokens in PhpToken::tokenize() format
|
||||
*/
|
||||
public function __construct(array $tokens) {
|
||||
public function __construct(array $tokens, int $tabWidth) {
|
||||
$this->tokens = $tokens;
|
||||
$this->indentMap = $this->calcIndentMap();
|
||||
$this->indentMap = $this->calcIndentMap($tabWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +248,7 @@ class TokenStream {
|
||||
*
|
||||
* @return int[] Token position to indentation map
|
||||
*/
|
||||
private function calcIndentMap(): array {
|
||||
private function calcIndentMap(int $tabWidth): array {
|
||||
$indentMap = [];
|
||||
$indent = 0;
|
||||
foreach ($this->tokens as $i => $token) {
|
||||
@@ -258,11 +258,11 @@ class TokenStream {
|
||||
$content = $token->text;
|
||||
$newlinePos = \strrpos($content, "\n");
|
||||
if (false !== $newlinePos) {
|
||||
$indent = \strlen($content) - $newlinePos - 1;
|
||||
$indent = $this->getIndent(\substr($content, $newlinePos + 1), $tabWidth);
|
||||
} 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);
|
||||
$indent = $this->getIndent($content, $tabWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,4 +272,11 @@ class TokenStream {
|
||||
|
||||
return $indentMap;
|
||||
}
|
||||
|
||||
private function getIndent(string $ws, int $tabWidth): int {
|
||||
$spaces = \substr_count($ws, " ");
|
||||
$tabs = \substr_count($ws, "\t");
|
||||
assert(\strlen($ws) === $spaces + $tabs);
|
||||
return $spaces + $tabs * $tabWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,13 @@ namespace PhpParser\Lexer;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Lexer\TokenEmulator\AsymmetricVisibilityTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\PropertyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||
@@ -47,6 +45,8 @@ class Emulative extends Lexer {
|
||||
new ReadonlyTokenEmulator(),
|
||||
new ExplicitOctalEmulator(),
|
||||
new ReadonlyFunctionTokenEmulator(),
|
||||
new PropertyTokenEmulator(),
|
||||
new AsymmetricVisibilityTokenEmulator(),
|
||||
];
|
||||
|
||||
// Collect emulators that are relevant for the PHP version we're running
|
||||
|
||||
+6
-2
@@ -14,8 +14,12 @@ abstract class KeywordEmulator extends TokenEmulator {
|
||||
|
||||
/** @param Token[] $tokens */
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool {
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
return $previousNonSpaceToken === null || $previousNonSpaceToken->id !== \T_OBJECT_OPERATOR;
|
||||
$prevToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
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 {
|
||||
|
||||
+42
-26
@@ -14,23 +14,47 @@ final class Modifiers {
|
||||
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 = 1 | 2 | 4;
|
||||
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 {
|
||||
if ($a & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
|
||||
throw new Error('Multiple abstract modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
|
||||
throw new Error('Multiple final modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
|
||||
throw new Error('Multiple readonly modifiers are not allowed');
|
||||
assert(self::isValidModifier($b));
|
||||
if (($a & $b) != 0) {
|
||||
throw new Error(
|
||||
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & 48 && $b & 48) {
|
||||
@@ -42,24 +66,16 @@ final class Modifiers {
|
||||
* @internal
|
||||
*/
|
||||
public static function verifyModifier(int $a, int $b): void {
|
||||
if ($a & Modifiers::VISIBILITY_MASK && $b & Modifiers::VISIBILITY_MASK) {
|
||||
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 & Modifiers::ABSTRACT && $b & Modifiers::ABSTRACT) {
|
||||
throw new Error('Multiple abstract modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::STATIC && $b & Modifiers::STATIC) {
|
||||
throw new Error('Multiple static modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::FINAL && $b & Modifiers::FINAL) {
|
||||
throw new Error('Multiple final modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & Modifiers::READONLY && $b & Modifiers::READONLY) {
|
||||
throw new Error('Multiple readonly modifiers are not allowed');
|
||||
if (($a & $b) != 0) {
|
||||
throw new Error(
|
||||
'Multiple ' . self::toString($b) . ' modifiers are not allowed');
|
||||
}
|
||||
|
||||
if ($a & 48 && $b & 48) {
|
||||
|
||||
+1
-1
@@ -185,7 +185,7 @@ class NameContext {
|
||||
// Check for relevant type-specific use statements
|
||||
foreach ($this->origAliases[$type] as $alias => $orig) {
|
||||
if ($type === Stmt\Use_::TYPE_CONSTANT) {
|
||||
// Constants are are complicated-sensitive
|
||||
// Constants are complicated-sensitive
|
||||
$normalizedOrig = $this->normalizeConstName($orig->toString());
|
||||
if ($normalizedOrig === $this->normalizeConstName($name)) {
|
||||
$possibleNames[] = new Name($alias);
|
||||
|
||||
+28
-2
@@ -21,6 +21,8 @@ class Param extends NodeAbstract {
|
||||
public int $flags;
|
||||
/** @var AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
/** @var PropertyHook[] Property hooks for promoted properties */
|
||||
public array $hooks;
|
||||
|
||||
/**
|
||||
* Constructs a parameter node.
|
||||
@@ -33,13 +35,15 @@ class Param extends NodeAbstract {
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
* @param int $flags Optional visibility flags
|
||||
* @param list<AttributeGroup> $attrGroups PHP attribute groups
|
||||
* @param PropertyHook[] $hooks Property hooks for promoted properties
|
||||
*/
|
||||
public function __construct(
|
||||
Expr $var, ?Expr $default = null, ?Node $type = null,
|
||||
bool $byRef = false, bool $variadic = false,
|
||||
array $attributes = [],
|
||||
int $flags = 0,
|
||||
array $attrGroups = []
|
||||
array $attrGroups = [],
|
||||
array $hooks = []
|
||||
) {
|
||||
$this->attributes = $attributes;
|
||||
$this->type = $type;
|
||||
@@ -49,10 +53,11 @@ class Param extends NodeAbstract {
|
||||
$this->default = $default;
|
||||
$this->flags = $flags;
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->hooks = $hooks;
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default'];
|
||||
return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default', 'hooks'];
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
@@ -81,4 +86,25 @@ class Param extends NodeAbstract {
|
||||
public function isReadonly(): bool {
|
||||
return (bool) ($this->flags & Modifiers::READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit public(set) visibility.
|
||||
*/
|
||||
public function isPublicSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit protected(set) visibility.
|
||||
*/
|
||||
public function isProtectedSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the promoted property has explicit private(set) visibility.
|
||||
*/
|
||||
public function isPrivateSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Scalar\MagicConst;
|
||||
|
||||
use PhpParser\Node\Scalar\MagicConst;
|
||||
|
||||
class Property extends MagicConst {
|
||||
public function getName(): string {
|
||||
return '__PROPERTY__';
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return 'Scalar_MagicConst_Property';
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ class Property extends Node\Stmt {
|
||||
public ?Node $type;
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public array $attrGroups;
|
||||
/** @var Node\PropertyHook[] Property hooks */
|
||||
public array $hooks;
|
||||
|
||||
/**
|
||||
* Constructs a class property list node.
|
||||
@@ -27,17 +29,19 @@ class Property extends Node\Stmt {
|
||||
* @param array<string, mixed> $attributes Additional attributes
|
||||
* @param null|Identifier|Name|ComplexType $type Type declaration
|
||||
* @param Node\AttributeGroup[] $attrGroups PHP attribute groups
|
||||
* @param Node\PropertyHook[] $hooks Property hooks
|
||||
*/
|
||||
public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = []) {
|
||||
public function __construct(int $flags, array $props, array $attributes = [], ?Node $type = null, array $attrGroups = [], array $hooks = []) {
|
||||
$this->attributes = $attributes;
|
||||
$this->flags = $flags;
|
||||
$this->props = $props;
|
||||
$this->type = $type;
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->hooks = $hooks;
|
||||
}
|
||||
|
||||
public function getSubNodeNames(): array {
|
||||
return ['attrGroups', 'flags', 'type', 'props'];
|
||||
return ['attrGroups', 'flags', 'type', 'props', 'hooks'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +80,27 @@ class Property extends Node\Stmt {
|
||||
return (bool) ($this->flags & Modifiers::READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit public(set) visibility.
|
||||
*/
|
||||
public function isPublicSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PUBLIC_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit protected(set) visibility.
|
||||
*/
|
||||
public function isProtectedSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PROTECTED_SET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the property has explicit private(set) visibility.
|
||||
*/
|
||||
public function isPrivateSet(): bool {
|
||||
return (bool) ($this->flags & Modifiers::PRIVATE_SET);
|
||||
}
|
||||
|
||||
public function getType(): string {
|
||||
return 'Stmt_Property';
|
||||
}
|
||||
|
||||
@@ -185,6 +185,15 @@ class NodeDumper {
|
||||
if ($flags & Modifiers::READONLY) {
|
||||
$strs[] = 'READONLY';
|
||||
}
|
||||
if ($flags & Modifiers::PUBLIC_SET) {
|
||||
$strs[] = 'PUBLIC_SET';
|
||||
}
|
||||
if ($flags & Modifiers::PROTECTED_SET) {
|
||||
$strs[] = 'PROTECTED_SET';
|
||||
}
|
||||
if ($flags & Modifiers::PRIVATE_SET) {
|
||||
$strs[] = 'PRIVATE_SET';
|
||||
}
|
||||
|
||||
if ($strs) {
|
||||
return implode(' | ', $strs) . ' (' . $flags . ')';
|
||||
|
||||
@@ -110,6 +110,12 @@ class NameResolver extends NodeVisitorAbstract {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Node\PropertyHook) {
|
||||
foreach ($node->params as $param) {
|
||||
$param->type = $this->resolveType($param->type);
|
||||
$this->resolveAttrGroups($param);
|
||||
}
|
||||
$this->resolveAttrGroups($node);
|
||||
} elseif ($node instanceof Stmt\Const_) {
|
||||
foreach ($node->consts as $const) {
|
||||
$this->addNamespacedName($const);
|
||||
|
||||
+1280
-1242
File diff suppressed because it is too large
Load Diff
+1269
-1215
File diff suppressed because it is too large
Load Diff
+83
-58
@@ -7,6 +7,7 @@ namespace PhpParser;
|
||||
* turn is based on work by Masato Bito.
|
||||
*/
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\Cast\Double;
|
||||
@@ -14,6 +15,7 @@ use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\InterpolatedStringPart;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\PropertyHook;
|
||||
use PhpParser\Node\Scalar\InterpolatedString;
|
||||
use PhpParser\Node\Scalar\Int_;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
@@ -1137,32 +1139,12 @@ abstract class ParserAbstract implements Parser {
|
||||
}
|
||||
|
||||
protected function checkClassConst(ClassConst $node, int $modifierPos): void {
|
||||
if ($node->flags & Modifiers::STATIC) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'static' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
if ($node->flags & Modifiers::ABSTRACT) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'abstract' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
if ($node->flags & Modifiers::READONLY) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use 'readonly' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkProperty(Property $node, int $modifierPos): void {
|
||||
if ($node->flags & Modifiers::ABSTRACT) {
|
||||
$this->emitError(new Error('Properties cannot be declared abstract',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
|
||||
if ($node->flags & Modifiers::FINAL) {
|
||||
$this->emitError(new Error('Properties cannot be declared final',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
foreach ([Modifiers::STATIC, Modifiers::ABSTRACT, Modifiers::READONLY] as $modifier) {
|
||||
if ($node->flags & $modifier) {
|
||||
$this->emitError(new Error(
|
||||
"Cannot use '" . Modifiers::toString($modifier) . "' as constant modifier",
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,6 +1160,68 @@ abstract class ParserAbstract implements Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/** @param PropertyHook[] $hooks */
|
||||
protected function checkPropertyHookList(array $hooks, int $hookPos): void {
|
||||
if (empty($hooks)) {
|
||||
$this->emitError(new Error(
|
||||
'Property hook list cannot be empty', $this->getAttributesAt($hookPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkPropertyHook(PropertyHook $hook, ?int $paramListPos): void {
|
||||
$name = $hook->name->toLowerString();
|
||||
if ($name !== 'get' && $name !== 'set') {
|
||||
$this->emitError(new Error(
|
||||
'Unknown hook "' . $hook->name . '", expected "get" or "set"',
|
||||
$hook->name->getAttributes()));
|
||||
}
|
||||
if ($name === 'get' && $paramListPos !== null) {
|
||||
$this->emitError(new Error(
|
||||
'get hook must not have a parameter list', $this->getAttributesAt($paramListPos)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkPropertyHookModifiers(int $a, int $b, int $modifierPos): void {
|
||||
try {
|
||||
Modifiers::verifyModifier($a, $b);
|
||||
} catch (Error $error) {
|
||||
$error->setAttributes($this->getAttributesAt($modifierPos));
|
||||
$this->emitError($error);
|
||||
}
|
||||
|
||||
if ($b != Modifiers::FINAL) {
|
||||
$this->emitError(new Error(
|
||||
'Cannot use the ' . Modifiers::toString($b) . ' modifier on a property hook',
|
||||
$this->getAttributesAt($modifierPos)));
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<Node\Arg|Node\VariadicPlaceholder> $args */
|
||||
private function isSimpleExit(array $args): bool {
|
||||
if (\count($args) === 0) {
|
||||
return true;
|
||||
}
|
||||
if (\count($args) === 1) {
|
||||
$arg = $args[0];
|
||||
return $arg instanceof Arg && $arg->name === null &&
|
||||
$arg->byRef === false && $arg->unpack === false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node\Arg|Node\VariadicPlaceholder> $args
|
||||
* @param array<string, mixed> $attrs
|
||||
*/
|
||||
protected function createExitExpr(string $name, int $namePos, array $args, array $attrs): Expr {
|
||||
if ($this->isSimpleExit($args)) {
|
||||
// Create Exit node for backwards compatibility.
|
||||
$attrs['kind'] = strtolower($name) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE;
|
||||
return new Expr\Exit_(\count($args) === 1 ? $args[0]->value : null, $attrs);
|
||||
}
|
||||
return new Expr\FuncCall(new Name($name, $this->getAttributesAt($namePos)), $args, $attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the token map.
|
||||
*
|
||||
@@ -1190,42 +1234,23 @@ abstract class ParserAbstract implements Parser {
|
||||
protected function createTokenMap(): array {
|
||||
$tokenMap = [];
|
||||
|
||||
for ($i = 0; $i < 1000; ++$i) {
|
||||
if ($i < 256) {
|
||||
// Single-char tokens use an identity mapping.
|
||||
$tokenMap[$i] = $i;
|
||||
} elseif (\T_DOUBLE_COLON === $i) {
|
||||
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
||||
$tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM;
|
||||
} elseif (\T_OPEN_TAG_WITH_ECHO === $i) {
|
||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||
$tokenMap[$i] = static::T_ECHO;
|
||||
} elseif (\T_CLOSE_TAG === $i) {
|
||||
// T_CLOSE_TAG is equivalent to ';'
|
||||
$tokenMap[$i] = ord(';');
|
||||
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
||||
if (defined($name = static::class . '::' . $name)) {
|
||||
// Other tokens can be mapped directly
|
||||
$tokenMap[$i] = constant($name);
|
||||
}
|
||||
// Single-char tokens use an identity mapping.
|
||||
for ($i = 0; $i < 256; ++$i) {
|
||||
$tokenMap[$i] = $i;
|
||||
}
|
||||
|
||||
foreach ($this->symbolToName as $name) {
|
||||
if ($name[0] === 'T') {
|
||||
$tokenMap[\constant($name)] = constant(static::class . '::' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
|
||||
$tokenMap[\T_FN] = static::T_FN;
|
||||
$tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL;
|
||||
$tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED;
|
||||
$tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED;
|
||||
$tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE;
|
||||
$tokenMap[\T_MATCH] = static::T_MATCH;
|
||||
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR;
|
||||
$tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE;
|
||||
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_ENUM] = static::T_ENUM;
|
||||
$tokenMap[\T_READONLY] = static::T_READONLY;
|
||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||
$tokenMap[\T_OPEN_TAG_WITH_ECHO] = static::T_ECHO;
|
||||
// T_CLOSE_TAG is equivalent to ';'
|
||||
$tokenMap[\T_CLOSE_TAG] = ord(';');
|
||||
|
||||
// We have create a map from PHP token IDs to external symbol IDs.
|
||||
// We have created a map from PHP token IDs to external symbol IDs.
|
||||
// Now map them to the internal symbol ID.
|
||||
$fullTokenMap = [];
|
||||
foreach ($tokenMap as $phpToken => $extSymbol) {
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ class PhpVersion {
|
||||
* if it is still under development.
|
||||
*/
|
||||
public static function getNewestSupported(): self {
|
||||
return self::fromComponents(8, 3);
|
||||
return self::fromComponents(8, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,8 @@ class Standard extends PrettyPrinterAbstract {
|
||||
. ($node->byRef ? '&' : '')
|
||||
. ($node->variadic ? '...' : '')
|
||||
. $this->p($node->var)
|
||||
. ($node->default ? ' = ' . $this->p($node->default) : '');
|
||||
. ($node->default ? ' = ' . $this->p($node->default) : '')
|
||||
. ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : '');
|
||||
}
|
||||
|
||||
protected function pArg(Node\Arg $node): string {
|
||||
@@ -125,6 +126,10 @@ class Standard extends PrettyPrinterAbstract {
|
||||
return '__TRAIT__';
|
||||
}
|
||||
|
||||
protected function pScalar_MagicConst_Property(MagicConst\Property $node): string {
|
||||
return '__PROPERTY__';
|
||||
}
|
||||
|
||||
// Scalars
|
||||
|
||||
private function indentString(string $str): string {
|
||||
@@ -827,7 +832,8 @@ class Standard extends PrettyPrinterAbstract {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags))
|
||||
. ($node->type ? $this->p($node->type) . ' ' : '')
|
||||
. $this->pCommaSeparated($node->props) . ';';
|
||||
. $this->pCommaSeparated($node->props)
|
||||
. ($node->hooks ? ' {' . $this->pStmts($node->hooks) . $this->nl . '}' : ';');
|
||||
}
|
||||
|
||||
protected function pPropertyItem(Node\PropertyItem $node): string {
|
||||
@@ -835,6 +841,15 @@ class Standard extends PrettyPrinterAbstract {
|
||||
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
|
||||
}
|
||||
|
||||
protected function pPropertyHook(Node\PropertyHook $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. $this->pModifiers($node->flags)
|
||||
. ($node->byRef ? '&' : '') . $node->name
|
||||
. ($node->params ? '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' : '')
|
||||
. (\is_array($node->body) ? ' {' . $this->pStmts($node->body) . $this->nl . '}'
|
||||
: ($node->body !== null ? ' => ' . $this->p($node->body) : '') . ';');
|
||||
}
|
||||
|
||||
protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string {
|
||||
return $this->pAttrGroups($node->attrGroups)
|
||||
. $this->pModifiers($node->flags)
|
||||
|
||||
+48
-10
@@ -14,6 +14,7 @@ use PhpParser\Node\Expr\Cast;
|
||||
use PhpParser\Node\IntersectionType;
|
||||
use PhpParser\Node\MatchArm;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\PropertyHook;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\UnionType;
|
||||
@@ -105,6 +106,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
|
||||
/** @var int Current indentation level. */
|
||||
protected int $indentLevel;
|
||||
/** @var string String for single level of indentation */
|
||||
private string $indent;
|
||||
/** @var int Width in spaces to indent by. */
|
||||
private int $indentWidth;
|
||||
/** @var bool Whether to use tab indentation. */
|
||||
private bool $useTabs;
|
||||
/** @var int Width in spaces of one tab. */
|
||||
private int $tabWidth = 4;
|
||||
|
||||
/** @var string Newline style. Does not include current indentation. */
|
||||
protected string $newline;
|
||||
/** @var string Newline including current indentation. */
|
||||
@@ -169,12 +179,14 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
* PHP version while specifying an older target (but the result will
|
||||
* of course not be compatible with the older version in that case).
|
||||
* * string $newline: The newline style to use. Should be "\n" (default) or "\r\n".
|
||||
* * string $indent: The indentation to use. Should either be all spaces or a single
|
||||
* tab. Defaults to four spaces (" ").
|
||||
* * bool $shortArraySyntax: Whether to use [] instead of array() as the default array
|
||||
* syntax, if the node does not specify a format. Defaults to whether
|
||||
* the phpVersion support short array syntax.
|
||||
*
|
||||
* @param array{
|
||||
* phpVersion?: PhpVersion, newline?: string, shortArraySyntax?: bool
|
||||
* phpVersion?: PhpVersion, newline?: string, indent?: string, shortArraySyntax?: bool
|
||||
* } $options Dictionary of formatting options
|
||||
*/
|
||||
public function __construct(array $options = []) {
|
||||
@@ -189,6 +201,17 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax();
|
||||
$this->docStringEndToken =
|
||||
$this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand();
|
||||
|
||||
$this->indent = $indent = $options['indent'] ?? ' ';
|
||||
if ($indent === "\t") {
|
||||
$this->useTabs = true;
|
||||
$this->indentWidth = $this->tabWidth;
|
||||
} elseif ($indent === \str_repeat(' ', \strlen($indent))) {
|
||||
$this->useTabs = false;
|
||||
$this->indentWidth = \strlen($indent);
|
||||
} else {
|
||||
throw new \LogicException('Option "indent" must either be all spaces or a single tab');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,24 +230,29 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
*/
|
||||
protected function setIndentLevel(int $level): void {
|
||||
$this->indentLevel = $level;
|
||||
$this->nl = $this->newline . \str_repeat(' ', $level);
|
||||
if ($this->useTabs) {
|
||||
$tabs = \intdiv($level, $this->tabWidth);
|
||||
$spaces = $level % $this->tabWidth;
|
||||
$this->nl = $this->newline . \str_repeat("\t", $tabs) . \str_repeat(' ', $spaces);
|
||||
} else {
|
||||
$this->nl = $this->newline . \str_repeat(' ', $level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase indentation level.
|
||||
*/
|
||||
protected function indent(): void {
|
||||
$this->indentLevel += 4;
|
||||
$this->nl .= ' ';
|
||||
$this->indentLevel += $this->indentWidth;
|
||||
$this->nl .= $this->indent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease indentation level.
|
||||
*/
|
||||
protected function outdent(): void {
|
||||
assert($this->indentLevel >= 4);
|
||||
$this->indentLevel -= 4;
|
||||
$this->nl = $this->newline . str_repeat(' ', $this->indentLevel);
|
||||
assert($this->indentLevel >= $this->indentWidth);
|
||||
$this->setIndentLevel($this->indentLevel - $this->indentWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,7 +564,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$this->initializeModifierChangeMap();
|
||||
|
||||
$this->resetState();
|
||||
$this->origTokens = new TokenStream($origTokens);
|
||||
$this->origTokens = new TokenStream($origTokens, $this->tabWidth);
|
||||
|
||||
$this->preprocessNodes($stmts);
|
||||
|
||||
@@ -708,7 +736,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
$result .= $extraLeft;
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment);
|
||||
$this->setIndentLevel(max($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment, 0));
|
||||
|
||||
// If it's the same node that was previously in this position, it certainly doesn't
|
||||
// need fixup. It's important to check this here, because our fixup checks are more
|
||||
@@ -811,7 +839,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment;
|
||||
$lastElemIndentLevel = max($this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
|
||||
$this->setIndentLevel($lastElemIndentLevel);
|
||||
|
||||
$comments = $arrItem->getComments();
|
||||
@@ -1195,6 +1223,9 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
. ($modifiers & Modifiers::PUBLIC ? 'public ' : '')
|
||||
. ($modifiers & Modifiers::PROTECTED ? 'protected ' : '')
|
||||
. ($modifiers & Modifiers::PRIVATE ? 'private ' : '')
|
||||
. ($modifiers & Modifiers::PUBLIC_SET ? 'public(set) ' : '')
|
||||
. ($modifiers & Modifiers::PROTECTED_SET ? 'protected(set) ' : '')
|
||||
. ($modifiers & Modifiers::PRIVATE_SET ? 'private(set) ' : '')
|
||||
. ($modifiers & Modifiers::STATIC ? 'static ' : '')
|
||||
. ($modifiers & Modifiers::READONLY ? 'readonly ' : '');
|
||||
}
|
||||
@@ -1517,6 +1548,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Stmt\UseUse::class . '->uses' => ', ',
|
||||
MatchArm::class . '->conds' => ', ',
|
||||
AttributeGroup::class . '->attrs' => ', ',
|
||||
PropertyHook::class . '->params' => ', ',
|
||||
|
||||
// statement lists
|
||||
Expr\Closure::class . '->stmts' => "\n",
|
||||
@@ -1554,10 +1586,15 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Expr\Closure::class . '->attrGroups' => ' ',
|
||||
Expr\ArrowFunction::class . '->attrGroups' => ' ',
|
||||
Param::class . '->attrGroups' => ' ',
|
||||
PropertyHook::class . '->attrGroups' => ' ',
|
||||
|
||||
Stmt\Switch_::class . '->cases' => "\n",
|
||||
Stmt\TraitUse::class . '->adaptations' => "\n",
|
||||
Stmt\TryCatch::class . '->stmts' => "\n",
|
||||
Stmt\While_::class . '->stmts' => "\n",
|
||||
PropertyHook::class . '->body' => "\n",
|
||||
Stmt\Property::class . '->hooks' => "\n",
|
||||
Param::class . '->hooks' => "\n",
|
||||
|
||||
// dummy for top-level context
|
||||
'File->stmts' => "\n",
|
||||
@@ -1641,6 +1678,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
||||
Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE],
|
||||
PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS],
|
||||
Param::class . '->flags' => ['pModifiers', \T_VARIABLE],
|
||||
PropertyHook::class . '->flags' => ['pModifiers', \T_STRING],
|
||||
Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION],
|
||||
Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN],
|
||||
//Stmt\TraitUseAdaptation\Alias::class . '->newModifier' => 0, // TODO
|
||||
|
||||
@@ -17,6 +17,11 @@ if (!\function_exists('PhpParser\defineCompatibilityTokens')) {
|
||||
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_READONLY',
|
||||
// PHP 8.4
|
||||
'T_PROPERTY_C',
|
||||
'T_PUBLIC_SET',
|
||||
'T_PROTECTED_SET',
|
||||
'T_PRIVATE_SET',
|
||||
];
|
||||
|
||||
// PHP-Parser might be used together with another library that also emulates some or all
|
||||
|
||||
Reference in New Issue
Block a user