summaryrefslogtreecommitdiff
path: root/vendor/nikic/php-parser/lib/PhpParser/Internal
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2022-03-22 12:24:31 +0300
committerAndrew Dolgov <[email protected]>2022-03-22 12:24:31 +0300
commit1c4f7ab3b838b23afb2ee4dab14acbf75956e952 (patch)
tree0a19274107d717efe92d2c0376cd3105fead5a11 /vendor/nikic/php-parser/lib/PhpParser/Internal
parent711662948768492e8d05b778a7d80eacaec368d2 (diff)
* add phpunit as a dev dependency
* add some basic tests for UrlHelper::rewrite_relative() * fix UrlHelper::rewrite_relative() to work better on non-absolute relative URL paths
Diffstat (limited to 'vendor/nikic/php-parser/lib/PhpParser/Internal')
-rw-r--r--vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php27
-rw-r--r--vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php164
-rw-r--r--vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php61
-rw-r--r--vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php281
4 files changed, 533 insertions, 0 deletions
diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php
new file mode 100644
index 000000000..a38b57ba9
--- /dev/null
+++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php
@@ -0,0 +1,27 @@
+<?php declare(strict_types=1);
+
+namespace PhpParser\Internal;
+
+/**
+ * @internal
+ */
+class DiffElem
+{
+ const TYPE_KEEP = 0;
+ const TYPE_REMOVE = 1;
+ const TYPE_ADD = 2;
+ const TYPE_REPLACE = 3;
+
+ /** @var int One of the TYPE_* constants */
+ public $type;
+ /** @var mixed Is null for add operations */
+ public $old;
+ /** @var mixed Is null for remove operations */
+ public $new;
+
+ public function __construct(int $type, $old, $new) {
+ $this->type = $type;
+ $this->old = $old;
+ $this->new = $new;
+ }
+}
diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php
new file mode 100644
index 000000000..7f218c74f
--- /dev/null
+++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php
@@ -0,0 +1,164 @@
+<?php declare(strict_types=1);
+
+namespace PhpParser\Internal;
+
+/**
+ * Implements the Myers diff algorithm.
+ *
+ * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
+ * Algorithmica 1.1 (1986): 251-266.
+ *
+ * @internal
+ */
+class Differ
+{
+ private $isEqual;
+
+ /**
+ * Create differ over the given equality relation.
+ *
+ * @param callable $isEqual Equality relation with signature function($a, $b) : bool
+ */
+ public function __construct(callable $isEqual) {
+ $this->isEqual = $isEqual;
+ }
+
+ /**
+ * Calculate diff (edit script) from $old to $new.
+ *
+ * @param array $old Original array
+ * @param array $new New array
+ *
+ * @return DiffElem[] Diff (edit script)
+ */
+ public function diff(array $old, array $new) {
+ list($trace, $x, $y) = $this->calculateTrace($old, $new);
+ return $this->extractDiff($trace, $x, $y, $old, $new);
+ }
+
+ /**
+ * Calculate diff, including "replace" operations.
+ *
+ * If a sequence of remove operations is followed by the same number of add operations, these
+ * will be coalesced into replace operations.
+ *
+ * @param array $old Original array
+ * @param array $new New array
+ *
+ * @return DiffElem[] Diff (edit script), including replace operations
+ */
+ public function diffWithReplacements(array $old, array $new) {
+ return $this->coalesceReplacements($this->diff($old, $new));
+ }
+
+ private function calculateTrace(array $a, array $b) {
+ $n = \count($a);
+ $m = \count($b);
+ $max = $n + $m;
+ $v = [1 => 0];
+ $trace = [];
+ for ($d = 0; $d <= $max; $d++) {
+ $trace[] = $v;
+ for ($k = -$d; $k <= $d; $k += 2) {
+ if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
+ $x = $v[$k+1];
+ } else {
+ $x = $v[$k-1] + 1;
+ }
+
+ $y = $x - $k;
+ while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) {
+ $x++;
+ $y++;
+ }
+
+ $v[$k] = $x;
+ if ($x >= $n && $y >= $m) {
+ return [$trace, $x, $y];
+ }
+ }
+ }
+ throw new \Exception('Should not happen');
+ }
+
+ private function extractDiff(array $trace, int $x, int $y, array $a, array $b) {
+ $result = [];
+ for ($d = \count($trace) - 1; $d >= 0; $d--) {
+ $v = $trace[$d];
+ $k = $x - $y;
+
+ if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
+ $prevK = $k + 1;
+ } else {
+ $prevK = $k - 1;
+ }
+
+ $prevX = $v[$prevK];
+ $prevY = $prevX - $prevK;
+
+ while ($x > $prevX && $y > $prevY) {
+ $result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]);
+ $x--;
+ $y--;
+ }
+
+ if ($d === 0) {
+ break;
+ }
+
+ while ($x > $prevX) {
+ $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null);
+ $x--;
+ }
+
+ while ($y > $prevY) {
+ $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]);
+ $y--;
+ }
+ }
+ return array_reverse($result);
+ }
+
+ /**
+ * Coalesce equal-length sequences of remove+add into a replace operation.
+ *
+ * @param DiffElem[] $diff
+ * @return DiffElem[]
+ */
+ private function coalesceReplacements(array $diff) {
+ $newDiff = [];
+ $c = \count($diff);
+ for ($i = 0; $i < $c; $i++) {
+ $diffType = $diff[$i]->type;
+ if ($diffType !== DiffElem::TYPE_REMOVE) {
+ $newDiff[] = $diff[$i];
+ continue;
+ }
+
+ $j = $i;
+ while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
+ $j++;
+ }
+
+ $k = $j;
+ while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
+ $k++;
+ }
+
+ if ($j - $i === $k - $j) {
+ $len = $j - $i;
+ for ($n = 0; $n < $len; $n++) {
+ $newDiff[] = new DiffElem(
+ DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new
+ );
+ }
+ } else {
+ for (; $i < $k; $i++) {
+ $newDiff[] = $diff[$i];
+ }
+ }
+ $i = $k - 1;
+ }
+ return $newDiff;
+ }
+}
diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php
new file mode 100644
index 000000000..3eeac04a4
--- /dev/null
+++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php
@@ -0,0 +1,61 @@
+<?php declare(strict_types=1);
+
+namespace PhpParser\Internal;
+
+use PhpParser\Node;
+use PhpParser\Node\Expr;
+
+/**
+ * This node is used internally by the format-preserving pretty printer to print anonymous classes.
+ *
+ * The normal anonymous class structure violates assumptions about the order of token offsets.
+ * Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even
+ * though they are actually interleaved with them. This special node type is used temporarily to
+ * restore a sane token offset order.
+ *
+ * @internal
+ */
+class PrintableNewAnonClassNode extends Expr
+{
+ /** @var Node\AttributeGroup[] PHP attribute groups */
+ public $attrGroups;
+ /** @var Node\Arg[] Arguments */
+ public $args;
+ /** @var null|Node\Name Name of extended class */
+ public $extends;
+ /** @var Node\Name[] Names of implemented interfaces */
+ public $implements;
+ /** @var Node\Stmt[] Statements */
+ public $stmts;
+
+ public function __construct(
+ array $attrGroups, array $args, Node\Name $extends = null, array $implements,
+ array $stmts, array $attributes
+ ) {
+ parent::__construct($attributes);
+ $this->attrGroups = $attrGroups;
+ $this->args = $args;
+ $this->extends = $extends;
+ $this->implements = $implements;
+ $this->stmts = $stmts;
+ }
+
+ public static function fromNewNode(Expr\New_ $newNode) {
+ $class = $newNode->class;
+ assert($class instanceof Node\Stmt\Class_);
+ // We don't assert that $class->name is null here, to allow consumers to assign unique names
+ // to anonymous classes for their own purposes. We simplify ignore the name here.
+ return new self(
+ $class->attrGroups, $newNode->args, $class->extends, $class->implements,
+ $class->stmts, $newNode->getAttributes()
+ );
+ }
+
+ public function getType() : string {
+ return 'Expr_PrintableNewAnonClass';
+ }
+
+ public function getSubNodeNames() : array {
+ return ['attrGroups', 'args', 'extends', 'implements', 'stmts'];
+ }
+}
diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php
new file mode 100644
index 000000000..84c0175ec
--- /dev/null
+++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php
@@ -0,0 +1,281 @@
+<?php declare(strict_types=1);
+
+namespace PhpParser\Internal;
+
+/**
+ * Provides operations on token streams, for use by pretty printer.
+ *
+ * @internal
+ */
+class TokenStream
+{
+ /** @var array Tokens (in token_get_all format) */
+ private $tokens;
+ /** @var int[] Map from position to indentation */
+ private $indentMap;
+
+ /**
+ * Create token stream instance.
+ *
+ * @param array $tokens Tokens in token_get_all() format
+ */
+ public function __construct(array $tokens) {
+ $this->tokens = $tokens;
+ $this->indentMap = $this->calcIndentMap();
+ }
+
+ /**
+ * Whether the given position is immediately surrounded by parenthesis.
+ *
+ * @param int $startPos Start position
+ * @param int $endPos End position
+ *
+ * @return bool
+ */
+ public function haveParens(int $startPos, int $endPos) : bool {
+ return $this->haveTokenImmediatelyBefore($startPos, '(')
+ && $this->haveTokenImmediatelyAfter($endPos, ')');
+ }
+
+ /**
+ * Whether the given position is immediately surrounded by braces.
+ *
+ * @param int $startPos Start position
+ * @param int $endPos End position
+ *
+ * @return bool
+ */
+ public function haveBraces(int $startPos, int $endPos) : bool {
+ return ($this->haveTokenImmediatelyBefore($startPos, '{')
+ || $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
+ && $this->haveTokenImmediatelyAfter($endPos, '}');
+ }
+
+ /**
+ * Check whether the position is directly preceded by a certain token type.
+ *
+ * During this check whitespace and comments are skipped.
+ *
+ * @param int $pos Position before which the token should occur
+ * @param int|string $expectedTokenType Token to check for
+ *
+ * @return bool Whether the expected token was found
+ */
+ public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool {
+ $tokens = $this->tokens;
+ $pos--;
+ for (; $pos >= 0; $pos--) {
+ $tokenType = $tokens[$pos][0];
+ if ($tokenType === $expectedTokenType) {
+ return true;
+ }
+ if ($tokenType !== \T_WHITESPACE
+ && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
+ break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the position is directly followed by a certain token type.
+ *
+ * During this check whitespace and comments are skipped.
+ *
+ * @param int $pos Position after which the token should occur
+ * @param int|string $expectedTokenType Token to check for
+ *
+ * @return bool Whether the expected token was found
+ */
+ public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool {
+ $tokens = $this->tokens;
+ $pos++;
+ for (; $pos < \count($tokens); $pos++) {
+ $tokenType = $tokens[$pos][0];
+ if ($tokenType === $expectedTokenType) {
+ return true;
+ }
+ if ($tokenType !== \T_WHITESPACE
+ && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
+ break;
+ }
+ }
+ return false;
+ }
+
+ public function skipLeft(int $pos, $skipTokenType) {
+ $tokens = $this->tokens;
+
+ $pos = $this->skipLeftWhitespace($pos);
+ if ($skipTokenType === \T_WHITESPACE) {
+ return $pos;
+ }
+
+ if ($tokens[$pos][0] !== $skipTokenType) {
+ // Shouldn't happen. The skip token MUST be there
+ throw new \Exception('Encountered unexpected token');
+ }
+ $pos--;
+
+ return $this->skipLeftWhitespace($pos);
+ }
+
+ public function skipRight(int $pos, $skipTokenType) {
+ $tokens = $this->tokens;
+
+ $pos = $this->skipRightWhitespace($pos);
+ if ($skipTokenType === \T_WHITESPACE) {
+ return $pos;
+ }
+
+ if ($tokens[$pos][0] !== $skipTokenType) {
+ // Shouldn't happen. The skip token MUST be there
+ throw new \Exception('Encountered unexpected token');
+ }
+ $pos++;
+
+ return $this->skipRightWhitespace($pos);
+ }
+
+ /**
+ * Return first non-whitespace token position smaller or equal to passed position.
+ *
+ * @param int $pos Token position
+ * @return int Non-whitespace token position
+ */
+ public function skipLeftWhitespace(int $pos) {
+ $tokens = $this->tokens;
+ for (; $pos >= 0; $pos--) {
+ $type = $tokens[$pos][0];
+ if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
+ break;
+ }
+ }
+ return $pos;
+ }
+
+ /**
+ * Return first non-whitespace position greater or equal to passed position.
+ *
+ * @param int $pos Token position
+ * @return int Non-whitespace token position
+ */
+ public function skipRightWhitespace(int $pos) {
+ $tokens = $this->tokens;
+ for ($count = \count($tokens); $pos < $count; $pos++) {
+ $type = $tokens[$pos][0];
+ if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
+ break;
+ }
+ }
+ return $pos;
+ }
+
+ public function findRight(int $pos, $findTokenType) {
+ $tokens = $this->tokens;
+ for ($count = \count($tokens); $pos < $count; $pos++) {
+ $type = $tokens[$pos][0];
+ if ($type === $findTokenType) {
+ return $pos;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Whether the given position range contains a certain token type.
+ *
+ * @param int $startPos Starting position (inclusive)
+ * @param int $endPos Ending position (exclusive)
+ * @param int|string $tokenType Token type to look for
+ * @return bool Whether the token occurs in the given range
+ */
+ public function haveTokenInRange(int $startPos, int $endPos, $tokenType) {
+ $tokens = $this->tokens;
+ for ($pos = $startPos; $pos < $endPos; $pos++) {
+ if ($tokens[$pos][0] === $tokenType) {
+ return true;
+ }
+ }
+ 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, '}');
+ }
+
+ /**
+ * Get indentation before token position.
+ *
+ * @param int $pos Token position
+ *
+ * @return int Indentation depth (in spaces)
+ */
+ public function getIndentationBefore(int $pos) : int {
+ return $this->indentMap[$pos];
+ }
+
+ /**
+ * Get the code corresponding to a token offset range, optionally adjusted for indentation.
+ *
+ * @param int $from Token start position (inclusive)
+ * @param int $to Token end position (exclusive)
+ * @param int $indent By how much the code should be indented (can be negative as well)
+ *
+ * @return string Code corresponding to token range, adjusted for indentation
+ */
+ public function getTokenCode(int $from, int $to, int $indent) : string {
+ $tokens = $this->tokens;
+ $result = '';
+ for ($pos = $from; $pos < $to; $pos++) {
+ $token = $tokens[$pos];
+ if (\is_array($token)) {
+ $type = $token[0];
+ $content = $token[1];
+ if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) {
+ $result .= $content;
+ } else {
+ // TODO Handle non-space indentation
+ if ($indent < 0) {
+ $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
+ } elseif ($indent > 0) {
+ $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
+ } else {
+ $result .= $content;
+ }
+ }
+ } else {
+ $result .= $token;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Precalculate the indentation at every token position.
+ *
+ * @return int[] Token position to indentation map
+ */
+ private function calcIndentMap() {
+ $indentMap = [];
+ $indent = 0;
+ foreach ($this->tokens as $token) {
+ $indentMap[] = $indent;
+
+ if ($token[0] === \T_WHITESPACE) {
+ $content = $token[1];
+ $newlinePos = \strrpos($content, "\n");
+ if (false !== $newlinePos) {
+ $indent = \strlen($content) - $newlinePos - 1;
+ }
+ }
+ }
+
+ // Add a sentinel for one past end of the file
+ $indentMap[] = $indent;
+
+ return $indentMap;
+ }
+}