N, [>=M, !=N,
getEnd()->getOperator() === '<' && $i+1 < $count) {
- $nextInterval = $intervals['numeric'][$i+1];
- if ($interval->getEnd()->getVersion() === $nextInterval->getStart()->getVersion() && $nextInterval->getStart()->getOperator() === '>') {
- // only add a start if we didn't already do so, can be skipped if we're looking at second
- // interval in [>=M, N, P, =M, !=N] already and we only want to add !=P right now
- if (\count($unEqualConstraints) === 0 && (string) $interval->getStart() !== (string) Interval::fromZero()) {
- $unEqualConstraints[] = $interval->getStart();
- }
- $unEqualConstraints[] = new Constraint('!=', $interval->getEnd()->getVersion());
- continue;
- }
- }
-
- if (\count($unEqualConstraints) > 0) {
- // this is where the end of the following interval of a != constraint is added as explained above
- if ((string) $interval->getEnd() !== (string) Interval::untilPositiveInfinity()) {
- $unEqualConstraints[] = $interval->getEnd();
- }
-
- // count is 1 if entire constraint is just one != expression
- if (\count($unEqualConstraints) > 1) {
- $constraints[] = new MultiConstraint($unEqualConstraints, true);
- } else {
- $constraints[] = $unEqualConstraints[0];
- }
-
- $unEqualConstraints = array();
- continue;
- }
-
- // convert back >= x - <= x intervals to == x
- if ($interval->getStart()->getVersion() === $interval->getEnd()->getVersion() && $interval->getStart()->getOperator() === '>=' && $interval->getEnd()->getOperator() === '<=') {
- $constraints[] = new Constraint('==', $interval->getStart()->getVersion());
- continue;
- }
-
- if ((string) $interval->getStart() === (string) Interval::fromZero()) {
- $constraints[] = $interval->getEnd();
- } elseif ((string) $interval->getEnd() === (string) Interval::untilPositiveInfinity()) {
- $constraints[] = $interval->getStart();
- } else {
- $constraints[] = new MultiConstraint(array($interval->getStart(), $interval->getEnd()), true);
- }
- }
- }
-
- $devConstraints = array();
-
- if (0 === \count($intervals['branches']['names'])) {
- if ($intervals['branches']['exclude']) {
- if ($hasNumericMatchAll) {
- return new MatchAllConstraint;
- }
- // otherwise constraint should contain a != operator and already cover this
- }
- } else {
- foreach ($intervals['branches']['names'] as $branchName) {
- if ($intervals['branches']['exclude']) {
- $devConstraints[] = new Constraint('!=', $branchName);
- } else {
- $devConstraints[] = new Constraint('==', $branchName);
- }
- }
-
- // excluded branches, e.g. != dev-foo are conjunctive with the interval, so
- // > 2.0 != dev-foo must return a conjunctive constraint
- if ($intervals['branches']['exclude']) {
- if (\count($constraints) > 1) {
- return new MultiConstraint(array_merge(
- array(new MultiConstraint($constraints, false)),
- $devConstraints
- ), true);
- }
-
- if (\count($constraints) === 1 && (string)$constraints[0] === (string)Interval::fromZero()) {
- if (\count($devConstraints) > 1) {
- return new MultiConstraint($devConstraints, true);
- }
- return $devConstraints[0];
- }
-
- return new MultiConstraint(array_merge($constraints, $devConstraints), true);
- }
-
- // otherwise devConstraints contains a list of == operators for branches which are disjunctive with the
- // rest of the constraint
- $constraints = array_merge($constraints, $devConstraints);
- }
-
- if (\count($constraints) > 1) {
- return new MultiConstraint($constraints, false);
- }
-
- if (\count($constraints) === 1) {
- return $constraints[0];
- }
-
- return new MatchNoneConstraint;
- }
-
- /**
- * Creates an array of numeric intervals and branch constraints representing a given constraint
- *
- * if the returned numeric array is empty it means the constraint matches nothing in the numeric range (0 - +inf)
- * if the returned branches array is empty it means no dev-* versions are matched
- * if a constraint matches all possible dev-* versions, branches will contain Interval::anyDev()
- *
- * @return array
- * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}
- */
- public static function get(ConstraintInterface $constraint)
- {
- $key = (string) $constraint;
-
- if (!isset(self::$intervalsCache[$key])) {
- self::$intervalsCache[$key] = self::generateIntervals($constraint);
- }
-
- return self::$intervalsCache[$key];
- }
-
- /**
- * @param bool $stopOnFirstValidInterval
- *
- * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}
- */
- private static function generateIntervals(ConstraintInterface $constraint, $stopOnFirstValidInterval = false)
- {
- if ($constraint instanceof MatchAllConstraint) {
- return array('numeric' => array(new Interval(Interval::fromZero(), Interval::untilPositiveInfinity())), 'branches' => Interval::anyDev());
- }
-
- if ($constraint instanceof MatchNoneConstraint) {
- return array('numeric' => array(), 'branches' => array('names' => array(), 'exclude' => false));
- }
-
- if ($constraint instanceof Constraint) {
- return self::generateSingleConstraintIntervals($constraint);
- }
-
- if (!$constraint instanceof MultiConstraint) {
- throw new \UnexpectedValueException('The constraint passed in should be an MatchAllConstraint, Constraint or MultiConstraint instance, got '.\get_class($constraint).'.');
- }
-
- $constraints = $constraint->getConstraints();
-
- $numericGroups = array();
- $constraintBranches = array();
- foreach ($constraints as $c) {
- $res = self::get($c);
- $numericGroups[] = $res['numeric'];
- $constraintBranches[] = $res['branches'];
- }
-
- if ($constraint->isDisjunctive()) {
- $branches = Interval::noDev();
- foreach ($constraintBranches as $b) {
- if ($b['exclude']) {
- if ($branches['exclude']) {
- // disjunctive constraint, so only exclude what's excluded in all constraints
- // !=a,!=b || !=b,!=c => !=b
- $branches['names'] = array_intersect($branches['names'], $b['names']);
- } else {
- // disjunctive constraint so exclude all names which are not explicitly included in the alternative
- // (==b || ==c) || !=a,!=b => !=a
- $branches['exclude'] = true;
- $branches['names'] = array_diff($b['names'], $branches['names']);
- }
- } else {
- if ($branches['exclude']) {
- // disjunctive constraint so exclude all names which are not explicitly included in the alternative
- // !=a,!=b || (==b || ==c) => !=a
- $branches['names'] = array_diff($branches['names'], $b['names']);
- } else {
- // disjunctive constraint, so just add all the other branches
- // (==a || ==b) || ==c => ==a || ==b || ==c
- $branches['names'] = array_merge($branches['names'], $b['names']);
- }
- }
- }
- } else {
- $branches = Interval::anyDev();
- foreach ($constraintBranches as $b) {
- if ($b['exclude']) {
- if ($branches['exclude']) {
- // conjunctive, so just add all branch names to be excluded
- // !=a && !=b => !=a,!=b
- $branches['names'] = array_merge($branches['names'], $b['names']);
- } else {
- // conjunctive, so only keep included names which are not excluded
- // (==a||==c) && !=a,!=b => ==c
- $branches['names'] = array_diff($branches['names'], $b['names']);
- }
- } else {
- if ($branches['exclude']) {
- // conjunctive, so only keep included names which are not excluded
- // !=a,!=b && (==a||==c) => ==c
- $branches['names'] = array_diff($b['names'], $branches['names']);
- $branches['exclude'] = false;
- } else {
- // conjunctive, so only keep names that are included in both
- // (==a||==b) && (==a||==c) => ==a
- $branches['names'] = array_intersect($branches['names'], $b['names']);
- }
- }
- }
- }
-
- $branches['names'] = array_unique($branches['names']);
-
- if (\count($numericGroups) === 1) {
- return array('numeric' => $numericGroups[0], 'branches' => $branches);
- }
-
- $borders = array();
- foreach ($numericGroups as $group) {
- foreach ($group as $interval) {
- $borders[] = array('version' => $interval->getStart()->getVersion(), 'operator' => $interval->getStart()->getOperator(), 'side' => 'start');
- $borders[] = array('version' => $interval->getEnd()->getVersion(), 'operator' => $interval->getEnd()->getOperator(), 'side' => 'end');
- }
- }
-
- $opSortOrder = self::$opSortOrder;
- usort($borders, function ($a, $b) use ($opSortOrder) {
- $order = version_compare($a['version'], $b['version']);
- if ($order === 0) {
- return $opSortOrder[$a['operator']] - $opSortOrder[$b['operator']];
- }
-
- return $order;
- });
-
- $activeIntervals = 0;
- $intervals = array();
- $index = 0;
- $activationThreshold = $constraint->isConjunctive() ? \count($numericGroups) : 1;
- $start = null;
- foreach ($borders as $border) {
- if ($border['side'] === 'start') {
- $activeIntervals++;
- } else {
- $activeIntervals--;
- }
- if (!$start && $activeIntervals >= $activationThreshold) {
- $start = new Constraint($border['operator'], $border['version']);
- } elseif ($start && $activeIntervals < $activationThreshold) {
- // filter out invalid intervals like > x - <= x, or >= x - < x
- if (
- version_compare($start->getVersion(), $border['version'], '=')
- && (
- ($start->getOperator() === '>' && $border['operator'] === '<=')
- || ($start->getOperator() === '>=' && $border['operator'] === '<')
- )
- ) {
- unset($intervals[$index]);
- } else {
- $intervals[$index] = new Interval($start, new Constraint($border['operator'], $border['version']));
- $index++;
-
- if ($stopOnFirstValidInterval) {
- break;
- }
- }
-
- $start = null;
- }
- }
-
- return array('numeric' => $intervals, 'branches' => $branches);
- }
-
- /**
- * @phpstan-return array{'numeric': Interval[], 'branches': array{'names': string[], 'exclude': bool}}
- */
- private static function generateSingleConstraintIntervals(Constraint $constraint)
- {
- $op = $constraint->getOperator();
-
- // handle branch constraints first
- if (strpos($constraint->getVersion(), 'dev-') === 0) {
- $intervals = array();
- $branches = array('names' => array(), 'exclude' => false);
-
- // != dev-foo means any numeric version may match, we treat >/< like != they are not really defined for branches
- if ($op === '!=') {
- $intervals[] = new Interval(Interval::fromZero(), Interval::untilPositiveInfinity());
- $branches = array('names' => array($constraint->getVersion()), 'exclude' => true);
- } elseif ($op === '==') {
- $branches['names'][] = $constraint->getVersion();
- }
-
- return array(
- 'numeric' => $intervals,
- 'branches' => $branches,
- );
- }
-
- if ($op[0] === '>') { // > & >=
- return array('numeric' => array(new Interval($constraint, Interval::untilPositiveInfinity())), 'branches' => Interval::noDev());
- }
- if ($op[0] === '<') { // < & <=
- return array('numeric' => array(new Interval(Interval::fromZero(), $constraint)), 'branches' => Interval::noDev());
- }
- if ($op === '!=') {
- // convert !=x to intervals of 0 - x - +inf + dev*
- return array('numeric' => array(
- new Interval(Interval::fromZero(), new Constraint('<', $constraint->getVersion())),
- new Interval(new Constraint('>', $constraint->getVersion()), Interval::untilPositiveInfinity()),
- ), 'branches' => Interval::anyDev());
- }
-
- // convert ==x to an interval of >=x - <=x
- return array('numeric' => array(
- new Interval(new Constraint('>=', $constraint->getVersion()), new Constraint('<=', $constraint->getVersion())),
- ), 'branches' => Interval::noDev());
- }
-}
diff --git a/vendor/composer/semver/src/Semver.php b/vendor/composer/semver/src/Semver.php
deleted file mode 100644
index 4d6de3c2..00000000
--- a/vendor/composer/semver/src/Semver.php
+++ /dev/null
@@ -1,129 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace Composer\Semver;
-
-use Composer\Semver\Constraint\Constraint;
-
-class Semver
-{
- const SORT_ASC = 1;
- const SORT_DESC = -1;
-
- /** @var VersionParser */
- private static $versionParser;
-
- /**
- * Determine if given version satisfies given constraints.
- *
- * @param string $version
- * @param string $constraints
- *
- * @return bool
- */
- public static function satisfies($version, $constraints)
- {
- if (null === self::$versionParser) {
- self::$versionParser = new VersionParser();
- }
-
- $versionParser = self::$versionParser;
- $provider = new Constraint('==', $versionParser->normalize($version));
- $parsedConstraints = $versionParser->parseConstraints($constraints);
-
- return $parsedConstraints->matches($provider);
- }
-
- /**
- * Return all versions that satisfy given constraints.
- *
- * @param string[] $versions
- * @param string $constraints
- *
- * @return string[]
- */
- public static function satisfiedBy(array $versions, $constraints)
- {
- $versions = array_filter($versions, function ($version) use ($constraints) {
- return Semver::satisfies($version, $constraints);
- });
-
- return array_values($versions);
- }
-
- /**
- * Sort given array of versions.
- *
- * @param string[] $versions
- *
- * @return string[]
- */
- public static function sort(array $versions)
- {
- return self::usort($versions, self::SORT_ASC);
- }
-
- /**
- * Sort given array of versions in reverse.
- *
- * @param string[] $versions
- *
- * @return string[]
- */
- public static function rsort(array $versions)
- {
- return self::usort($versions, self::SORT_DESC);
- }
-
- /**
- * @param string[] $versions
- * @param int $direction
- *
- * @return string[]
- */
- private static function usort(array $versions, $direction)
- {
- if (null === self::$versionParser) {
- self::$versionParser = new VersionParser();
- }
-
- $versionParser = self::$versionParser;
- $normalized = array();
-
- // Normalize outside of usort() scope for minor performance increase.
- // Creates an array of arrays: [[normalized, key], ...]
- foreach ($versions as $key => $version) {
- $normalizedVersion = $versionParser->normalize($version);
- $normalizedVersion = $versionParser->normalizeDefaultBranch($normalizedVersion);
- $normalized[] = array($normalizedVersion, $key);
- }
-
- usort($normalized, function (array $left, array $right) use ($direction) {
- if ($left[0] === $right[0]) {
- return 0;
- }
-
- if (Comparator::lessThan($left[0], $right[0])) {
- return -$direction;
- }
-
- return $direction;
- });
-
- // Recreate input array, using the original indexes which are now in sorted order.
- $sorted = array();
- foreach ($normalized as $item) {
- $sorted[] = $versions[$item[1]];
- }
-
- return $sorted;
- }
-}
diff --git a/vendor/composer/semver/src/VersionParser.php b/vendor/composer/semver/src/VersionParser.php
deleted file mode 100644
index 202ce247..00000000
--- a/vendor/composer/semver/src/VersionParser.php
+++ /dev/null
@@ -1,586 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace Composer\Semver;
-
-use Composer\Semver\Constraint\ConstraintInterface;
-use Composer\Semver\Constraint\MatchAllConstraint;
-use Composer\Semver\Constraint\MultiConstraint;
-use Composer\Semver\Constraint\Constraint;
-
-/**
- * Version parser.
- *
- * @author Jordi Boggiano
- */
-class VersionParser
-{
- /**
- * Regex to match pre-release data (sort of).
- *
- * Due to backwards compatibility:
- * - Instead of enforcing hyphen, an underscore, dot or nothing at all are also accepted.
- * - Only stabilities as recognized by Composer are allowed to precede a numerical identifier.
- * - Numerical-only pre-release identifiers are not supported, see tests.
- *
- * |--------------|
- * [major].[minor].[patch] -[pre-release] +[build-metadata]
- *
- * @var string
- */
- private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*+)?)?([.-]?dev)?';
-
- /** @var string */
- private static $stabilitiesRegex = 'stable|RC|beta|alpha|dev';
-
- /**
- * Returns the stability of a version.
- *
- * @param string $version
- *
- * @return string
- * @phpstan-return 'stable'|'RC'|'beta'|'alpha'|'dev'
- */
- public static function parseStability($version)
- {
- $version = (string) preg_replace('{#.+$}', '', (string) $version);
-
- if (strpos($version, 'dev-') === 0 || '-dev' === substr($version, -4)) {
- return 'dev';
- }
-
- preg_match('{' . self::$modifierRegex . '(?:\+.*)?$}i', strtolower($version), $match);
-
- if (!empty($match[3])) {
- return 'dev';
- }
-
- if (!empty($match[1])) {
- if ('beta' === $match[1] || 'b' === $match[1]) {
- return 'beta';
- }
- if ('alpha' === $match[1] || 'a' === $match[1]) {
- return 'alpha';
- }
- if ('rc' === $match[1]) {
- return 'RC';
- }
- }
-
- return 'stable';
- }
-
- /**
- * @param string $stability
- *
- * @return string
- */
- public static function normalizeStability($stability)
- {
- $stability = strtolower((string) $stability);
-
- return $stability === 'rc' ? 'RC' : $stability;
- }
-
- /**
- * Normalizes a version string to be able to perform comparisons on it.
- *
- * @param string $version
- * @param ?string $fullVersion optional complete version string to give more context
- *
- * @throws \UnexpectedValueException
- *
- * @return string
- */
- public function normalize($version, $fullVersion = null)
- {
- $version = trim((string) $version);
- $origVersion = $version;
- if (null === $fullVersion) {
- $fullVersion = $version;
- }
-
- // strip off aliasing
- if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $version, $match)) {
- $version = $match[1];
- }
-
- // strip off stability flag
- if (preg_match('{@(?:' . self::$stabilitiesRegex . ')$}i', $version, $match)) {
- $version = substr($version, 0, strlen($version) - strlen($match[0]));
- }
-
- // normalize master/trunk/default branches to dev-name for BC with 1.x as these used to be valid constraints
- if (\in_array($version, array('master', 'trunk', 'default'), true)) {
- $version = 'dev-' . $version;
- }
-
- // if requirement is branch-like, use full name
- if (stripos($version, 'dev-') === 0) {
- return 'dev-' . substr($version, 4);
- }
-
- // strip off build metadata
- if (preg_match('{^([^,\s+]++)\+[^\s]++$}', $version, $match)) {
- $version = $match[1];
- }
-
- // match classical versioning
- if (preg_match('{^v?(\d{1,5})(\.\d++)?(\.\d++)?(\.\d++)?' . self::$modifierRegex . '$}i', $version, $matches)) {
- $version = $matches[1]
- . (!empty($matches[2]) ? $matches[2] : '.0')
- . (!empty($matches[3]) ? $matches[3] : '.0')
- . (!empty($matches[4]) ? $matches[4] : '.0');
- $index = 5;
- // match date(time) based versioning
- } elseif (preg_match('{^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)' . self::$modifierRegex . '$}i', $version, $matches)) {
- $version = preg_replace('{\D}', '.', $matches[1]);
- $index = 2;
- }
-
- // add version modifiers if a version was matched
- if (isset($index)) {
- if (!empty($matches[$index])) {
- if ('stable' === $matches[$index]) {
- return $version;
- }
- $version .= '-' . $this->expandStability($matches[$index]) . (isset($matches[$index + 1]) && '' !== $matches[$index + 1] ? ltrim($matches[$index + 1], '.-') : '');
- }
-
- if (!empty($matches[$index + 2])) {
- $version .= '-dev';
- }
-
- return $version;
- }
-
- // match dev branches
- if (preg_match('{(.*?)[.-]?dev$}i', $version, $match)) {
- try {
- $normalized = $this->normalizeBranch($match[1]);
- // a branch ending with -dev is only valid if it is numeric
- // if it gets prefixed with dev- it means the branch name should
- // have had a dev- prefix already when passed to normalize
- if (strpos($normalized, 'dev-') === false) {
- return $normalized;
- }
- } catch (\Exception $e) {
- }
- }
-
- $extraMessage = '';
- if (preg_match('{ +as +' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))?$}', $fullVersion)) {
- $extraMessage = ' in "' . $fullVersion . '", the alias must be an exact version';
- } elseif (preg_match('{^' . preg_quote($version) . '(?:@(?:'.self::$stabilitiesRegex.'))? +as +}', $fullVersion)) {
- $extraMessage = ' in "' . $fullVersion . '", the alias source must be an exact version, if it is a branch name you should prefix it with dev-';
- }
-
- throw new \UnexpectedValueException('Invalid version string "' . $origVersion . '"' . $extraMessage);
- }
-
- /**
- * Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison.
- *
- * @param string $branch Branch name (e.g. 2.1.x-dev)
- *
- * @return string|false Numeric prefix if present (e.g. 2.1.) or false
- */
- public function parseNumericAliasPrefix($branch)
- {
- if (preg_match('{^(?P(\d++\\.)*\d++)(?:\.x)?-dev$}i', (string) $branch, $matches)) {
- return $matches['version'] . '.';
- }
-
- return false;
- }
-
- /**
- * Normalizes a branch name to be able to perform comparisons on it.
- *
- * @param string $name
- *
- * @return string
- */
- public function normalizeBranch($name)
- {
- $name = trim((string) $name);
-
- if (preg_match('{^v?(\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?$}i', $name, $matches)) {
- $version = '';
- for ($i = 1; $i < 5; ++$i) {
- $version .= isset($matches[$i]) ? str_replace(array('*', 'X'), 'x', $matches[$i]) : '.x';
- }
-
- return str_replace('x', '9999999', $version) . '-dev';
- }
-
- return 'dev-' . $name;
- }
-
- /**
- * Normalizes a default branch name (i.e. master on git) to 9999999-dev.
- *
- * @param string $name
- *
- * @return string
- *
- * @deprecated No need to use this anymore in theory, Composer 2 does not normalize any branch names to 9999999-dev anymore
- */
- public function normalizeDefaultBranch($name)
- {
- if ($name === 'dev-master' || $name === 'dev-default' || $name === 'dev-trunk') {
- return '9999999-dev';
- }
-
- return (string) $name;
- }
-
- /**
- * Parses a constraint string into MultiConstraint and/or Constraint objects.
- *
- * @param string $constraints
- *
- * @return ConstraintInterface
- */
- public function parseConstraints($constraints)
- {
- $prettyConstraint = (string) $constraints;
-
- $orConstraints = preg_split('{\s*\|\|?\s*}', trim((string) $constraints));
- if (false === $orConstraints) {
- throw new \RuntimeException('Failed to preg_split string: '.$constraints);
- }
- $orGroups = array();
-
- foreach ($orConstraints as $constraints) {
- $andConstraints = preg_split('{(?< ,]) *(? 1) {
- $constraintObjects = array();
- foreach ($andConstraints as $constraint) {
- foreach ($this->parseConstraint($constraint) as $parsedConstraint) {
- $constraintObjects[] = $parsedConstraint;
- }
- }
- } else {
- $constraintObjects = $this->parseConstraint($andConstraints[0]);
- }
-
- if (1 === \count($constraintObjects)) {
- $constraint = $constraintObjects[0];
- } else {
- $constraint = new MultiConstraint($constraintObjects);
- }
-
- $orGroups[] = $constraint;
- }
-
- $constraint = MultiConstraint::create($orGroups, false);
-
- $constraint->setPrettyString($prettyConstraint);
-
- return $constraint;
- }
-
- /**
- * @param string $constraint
- *
- * @throws \UnexpectedValueException
- *
- * @return array
- *
- * @phpstan-return non-empty-array
- */
- private function parseConstraint($constraint)
- {
- // strip off aliasing
- if (preg_match('{^([^,\s]++) ++as ++([^,\s]++)$}', $constraint, $match)) {
- $constraint = $match[1];
- }
-
- // strip @stability flags, and keep it for later use
- if (preg_match('{^([^,\s]*?)@(' . self::$stabilitiesRegex . ')$}i', $constraint, $match)) {
- $constraint = '' !== $match[1] ? $match[1] : '*';
- if ($match[2] !== 'stable') {
- $stabilityModifier = $match[2];
- }
- }
-
- // get rid of #refs as those are used by composer only
- if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$}i', $constraint, $match)) {
- $constraint = $match[1];
- }
-
- if (preg_match('{^(v)?[xX*](\.[xX*])*$}i', $constraint, $match)) {
- if (!empty($match[1]) || !empty($match[2])) {
- return array(new Constraint('>=', '0.0.0.0-dev'));
- }
-
- return array(new MatchAllConstraint());
- }
-
- $versionRegex = 'v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.(\d++))?(?:' . self::$modifierRegex . '|\.([xX*][.-]?dev))(?:\+[^\s]+)?';
-
- // Tilde Range
- //
- // Like wildcard constraints, unsuffixed tilde constraints say that they must be greater than the previous
- // version, to ensure that unstable instances of the current version are allowed. However, if a stability
- // suffix is added to the constraint, then a >= match on the current version is used instead.
- if (preg_match('{^~>?' . $versionRegex . '$}i', $constraint, $matches)) {
- if (strpos($constraint, '~>') === 0) {
- throw new \UnexpectedValueException(
- 'Could not parse version constraint ' . $constraint . ': ' .
- 'Invalid operator "~>", you probably meant to use the "~" operator'
- );
- }
-
- // Work out which position in the version we are operating at
- if (isset($matches[4]) && '' !== $matches[4] && null !== $matches[4]) {
- $position = 4;
- } elseif (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
- $position = 3;
- } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
- $position = 2;
- } else {
- $position = 1;
- }
-
- // when matching 2.x-dev or 3.0.x-dev we have to shift the second or third number, despite no second/third number matching above
- if (!empty($matches[8])) {
- $position++;
- }
-
- // Calculate the stability suffix
- $stabilitySuffix = '';
- if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
- $stabilitySuffix .= '-dev';
- }
-
- $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
- $lowerBound = new Constraint('>=', $lowVersion);
-
- // For upper bound, we increment the position of one more significance,
- // but highPosition = 0 would be illegal
- $highPosition = max(1, $position - 1);
- $highVersion = $this->manipulateVersionString($matches, $highPosition, 1) . '-dev';
- $upperBound = new Constraint('<', $highVersion);
-
- return array(
- $lowerBound,
- $upperBound,
- );
- }
-
- // Caret Range
- //
- // Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple.
- // In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for
- // versions 0.X >=0.1.0, and no updates for versions 0.0.X
- if (preg_match('{^\^' . $versionRegex . '($)}i', $constraint, $matches)) {
- // Work out which position in the version we are operating at
- if ('0' !== $matches[1] || '' === $matches[2] || null === $matches[2]) {
- $position = 1;
- } elseif ('0' !== $matches[2] || '' === $matches[3] || null === $matches[3]) {
- $position = 2;
- } else {
- $position = 3;
- }
-
- // Calculate the stability suffix
- $stabilitySuffix = '';
- if (empty($matches[5]) && empty($matches[7]) && empty($matches[8])) {
- $stabilitySuffix .= '-dev';
- }
-
- $lowVersion = $this->normalize(substr($constraint . $stabilitySuffix, 1));
- $lowerBound = new Constraint('>=', $lowVersion);
-
- // For upper bound, we increment the position of one more significance,
- // but highPosition = 0 would be illegal
- $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
- $upperBound = new Constraint('<', $highVersion);
-
- return array(
- $lowerBound,
- $upperBound,
- );
- }
-
- // X Range
- //
- // Any of X, x, or * may be used to "stand in" for one of the numeric values in the [major, minor, patch] tuple.
- // A partial version range is treated as an X-Range, so the special character is in fact optional.
- if (preg_match('{^v?(\d++)(?:\.(\d++))?(?:\.(\d++))?(?:\.[xX*])++$}', $constraint, $matches)) {
- if (isset($matches[3]) && '' !== $matches[3] && null !== $matches[3]) {
- $position = 3;
- } elseif (isset($matches[2]) && '' !== $matches[2] && null !== $matches[2]) {
- $position = 2;
- } else {
- $position = 1;
- }
-
- $lowVersion = $this->manipulateVersionString($matches, $position) . '-dev';
- $highVersion = $this->manipulateVersionString($matches, $position, 1) . '-dev';
-
- if ($lowVersion === '0.0.0.0-dev') {
- return array(new Constraint('<', $highVersion));
- }
-
- return array(
- new Constraint('>=', $lowVersion),
- new Constraint('<', $highVersion),
- );
- }
-
- // Hyphen Range
- //
- // Specifies an inclusive set. If a partial version is provided as the first version in the inclusive range,
- // then the missing pieces are replaced with zeroes. If a partial version is provided as the second version in
- // the inclusive range, then all versions that start with the supplied parts of the tuple are accepted, but
- // nothing that would be greater than the provided tuple parts.
- if (preg_match('{^(?P' . $versionRegex . ') +- +(?P' . $versionRegex . ')($)}i', $constraint, $matches)) {
- // Calculate the stability suffix
- $lowStabilitySuffix = '';
- if (empty($matches[6]) && empty($matches[8]) && empty($matches[9])) {
- $lowStabilitySuffix = '-dev';
- }
-
- $lowVersion = $this->normalize($matches['from']);
- $lowerBound = new Constraint('>=', $lowVersion . $lowStabilitySuffix);
-
- $empty = function ($x) {
- return ($x === 0 || $x === '0') ? false : empty($x);
- };
-
- if ((!$empty($matches[12]) && !$empty($matches[13])) || !empty($matches[15]) || !empty($matches[17]) || !empty($matches[18])) {
- $highVersion = $this->normalize($matches['to']);
- $upperBound = new Constraint('<=', $highVersion);
- } else {
- $highMatch = array('', $matches[11], $matches[12], $matches[13], $matches[14]);
-
- // validate to version
- $this->normalize($matches['to']);
-
- $highVersion = $this->manipulateVersionString($highMatch, $empty($matches[12]) ? 1 : 2, 1) . '-dev';
- $upperBound = new Constraint('<', $highVersion);
- }
-
- return array(
- $lowerBound,
- $upperBound,
- );
- }
-
- // Basic Comparators
- if (preg_match('{^(<>|!=|>=?|<=?|==?)?\s*(.*)}', $constraint, $matches)) {
- try {
- try {
- $version = $this->normalize($matches[2]);
- } catch (\UnexpectedValueException $e) {
- // recover from an invalid constraint like foobar-dev which should be dev-foobar
- // except if the constraint uses a known operator, in which case it must be a parse error
- if (substr($matches[2], -4) === '-dev' && preg_match('{^[0-9a-zA-Z-./]+$}', $matches[2])) {
- $version = $this->normalize('dev-'.substr($matches[2], 0, -4));
- } else {
- throw $e;
- }
- }
-
- $op = $matches[1] ?: '=';
-
- if ($op !== '==' && $op !== '=' && !empty($stabilityModifier) && self::parseStability($version) === 'stable') {
- $version .= '-' . $stabilityModifier;
- } elseif ('<' === $op || '>=' === $op) {
- if (!preg_match('/-' . self::$modifierRegex . '$/', strtolower($matches[2]))) {
- if (strpos($matches[2], 'dev-') !== 0) {
- $version .= '-dev';
- }
- }
- }
-
- return array(new Constraint($matches[1] ?: '=', $version));
- } catch (\Exception $e) {
- }
- }
-
- $message = 'Could not parse version constraint ' . $constraint;
- if (isset($e)) {
- $message .= ': ' . $e->getMessage();
- }
-
- throw new \UnexpectedValueException($message);
- }
-
- /**
- * Increment, decrement, or simply pad a version number.
- *
- * Support function for {@link parseConstraint()}
- *
- * @param array $matches Array with version parts in array indexes 1,2,3,4
- * @param int $position 1,2,3,4 - which segment of the version to increment/decrement
- * @param int $increment
- * @param string $pad The string to pad version parts after $position
- *
- * @return string|null The new version
- *
- * @phpstan-param string[] $matches
- */
- private function manipulateVersionString(array $matches, $position, $increment = 0, $pad = '0')
- {
- for ($i = 4; $i > 0; --$i) {
- if ($i > $position) {
- $matches[$i] = $pad;
- } elseif ($i === $position && $increment) {
- $matches[$i] += $increment;
- // If $matches[$i] was 0, carry the decrement
- if ($matches[$i] < 0) {
- $matches[$i] = $pad;
- --$position;
-
- // Return null on a carry overflow
- if ($i === 1) {
- return null;
- }
- }
- }
- }
-
- return $matches[1] . '.' . $matches[2] . '.' . $matches[3] . '.' . $matches[4];
- }
-
- /**
- * Expand shorthand stability string to long version.
- *
- * @param string $stability
- *
- * @return string
- */
- private function expandStability($stability)
- {
- $stability = strtolower($stability);
-
- switch ($stability) {
- case 'a':
- return 'alpha';
- case 'b':
- return 'beta';
- case 'p':
- case 'pl':
- return 'patch';
- case 'rc':
- return 'RC';
- default:
- return $stability;
- }
- }
-}
diff --git a/vendor/composer/xdebug-handler/CHANGELOG.md b/vendor/composer/xdebug-handler/CHANGELOG.md
deleted file mode 100644
index c5b5bcf4..00000000
--- a/vendor/composer/xdebug-handler/CHANGELOG.md
+++ /dev/null
@@ -1,134 +0,0 @@
-## [Unreleased]
-
-## [3.0.3] - 2022-02-25
- * Added: support for composer/pcre versions 2 and 3.
-
-## [3.0.2] - 2022-02-24
- * Fixed: regression in 3.0.1 affecting Xdebug 2
-
-## [3.0.1] - 2022-01-04
- * Fixed: error when calling `isXdebugActive` before class instantiation.
-
-## [3.0.0] - 2021-12-23
- * Removed: support for legacy PHP versions (< PHP 7.2.5).
- * Added: type declarations to arguments and return values.
- * Added: strict typing to all classes.
-
-## [2.0.3] - 2021-12-08
- * Added: support, type annotations and refactoring for stricter PHPStan analysis.
-
-## [2.0.2] - 2021-07-31
- * Added: support for `xdebug_info('mode')` in Xdebug 3.1.
- * Added: support for Psr\Log versions 2 and 3.
- * Fixed: remove ini directives from non-cli HOST/PATH sections.
-
-## [2.0.1] - 2021-05-05
- * Fixed: don't restart if the cwd is a UNC path and cmd.exe will be invoked.
-
-## [2.0.0] - 2021-04-09
- * Break: this is a major release, see [UPGRADE.md](UPGRADE.md) for more information.
- * Break: removed optional `$colorOption` constructor param and passthru fallback.
- * Break: renamed `requiresRestart` param from `$isLoaded` to `$default`.
- * Break: changed `restart` param `$command` from a string to an array.
- * Added: support for Xdebug3 to only restart if Xdebug is not running with `xdebug.mode=off`.
- * Added: `isXdebugActive()` method to determine if Xdebug is still running in the restart.
- * Added: feature to bypass the shell in PHP-7.4+ by giving `proc_open` an array of arguments.
- * Added: Process utility class to the API.
-
-## [1.4.6] - 2021-03-25
- * Fixed: fail restart if `proc_open` has been disabled in `disable_functions`.
- * Fixed: enable Windows CTRL event handling in the restarted process.
-
-## [1.4.5] - 2020-11-13
- * Fixed: use `proc_open` when available for correct FD forwarding to the restarted process.
-
-## [1.4.4] - 2020-10-24
- * Fixed: exception if 'pcntl_signal' is disabled.
-
-## [1.4.3] - 2020-08-19
- * Fixed: restore SIGINT to default handler in restarted process if no other handler exists.
-
-## [1.4.2] - 2020-06-04
- * Fixed: ignore SIGINTs to let the restarted process handle them.
-
-## [1.4.1] - 2020-03-01
- * Fixed: restart fails if an ini file is empty.
-
-## [1.4.0] - 2019-11-06
- * Added: support for `NO_COLOR` environment variable: https://no-color.org
- * Added: color support for Hyper terminal: https://github.com/zeit/hyper
- * Fixed: correct capitalization of Xdebug (apparently).
- * Fixed: improved handling for uopz extension.
-
-## [1.3.3] - 2019-05-27
- * Fixed: add environment changes to `$_ENV` if it is being used.
-
-## [1.3.2] - 2019-01-28
- * Fixed: exit call being blocked by uopz extension, resulting in application code running twice.
-
-## [1.3.1] - 2018-11-29
- * Fixed: fail restart if `passthru` has been disabled in `disable_functions`.
- * Fixed: fail restart if an ini file cannot be opened, otherwise settings will be missing.
-
-## [1.3.0] - 2018-08-31
- * Added: `setPersistent` method to use environment variables for the restart.
- * Fixed: improved debugging by writing output to stderr.
- * Fixed: no restart when `php_ini_scanned_files` is not functional and is needed.
-
-## [1.2.1] - 2018-08-23
- * Fixed: fatal error with apc, when using `apc.mmap_file_mask`.
-
-## [1.2.0] - 2018-08-16
- * Added: debug information using `XDEBUG_HANDLER_DEBUG`.
- * Added: fluent interface for setters.
- * Added: `PhpConfig` helper class for calling PHP sub-processes.
- * Added: `PHPRC` original value to restart stettings, for use in a restarted process.
- * Changed: internal procedure to disable ini-scanning, using `-n` command-line option.
- * Fixed: replaced `escapeshellarg` usage to avoid locale problems.
- * Fixed: improved color-option handling to respect double-dash delimiter.
- * Fixed: color-option handling regression from main script changes.
- * Fixed: improved handling when checking main script.
- * Fixed: handling for standard input, that never actually did anything.
- * Fixed: fatal error when ctype extension is not available.
-
-## [1.1.0] - 2018-04-11
- * Added: `getRestartSettings` method for calling PHP processes in a restarted process.
- * Added: API definition and @internal class annotations.
- * Added: protected `requiresRestart` method for extending classes.
- * Added: `setMainScript` method for applications that change the working directory.
- * Changed: private `tmpIni` variable to protected for extending classes.
- * Fixed: environment variables not available in $_SERVER when restored in the restart.
- * Fixed: relative path problems caused by Phar::interceptFileFuncs.
- * Fixed: incorrect handling when script file cannot be found.
-
-## [1.0.0] - 2018-03-08
- * Added: PSR3 logging for optional status output.
- * Added: existing ini settings are merged to catch command-line overrides.
- * Added: code, tests and other artefacts to decouple from Composer.
- * Break: the following class was renamed:
- - `Composer\XdebugHandler` -> `Composer\XdebugHandler\XdebugHandler`
-
-[Unreleased]: https://github.com/composer/xdebug-handler/compare/3.0.3...HEAD
-[3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.2...3.0.3
-[3.0.2]: https://github.com/composer/xdebug-handler/compare/3.0.1...3.0.2
-[3.0.1]: https://github.com/composer/xdebug-handler/compare/3.0.0...3.0.1
-[3.0.0]: https://github.com/composer/xdebug-handler/compare/2.0.3...3.0.0
-[2.0.3]: https://github.com/composer/xdebug-handler/compare/2.0.2...2.0.3
-[2.0.2]: https://github.com/composer/xdebug-handler/compare/2.0.1...2.0.2
-[2.0.1]: https://github.com/composer/xdebug-handler/compare/2.0.0...2.0.1
-[2.0.0]: https://github.com/composer/xdebug-handler/compare/1.4.6...2.0.0
-[1.4.6]: https://github.com/composer/xdebug-handler/compare/1.4.5...1.4.6
-[1.4.5]: https://github.com/composer/xdebug-handler/compare/1.4.4...1.4.5
-[1.4.4]: https://github.com/composer/xdebug-handler/compare/1.4.3...1.4.4
-[1.4.3]: https://github.com/composer/xdebug-handler/compare/1.4.2...1.4.3
-[1.4.2]: https://github.com/composer/xdebug-handler/compare/1.4.1...1.4.2
-[1.4.1]: https://github.com/composer/xdebug-handler/compare/1.4.0...1.4.1
-[1.4.0]: https://github.com/composer/xdebug-handler/compare/1.3.3...1.4.0
-[1.3.3]: https://github.com/composer/xdebug-handler/compare/1.3.2...1.3.3
-[1.3.2]: https://github.com/composer/xdebug-handler/compare/1.3.1...1.3.2
-[1.3.1]: https://github.com/composer/xdebug-handler/compare/1.3.0...1.3.1
-[1.3.0]: https://github.com/composer/xdebug-handler/compare/1.2.1...1.3.0
-[1.2.1]: https://github.com/composer/xdebug-handler/compare/1.2.0...1.2.1
-[1.2.0]: https://github.com/composer/xdebug-handler/compare/1.1.0...1.2.0
-[1.1.0]: https://github.com/composer/xdebug-handler/compare/1.0.0...1.1.0
-[1.0.0]: https://github.com/composer/xdebug-handler/compare/d66f0d15cb57...1.0.0
diff --git a/vendor/composer/xdebug-handler/LICENSE b/vendor/composer/xdebug-handler/LICENSE
deleted file mode 100644
index 963618a1..00000000
--- a/vendor/composer/xdebug-handler/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 Composer
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/composer/xdebug-handler/README.md b/vendor/composer/xdebug-handler/README.md
deleted file mode 100644
index 56618fc1..00000000
--- a/vendor/composer/xdebug-handler/README.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# composer/xdebug-handler
-
-[](https://packagist.org/packages/composer/xdebug-handler)
-[](https://github.com/composer/xdebug-handler/actions?query=branch:main)
-
-
-
-Restart a CLI process without loading the Xdebug extension, unless `xdebug.mode=off`.
-
-Originally written as part of [composer/composer](https://github.com/composer/composer),
-now extracted and made available as a stand-alone library.
-
-### Version 3
-
-Removed support for legacy PHP versions and added type declarations.
-
-Long term support for version 2 (PHP 5.3.2 - 7.2.4) follows [Composer 2.2 LTS](https://blog.packagist.com/composer-2-2/) policy.
-
-## Installation
-
-Install the latest version with:
-
-```bash
-$ composer require composer/xdebug-handler
-```
-
-## Requirements
-
-* PHP 7.2.5 minimum, although using the latest PHP version is highly recommended.
-
-## Basic Usage
-```php
-use Composer\XdebugHandler\XdebugHandler;
-
-$xdebug = new XdebugHandler('myapp');
-$xdebug->check();
-unset($xdebug);
-```
-
-The constructor takes a single parameter, `$envPrefix`, which is upper-cased and prepended to default base values to create two distinct environment variables. The above example enables the use of:
-
-- `MYAPP_ALLOW_XDEBUG=1` to override automatic restart and allow Xdebug
-- `MYAPP_ORIGINAL_INIS` to obtain ini file locations in a restarted process
-
-## Advanced Usage
-
-* [How it works](#how-it-works)
-* [Limitations](#limitations)
-* [Helper methods](#helper-methods)
-* [Setter methods](#setter-methods)
-* [Process configuration](#process-configuration)
-* [Troubleshooting](#troubleshooting)
-* [Extending the library](#extending-the-library)
-
-### How it works
-
-A temporary ini file is created from the loaded (and scanned) ini files, with any references to the Xdebug extension commented out. Current ini settings are merged, so that most ini settings made on the command-line or by the application are included (see [Limitations](#limitations))
-
-* `MYAPP_ALLOW_XDEBUG` is set with internal data to flag and use in the restart.
-* The command-line and environment are [configured](#process-configuration) for the restart.
-* The application is restarted in a new process.
- * The restart settings are stored in the environment.
- * `MYAPP_ALLOW_XDEBUG` is unset.
- * The application runs and exits.
-* The main process exits with the exit code from the restarted process.
-
-#### Signal handling
-Asynchronous signal handling is automatically enabled if the pcntl extension is loaded. `SIGINT` is set to `SIG_IGN` in the parent
-process and restored to `SIG_DFL` in the restarted process (if no other handler has been set).
-
-From PHP 7.4 on Windows, `CTRL+C` and `CTRL+BREAK` handling is automatically enabled in the restarted process and ignored in the parent process.
-
-### Limitations
-There are a few things to be aware of when running inside a restarted process.
-
-* Extensions set on the command-line will not be loaded.
-* Ini file locations will be reported as per the restart - see [getAllIniFiles()](#getallinifiles).
-* Php sub-processes may be loaded with Xdebug enabled - see [Process configuration](#process-configuration).
-
-### Helper methods
-These static methods provide information from the current process, regardless of whether it has been restarted or not.
-
-#### _getAllIniFiles(): array_
-Returns an array of the original ini file locations. Use this instead of calling `php_ini_loaded_file` and `php_ini_scanned_files`, which will report the wrong values in a restarted process.
-
-```php
-use Composer\XdebugHandler\XdebugHandler;
-
-$files = XdebugHandler::getAllIniFiles();
-
-# $files[0] always exists, it could be an empty string
-$loadedIni = array_shift($files);
-$scannedInis = $files;
-```
-
-These locations are also available in the `MYAPP_ORIGINAL_INIS` environment variable. This is a path-separated string comprising the location returned from `php_ini_loaded_file`, which could be empty, followed by locations parsed from calling `php_ini_scanned_files`.
-
-#### _getRestartSettings(): ?array_
-Returns an array of settings that can be used with PHP [sub-processes](#sub-processes), or null if the process was not restarted.
-
-```php
-use Composer\XdebugHandler\XdebugHandler;
-
-$settings = XdebugHandler::getRestartSettings();
-/**
- * $settings: array (if the current process was restarted,
- * or called with the settings from a previous restart), or null
- *
- * 'tmpIni' => the temporary ini file used in the restart (string)
- * 'scannedInis' => if there were any scanned inis (bool)
- * 'scanDir' => the original PHP_INI_SCAN_DIR value (false|string)
- * 'phprc' => the original PHPRC value (false|string)
- * 'inis' => the original inis from getAllIniFiles (array)
- * 'skipped' => the skipped version from getSkippedVersion (string)
- */
-```
-
-#### _getSkippedVersion(): string_
-Returns the Xdebug version string that was skipped by the restart, or an empty string if there was no restart (or Xdebug is still loaded, perhaps by an extending class restarting for a reason other than removing Xdebug).
-
-```php
-use Composer\XdebugHandler\XdebugHandler;
-
-$version = XdebugHandler::getSkippedVersion();
-# $version: '3.1.1' (for example), or an empty string
-```
-
-#### _isXdebugActive(): bool_
-Returns true if Xdebug is loaded and is running in an active mode (if it supports modes). Returns false if Xdebug is not loaded, or it is running with `xdebug.mode=off`.
-
-### Setter methods
-These methods implement a fluent interface and must be called before the main `check()` method.
-
-#### _setLogger(LoggerInterface $logger): self_
-Enables the output of status messages to an external PSR3 logger. All messages are reported with either `DEBUG` or `WARNING` log levels. For example (showing the level and message):
-
-```
-// No restart
-DEBUG Checking MYAPP_ALLOW_XDEBUG
-DEBUG The Xdebug extension is loaded (3.1.1) xdebug.mode=off
-DEBUG No restart (APP_ALLOW_XDEBUG=0) Allowed by xdebug.mode
-
-// Restart overridden
-DEBUG Checking MYAPP_ALLOW_XDEBUG
-DEBUG The Xdebug extension is loaded (3.1.1) xdebug.mode=coverage,debug,develop
-DEBUG No restart (MYAPP_ALLOW_XDEBUG=1)
-
-// Failed restart
-DEBUG Checking MYAPP_ALLOW_XDEBUG
-DEBUG The Xdebug extension is loaded (3.1.0)
-WARNING No restart (Unable to create temp ini file at: ...)
-```
-
-Status messages can also be output with `XDEBUG_HANDLER_DEBUG`. See [Troubleshooting](#troubleshooting).
-
-#### _setMainScript(string $script): self_
-Sets the location of the main script to run in the restart. This is only needed in more esoteric use-cases, or if the `argv[0]` location is inaccessible. The script name `--` is supported for standard input.
-
-#### _setPersistent(): self_
-Configures the restart using [persistent settings](#persistent-settings), so that Xdebug is not loaded in any sub-process.
-
-Use this method if your application invokes one or more PHP sub-process and the Xdebug extension is not needed. This avoids the overhead of implementing specific [sub-process](#sub-processes) strategies.
-
-Alternatively, this method can be used to set up a default _Xdebug-free_ environment which can be changed if a sub-process requires Xdebug, then restored afterwards:
-
-```php
-function SubProcessWithXdebug()
-{
- $phpConfig = new Composer\XdebugHandler\PhpConfig();
-
- # Set the environment to the original configuration
- $phpConfig->useOriginal();
-
- # run the process with Xdebug loaded
- ...
-
- # Restore Xdebug-free environment
- $phpConfig->usePersistent();
-}
-```
-
-### Process configuration
-The library offers two strategies to invoke a new PHP process without loading Xdebug, using either _standard_ or _persistent_ settings. Note that this is only important if the application calls a PHP sub-process.
-
-#### Standard settings
-Uses command-line options to remove Xdebug from the new process only.
-
-* The -n option is added to the command-line. This tells PHP not to scan for additional inis.
-* The temporary ini is added to the command-line with the -c option.
-
->_If the new process calls a PHP sub-process, Xdebug will be loaded in that sub-process (unless it implements xdebug-handler, in which case there will be another restart)._
-
-This is the default strategy used in the restart.
-
-#### Persistent settings
-Uses environment variables to remove Xdebug from the new process and persist these settings to any sub-process.
-
-* `PHP_INI_SCAN_DIR` is set to an empty string. This tells PHP not to scan for additional inis.
-* `PHPRC` is set to the temporary ini.
-
->_If the new process calls a PHP sub-process, Xdebug will not be loaded in that sub-process._
-
-This strategy can be used in the restart by calling [setPersistent()](#setpersistent).
-
-#### Sub-processes
-The `PhpConfig` helper class makes it easy to invoke a PHP sub-process (with or without Xdebug loaded), regardless of whether there has been a restart.
-
-Each of its methods returns an array of PHP options (to add to the command-line) and sets up the environment for the required strategy. The [getRestartSettings()](#getrestartsettings) method is used internally.
-
-* `useOriginal()` - Xdebug will be loaded in the new process.
-* `useStandard()` - Xdebug will **not** be loaded in the new process - see [standard settings](#standard-settings).
-* `userPersistent()` - Xdebug will **not** be loaded in the new process - see [persistent settings](#persistent-settings)
-
-If there was no restart, an empty options array is returned and the environment is not changed.
-
-```php
-use Composer\XdebugHandler\PhpConfig;
-
-$config = new PhpConfig;
-
-$options = $config->useOriginal();
-# $options: empty array
-# environment: PHPRC and PHP_INI_SCAN_DIR set to original values
-
-$options = $config->useStandard();
-# $options: [-n, -c, tmpIni]
-# environment: PHPRC and PHP_INI_SCAN_DIR set to original values
-
-$options = $config->usePersistent();
-# $options: empty array
-# environment: PHPRC=tmpIni, PHP_INI_SCAN_DIR=''
-```
-
-### Troubleshooting
-The following environment settings can be used to troubleshoot unexpected behavior:
-
-* `XDEBUG_HANDLER_DEBUG=1` Outputs status messages to `STDERR`, if it is defined, irrespective of any PSR3 logger. Each message is prefixed `xdebug-handler[pid]`, where pid is the process identifier.
-
-* `XDEBUG_HANDLER_DEBUG=2` As above, but additionally saves the temporary ini file and reports its location in a status message.
-
-### Extending the library
-The API is defined by classes and their accessible elements that are not annotated as @internal. The main class has two protected methods that can be overridden to provide additional functionality:
-
-#### _requiresRestart(bool $default): bool_
-By default the process will restart if Xdebug is loaded and not running with `xdebug.mode=off`. Extending this method allows an application to decide, by returning a boolean (or equivalent) value.
-It is only called if `MYAPP_ALLOW_XDEBUG` is empty, so it will not be called in the restarted process (where this variable contains internal data), or if the restart has been overridden.
-
-Note that the [setMainScript()](#setmainscriptscript) and [setPersistent()](#setpersistent) setters can be used here, if required.
-
-#### _restart(array $command): void_
-An application can extend this to modify the temporary ini file, its location given in the `tmpIni` property. New settings can be safely appended to the end of the data, which is `PHP_EOL` terminated.
-
-The `$command` parameter is an array of unescaped command-line arguments that will be used for the new process.
-
-Remember to finish with `parent::restart($command)`.
-
-#### Example
-This example demonstrates two ways to extend basic functionality:
-
-* To avoid the overhead of spinning up a new process, the restart is skipped if a simple help command is requested.
-
-* The application needs write-access to phar files, so it will force a restart if `phar.readonly` is set (regardless of whether Xdebug is loaded) and change this value in the temporary ini file.
-
-```php
-use Composer\XdebugHandler\XdebugHandler;
-use MyApp\Command;
-
-class MyRestarter extends XdebugHandler
-{
- private $required;
-
- protected function requiresRestart(bool $default): bool
- {
- if (Command::isHelp()) {
- # No need to disable Xdebug for this
- return false;
- }
-
- $this->required = (bool) ini_get('phar.readonly');
- return $this->required || $default;
- }
-
- protected function restart(array $command): void
- {
- if ($this->required) {
- # Add required ini setting to tmpIni
- $content = file_get_contents($this->tmpIni);
- $content .= 'phar.readonly=0'.PHP_EOL;
- file_put_contents($this->tmpIni, $content);
- }
-
- parent::restart($command);
- }
-}
-```
-
-## License
-composer/xdebug-handler is licensed under the MIT License, see the LICENSE file for details.
diff --git a/vendor/composer/xdebug-handler/composer.json b/vendor/composer/xdebug-handler/composer.json
deleted file mode 100644
index 6b649dab..00000000
--- a/vendor/composer/xdebug-handler/composer.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "name": "composer/xdebug-handler",
- "description": "Restarts a process without Xdebug.",
- "type": "library",
- "license": "MIT",
- "keywords": [
- "xdebug",
- "performance"
- ],
- "authors": [
- {
- "name": "John Stevenson",
- "email": "john-stevenson@blueyonder.co.uk"
- }
- ],
- "support": {
- "irc": "irc://irc.freenode.org/composer",
- "issues": "https://github.com/composer/xdebug-handler/issues"
- },
- "require": {
- "php": "^7.2.5 || ^8.0",
- "psr/log": "^1 || ^2 || ^3",
- "composer/pcre": "^1 || ^2 || ^3"
- },
- "require-dev": {
- "symfony/phpunit-bridge": "^6.0",
- "phpstan/phpstan": "^1.0",
- "phpstan/phpstan-strict-rules": "^1.1"
- },
- "autoload": {
- "psr-4": {
- "Composer\\XdebugHandler\\": "src"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "Composer\\XdebugHandler\\Tests\\": "tests"
- }
- },
- "scripts": {
- "test": "@php vendor/bin/simple-phpunit",
- "phpstan": "@php vendor/bin/phpstan analyse"
- }
-}
diff --git a/vendor/composer/xdebug-handler/src/PhpConfig.php b/vendor/composer/xdebug-handler/src/PhpConfig.php
deleted file mode 100644
index 7edac888..00000000
--- a/vendor/composer/xdebug-handler/src/PhpConfig.php
+++ /dev/null
@@ -1,91 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-namespace Composer\XdebugHandler;
-
-/**
- * @author John Stevenson
- *
- * @phpstan-type restartData array{tmpIni: string, scannedInis: bool, scanDir: false|string, phprc: false|string, inis: string[], skipped: string}
- */
-class PhpConfig
-{
- /**
- * Use the original PHP configuration
- *
- * @return string[] Empty array of PHP cli options
- */
- public function useOriginal(): array
- {
- $this->getDataAndReset();
- return [];
- }
-
- /**
- * Use standard restart settings
- *
- * @return string[] PHP cli options
- */
- public function useStandard(): array
- {
- $data = $this->getDataAndReset();
- if ($data !== null) {
- return ['-n', '-c', $data['tmpIni']];
- }
-
- return [];
- }
-
- /**
- * Use environment variables to persist settings
- *
- * @return string[] Empty array of PHP cli options
- */
- public function usePersistent(): array
- {
- $data = $this->getDataAndReset();
- if ($data !== null) {
- $this->updateEnv('PHPRC', $data['tmpIni']);
- $this->updateEnv('PHP_INI_SCAN_DIR', '');
- }
-
- return [];
- }
-
- /**
- * Returns restart data if available and resets the environment
- *
- * @phpstan-return restartData|null
- */
- private function getDataAndReset(): ?array
- {
- $data = XdebugHandler::getRestartSettings();
- if ($data !== null) {
- $this->updateEnv('PHPRC', $data['phprc']);
- $this->updateEnv('PHP_INI_SCAN_DIR', $data['scanDir']);
- }
-
- return $data;
- }
-
- /**
- * Updates a restart settings value in the environment
- *
- * @param string $name
- * @param string|false $value
- */
- private function updateEnv(string $name, $value): void
- {
- Process::setEnv($name, false !== $value ? $value : null);
- }
-}
diff --git a/vendor/composer/xdebug-handler/src/Process.php b/vendor/composer/xdebug-handler/src/Process.php
deleted file mode 100644
index c612200b..00000000
--- a/vendor/composer/xdebug-handler/src/Process.php
+++ /dev/null
@@ -1,118 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-declare(strict_types=1);
-
-namespace Composer\XdebugHandler;
-
-use Composer\Pcre\Preg;
-
-/**
- * Process utility functions
- *
- * @author John Stevenson
- */
-class Process
-{
- /**
- * Escapes a string to be used as a shell argument.
- *
- * From https://github.com/johnstevenson/winbox-args
- * MIT Licensed (c) John Stevenson
- *
- * @param string $arg The argument to be escaped
- * @param bool $meta Additionally escape cmd.exe meta characters
- * @param bool $module The argument is the module to invoke
- */
- public static function escape(string $arg, bool $meta = true, bool $module = false): string
- {
- if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
- return "'".str_replace("'", "'\\''", $arg)."'";
- }
-
- $quote = strpbrk($arg, " \t") !== false || $arg === '';
-
- $arg = Preg::replace('/(\\\\*)"/', '$1$1\\"', $arg, -1, $dquotes);
-
- if ($meta) {
- $meta = $dquotes || Preg::isMatch('/%[^%]+%/', $arg);
-
- if (!$meta) {
- $quote = $quote || strpbrk($arg, '^&|<>()') !== false;
- } elseif ($module && !$dquotes && $quote) {
- $meta = false;
- }
- }
-
- if ($quote) {
- $arg = '"'.(Preg::replace('/(\\\\*)$/', '$1$1', $arg)).'"';
- }
-
- if ($meta) {
- $arg = Preg::replace('/(["^&|<>()%])/', '^$1', $arg);
- }
-
- return $arg;
- }
-
- /**
- * Escapes an array of arguments that make up a shell command
- *
- * @param string[] $args Argument list, with the module name first
- */
- public static function escapeShellCommand(array $args): string
- {
- $command = '';
- $module = array_shift($args);
-
- if ($module !== null) {
- $command = self::escape($module, true, true);
-
- foreach ($args as $arg) {
- $command .= ' '.self::escape($arg);
- }
- }
-
- return $command;
- }
-
- /**
- * Makes putenv environment changes available in $_SERVER and $_ENV
- *
- * @param string $name
- * @param ?string $value A null value unsets the variable
- */
- public static function setEnv(string $name, ?string $value = null): bool
- {
- $unset = null === $value;
-
- if (!putenv($unset ? $name : $name.'='.$value)) {
- return false;
- }
-
- if ($unset) {
- unset($_SERVER[$name]);
- } else {
- $_SERVER[$name] = $value;
- }
-
- // Update $_ENV if it is being used
- if (false !== stripos((string) ini_get('variables_order'), 'E')) {
- if ($unset) {
- unset($_ENV[$name]);
- } else {
- $_ENV[$name] = $value;
- }
- }
-
- return true;
- }
-}
diff --git a/vendor/composer/xdebug-handler/src/Status.php b/vendor/composer/xdebug-handler/src/Status.php
deleted file mode 100644
index b434f859..00000000
--- a/vendor/composer/xdebug-handler/src/Status.php
+++ /dev/null
@@ -1,203 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-declare(strict_types=1);
-
-namespace Composer\XdebugHandler;
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\LogLevel;
-
-/**
- * @author John Stevenson
- * @internal
- */
-class Status
-{
- const ENV_RESTART = 'XDEBUG_HANDLER_RESTART';
- const CHECK = 'Check';
- const ERROR = 'Error';
- const INFO = 'Info';
- const NORESTART = 'NoRestart';
- const RESTART = 'Restart';
- const RESTARTING = 'Restarting';
- const RESTARTED = 'Restarted';
-
- /** @var bool */
- private $debug;
-
- /** @var string */
- private $envAllowXdebug;
-
- /** @var string|null */
- private $loaded;
-
- /** @var LoggerInterface|null */
- private $logger;
-
- /** @var bool */
- private $modeOff;
-
- /** @var float */
- private $time;
-
- /**
- * @param string $envAllowXdebug Prefixed _ALLOW_XDEBUG name
- * @param bool $debug Whether debug output is required
- */
- public function __construct(string $envAllowXdebug, bool $debug)
- {
- $start = getenv(self::ENV_RESTART);
- Process::setEnv(self::ENV_RESTART);
- $this->time = is_numeric($start) ? round((microtime(true) - $start) * 1000) : 0;
-
- $this->envAllowXdebug = $envAllowXdebug;
- $this->debug = $debug && defined('STDERR');
- $this->modeOff = false;
- }
-
- /**
- * Activates status message output to a PSR3 logger
- *
- * @return void
- */
- public function setLogger(LoggerInterface $logger): void
- {
- $this->logger = $logger;
- }
-
- /**
- * Calls a handler method to report a message
- *
- * @throws \InvalidArgumentException If $op is not known
- */
- public function report(string $op, ?string $data): void
- {
- if ($this->logger !== null || $this->debug) {
- $callable = [$this, 'report'.$op];
-
- if (!is_callable($callable)) {
- throw new \InvalidArgumentException('Unknown op handler: '.$op);
- }
-
- $params = $data !== null ? [$data] : [];
- call_user_func_array($callable, $params);
- }
- }
-
- /**
- * Outputs a status message
- */
- private function output(string $text, ?string $level = null): void
- {
- if ($this->logger !== null) {
- $this->logger->log($level !== null ? $level: LogLevel::DEBUG, $text);
- }
-
- if ($this->debug) {
- fwrite(STDERR, sprintf('xdebug-handler[%d] %s', getmypid(), $text.PHP_EOL));
- }
- }
-
- /**
- * Checking status message
- */
- private function reportCheck(string $loaded): void
- {
- list($version, $mode) = explode('|', $loaded);
-
- if ($version !== '') {
- $this->loaded = '('.$version.')'.($mode !== '' ? ' xdebug.mode='.$mode : '');
- }
- $this->modeOff = $mode === 'off';
- $this->output('Checking '.$this->envAllowXdebug);
- }
-
- /**
- * Error status message
- */
- private function reportError(string $error): void
- {
- $this->output(sprintf('No restart (%s)', $error), LogLevel::WARNING);
- }
-
- /**
- * Info status message
- */
- private function reportInfo(string $info): void
- {
- $this->output($info);
- }
-
- /**
- * No restart status message
- */
- private function reportNoRestart(): void
- {
- $this->output($this->getLoadedMessage());
-
- if ($this->loaded !== null) {
- $text = sprintf('No restart (%s)', $this->getEnvAllow());
- if (!((bool) getenv($this->envAllowXdebug))) {
- $text .= ' Allowed by '.($this->modeOff ? 'xdebug.mode' : 'application');
- }
- $this->output($text);
- }
- }
-
- /**
- * Restart status message
- */
- private function reportRestart(): void
- {
- $this->output($this->getLoadedMessage());
- Process::setEnv(self::ENV_RESTART, (string) microtime(true));
- }
-
- /**
- * Restarted status message
- */
- private function reportRestarted(): void
- {
- $loaded = $this->getLoadedMessage();
- $text = sprintf('Restarted (%d ms). %s', $this->time, $loaded);
- $level = $this->loaded !== null ? LogLevel::WARNING : null;
- $this->output($text, $level);
- }
-
- /**
- * Restarting status message
- */
- private function reportRestarting(string $command): void
- {
- $text = sprintf('Process restarting (%s)', $this->getEnvAllow());
- $this->output($text);
- $text = 'Running '.$command;
- $this->output($text);
- }
-
- /**
- * Returns the _ALLOW_XDEBUG environment variable as name=value
- */
- private function getEnvAllow(): string
- {
- return $this->envAllowXdebug.'='.getenv($this->envAllowXdebug);
- }
-
- /**
- * Returns the Xdebug status and version
- */
- private function getLoadedMessage(): string
- {
- $loaded = $this->loaded !== null ? sprintf('loaded %s', $this->loaded) : 'not loaded';
- return 'The Xdebug extension is '.$loaded;
- }
-}
diff --git a/vendor/composer/xdebug-handler/src/XdebugHandler.php b/vendor/composer/xdebug-handler/src/XdebugHandler.php
deleted file mode 100644
index 9052bfa4..00000000
--- a/vendor/composer/xdebug-handler/src/XdebugHandler.php
+++ /dev/null
@@ -1,668 +0,0 @@
-
- *
- * For the full copyright and license information, please view
- * the LICENSE file that was distributed with this source code.
- */
-
-declare(strict_types=1);
-
-namespace Composer\XdebugHandler;
-
-use Composer\Pcre\Preg;
-use Psr\Log\LoggerInterface;
-
-/**
- * @author John Stevenson
- *
- * @phpstan-import-type restartData from PhpConfig
- */
-class XdebugHandler
-{
- const SUFFIX_ALLOW = '_ALLOW_XDEBUG';
- const SUFFIX_INIS = '_ORIGINAL_INIS';
- const RESTART_ID = 'internal';
- const RESTART_SETTINGS = 'XDEBUG_HANDLER_SETTINGS';
- const DEBUG = 'XDEBUG_HANDLER_DEBUG';
-
- /** @var string|null */
- protected $tmpIni;
-
- /** @var bool */
- private static $inRestart;
-
- /** @var string */
- private static $name;
-
- /** @var string|null */
- private static $skipped;
-
- /** @var bool */
- private static $xdebugActive;
-
- /** @var string|null */
- private static $xdebugMode;
-
- /** @var string|null */
- private static $xdebugVersion;
-
- /** @var bool */
- private $cli;
-
- /** @var string|null */
- private $debug;
-
- /** @var string */
- private $envAllowXdebug;
-
- /** @var string */
- private $envOriginalInis;
-
- /** @var bool */
- private $persistent;
-
- /** @var string|null */
- private $script;
-
- /** @var Status */
- private $statusWriter;
-
- /**
- * Constructor
- *
- * The $envPrefix is used to create distinct environment variables. It is
- * uppercased and prepended to the default base values. For example 'myapp'
- * would result in MYAPP_ALLOW_XDEBUG and MYAPP_ORIGINAL_INIS.
- *
- * @param string $envPrefix Value used in environment variables
- * @throws \RuntimeException If the parameter is invalid
- */
- public function __construct(string $envPrefix)
- {
- if ($envPrefix === '') {
- throw new \RuntimeException('Invalid constructor parameter');
- }
-
- self::$name = strtoupper($envPrefix);
- $this->envAllowXdebug = self::$name.self::SUFFIX_ALLOW;
- $this->envOriginalInis = self::$name.self::SUFFIX_INIS;
-
- self::setXdebugDetails();
- self::$inRestart = false;
-
- if ($this->cli = PHP_SAPI === 'cli') {
- $this->debug = (string) getenv(self::DEBUG);
- }
-
- $this->statusWriter = new Status($this->envAllowXdebug, (bool) $this->debug);
- }
-
- /**
- * Activates status message output to a PSR3 logger
- */
- public function setLogger(LoggerInterface $logger): self
- {
- $this->statusWriter->setLogger($logger);
- return $this;
- }
-
- /**
- * Sets the main script location if it cannot be called from argv
- */
- public function setMainScript(string $script): self
- {
- $this->script = $script;
- return $this;
- }
-
- /**
- * Persist the settings to keep Xdebug out of sub-processes
- */
- public function setPersistent(): self
- {
- $this->persistent = true;
- return $this;
- }
-
- /**
- * Checks if Xdebug is loaded and the process needs to be restarted
- *
- * This behaviour can be disabled by setting the MYAPP_ALLOW_XDEBUG
- * environment variable to 1. This variable is used internally so that
- * the restarted process is created only once.
- */
- public function check(): void
- {
- $this->notify(Status::CHECK, self::$xdebugVersion.'|'.self::$xdebugMode);
- $envArgs = explode('|', (string) getenv($this->envAllowXdebug));
-
- if (!((bool) $envArgs[0]) && $this->requiresRestart(self::$xdebugActive)) {
- // Restart required
- $this->notify(Status::RESTART);
-
- if ($this->prepareRestart()) {
- $command = $this->getCommand();
- $this->restart($command);
- }
- return;
- }
-
- if (self::RESTART_ID === $envArgs[0] && count($envArgs) === 5) {
- // Restarted, so unset environment variable and use saved values
- $this->notify(Status::RESTARTED);
-
- Process::setEnv($this->envAllowXdebug);
- self::$inRestart = true;
-
- if (self::$xdebugVersion === null) {
- // Skipped version is only set if Xdebug is not loaded
- self::$skipped = $envArgs[1];
- }
-
- $this->tryEnableSignals();
-
- // Put restart settings in the environment
- $this->setEnvRestartSettings($envArgs);
- return;
- }
-
- $this->notify(Status::NORESTART);
- $settings = self::getRestartSettings();
-
- if ($settings !== null) {
- // Called with existing settings, so sync our settings
- $this->syncSettings($settings);
- }
- }
-
- /**
- * Returns an array of php.ini locations with at least one entry
- *
- * The equivalent of calling php_ini_loaded_file then php_ini_scanned_files.
- * The loaded ini location is the first entry and may be empty.
- *
- * @return string[]
- */
- public static function getAllIniFiles(): array
- {
- if (self::$name !== null) {
- $env = getenv(self::$name.self::SUFFIX_INIS);
-
- if (false !== $env) {
- return explode(PATH_SEPARATOR, $env);
- }
- }
-
- $paths = [(string) php_ini_loaded_file()];
- $scanned = php_ini_scanned_files();
-
- if ($scanned !== false) {
- $paths = array_merge($paths, array_map('trim', explode(',', $scanned)));
- }
-
- return $paths;
- }
-
- /**
- * Returns an array of restart settings or null
- *
- * Settings will be available if the current process was restarted, or
- * called with the settings from an existing restart.
- *
- * @phpstan-return restartData|null
- */
- public static function getRestartSettings(): ?array
- {
- $envArgs = explode('|', (string) getenv(self::RESTART_SETTINGS));
-
- if (count($envArgs) !== 6
- || (!self::$inRestart && php_ini_loaded_file() !== $envArgs[0])) {
- return null;
- }
-
- return [
- 'tmpIni' => $envArgs[0],
- 'scannedInis' => (bool) $envArgs[1],
- 'scanDir' => '*' === $envArgs[2] ? false : $envArgs[2],
- 'phprc' => '*' === $envArgs[3] ? false : $envArgs[3],
- 'inis' => explode(PATH_SEPARATOR, $envArgs[4]),
- 'skipped' => $envArgs[5],
- ];
- }
-
- /**
- * Returns the Xdebug version that triggered a successful restart
- */
- public static function getSkippedVersion(): string
- {
- return (string) self::$skipped;
- }
-
- /**
- * Returns whether Xdebug is loaded and active
- *
- * true: if Xdebug is loaded and is running in an active mode.
- * false: if Xdebug is not loaded, or it is running with xdebug.mode=off.
- */
- public static function isXdebugActive(): bool
- {
- self::setXdebugDetails();
- return self::$xdebugActive;
- }
-
- /**
- * Allows an extending class to decide if there should be a restart
- *
- * The default is to restart if Xdebug is loaded and its mode is not "off".
- */
- protected function requiresRestart(bool $default): bool
- {
- return $default;
- }
-
- /**
- * Allows an extending class to access the tmpIni
- *
- * @param string[] $command *
- */
- protected function restart(array $command): void
- {
- $this->doRestart($command);
- }
-
- /**
- * Executes the restarted command then deletes the tmp ini
- *
- * @param string[] $command
- * @phpstan-return never
- */
- private function doRestart(array $command): void
- {
- $this->tryEnableSignals();
- $this->notify(Status::RESTARTING, implode(' ', $command));
-
- if (PHP_VERSION_ID >= 70400) {
- $cmd = $command;
- } else {
- $cmd = Process::escapeShellCommand($command);
- if (defined('PHP_WINDOWS_VERSION_BUILD')) {
- // Outer quotes required on cmd string below PHP 8
- $cmd = '"'.$cmd.'"';
- }
- }
-
- $process = proc_open($cmd, [], $pipes);
- if (is_resource($process)) {
- $exitCode = proc_close($process);
- }
-
- if (!isset($exitCode)) {
- // Unlikely that php or the default shell cannot be invoked
- $this->notify(Status::ERROR, 'Unable to restart process');
- $exitCode = -1;
- } else {
- $this->notify(Status::INFO, 'Restarted process exited '.$exitCode);
- }
-
- if ($this->debug === '2') {
- $this->notify(Status::INFO, 'Temp ini saved: '.$this->tmpIni);
- } else {
- @unlink((string) $this->tmpIni);
- }
-
- exit($exitCode);
- }
-
- /**
- * Returns true if everything was written for the restart
- *
- * If any of the following fails (however unlikely) we must return false to
- * stop potential recursion:
- * - tmp ini file creation
- * - environment variable creation
- */
- private function prepareRestart(): bool
- {
- $error = null;
- $iniFiles = self::getAllIniFiles();
- $scannedInis = count($iniFiles) > 1;
- $tmpDir = sys_get_temp_dir();
-
- if (!$this->cli) {
- $error = 'Unsupported SAPI: '.PHP_SAPI;
- } elseif (!$this->checkConfiguration($info)) {
- $error = $info;
- } elseif (!$this->checkMainScript()) {
- $error = 'Unable to access main script: '.$this->script;
- } elseif (!$this->writeTmpIni($iniFiles, $tmpDir, $error)) {
- $error = $error !== null ? $error : 'Unable to create temp ini file at: '.$tmpDir;
- } elseif (!$this->setEnvironment($scannedInis, $iniFiles)) {
- $error = 'Unable to set environment variables';
- }
-
- if ($error !== null) {
- $this->notify(Status::ERROR, $error);
- }
-
- return $error === null;
- }
-
- /**
- * Returns true if the tmp ini file was written
- *
- * @param string[] $iniFiles All ini files used in the current process
- */
- private function writeTmpIni(array $iniFiles, string $tmpDir, ?string &$error): bool
- {
- if (($tmpfile = @tempnam($tmpDir, '')) === false) {
- return false;
- }
-
- $this->tmpIni = $tmpfile;
-
- // $iniFiles has at least one item and it may be empty
- if ($iniFiles[0] === '') {
- array_shift($iniFiles);
- }
-
- $content = '';
- $sectionRegex = '/^\s*\[(?:PATH|HOST)\s*=/mi';
- $xdebugRegex = '/^\s*(zend_extension\s*=.*xdebug.*)$/mi';
-
- foreach ($iniFiles as $file) {
- // Check for inaccessible ini files
- if (($data = @file_get_contents($file)) === false) {
- $error = 'Unable to read ini: '.$file;
- return false;
- }
- // Check and remove directives after HOST and PATH sections
- if (Preg::isMatchWithOffsets($sectionRegex, $data, $matches, PREG_OFFSET_CAPTURE)) {
- $data = substr($data, 0, $matches[0][1]);
- }
- $content .= Preg::replace($xdebugRegex, ';$1', $data).PHP_EOL;
- }
-
- // Merge loaded settings into our ini content, if it is valid
- $config = parse_ini_string($content);
- $loaded = ini_get_all(null, false);
-
- if (false === $config || false === $loaded) {
- $error = 'Unable to parse ini data';
- return false;
- }
-
- $content .= $this->mergeLoadedConfig($loaded, $config);
-
- // Work-around for https://bugs.php.net/bug.php?id=75932
- $content .= 'opcache.enable_cli=0'.PHP_EOL;
-
- return (bool) @file_put_contents($this->tmpIni, $content);
- }
-
- /**
- * Returns the command line arguments for the restart
- *
- * @return string[]
- */
- private function getCommand(): array
- {
- $php = [PHP_BINARY];
- $args = array_slice($_SERVER['argv'], 1);
-
- if (!$this->persistent) {
- // Use command-line options
- array_push($php, '-n', '-c', $this->tmpIni);
- }
-
- return array_merge($php, [$this->script], $args);
- }
-
- /**
- * Returns true if the restart environment variables were set
- *
- * No need to update $_SERVER since this is set in the restarted process.
- *
- * @param string[] $iniFiles All ini files used in the current process
- */
- private function setEnvironment(bool $scannedInis, array $iniFiles): bool
- {
- $scanDir = getenv('PHP_INI_SCAN_DIR');
- $phprc = getenv('PHPRC');
-
- // Make original inis available to restarted process
- if (!putenv($this->envOriginalInis.'='.implode(PATH_SEPARATOR, $iniFiles))) {
- return false;
- }
-
- if ($this->persistent) {
- // Use the environment to persist the settings
- if (!putenv('PHP_INI_SCAN_DIR=') || !putenv('PHPRC='.$this->tmpIni)) {
- return false;
- }
- }
-
- // Flag restarted process and save values for it to use
- $envArgs = [
- self::RESTART_ID,
- self::$xdebugVersion,
- (int) $scannedInis,
- false === $scanDir ? '*' : $scanDir,
- false === $phprc ? '*' : $phprc,
- ];
-
- return putenv($this->envAllowXdebug.'='.implode('|', $envArgs));
- }
-
- /**
- * Logs status messages
- */
- private function notify(string $op, ?string $data = null): void
- {
- $this->statusWriter->report($op, $data);
- }
-
- /**
- * Returns default, changed and command-line ini settings
- *
- * @param mixed[] $loadedConfig All current ini settings
- * @param mixed[] $iniConfig Settings from user ini files
- *
- */
- private function mergeLoadedConfig(array $loadedConfig, array $iniConfig): string
- {
- $content = '';
-
- foreach ($loadedConfig as $name => $value) {
- // Value will either be null, string or array (HHVM only)
- if (!is_string($value)
- || strpos($name, 'xdebug') === 0
- || $name === 'apc.mmap_file_mask') {
- continue;
- }
-
- if (!isset($iniConfig[$name]) || $iniConfig[$name] !== $value) {
- // Double-quote escape each value
- $content .= $name.'="'.addcslashes($value, '\\"').'"'.PHP_EOL;
- }
- }
-
- return $content;
- }
-
- /**
- * Returns true if the script name can be used
- */
- private function checkMainScript(): bool
- {
- if ($this->script !== null) {
- // Allow an application to set -- for standard input
- return file_exists($this->script) || '--' === $this->script;
- }
-
- if (file_exists($this->script = $_SERVER['argv'][0])) {
- return true;
- }
-
- // Use a backtrace to resolve Phar and chdir issues.
- $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
- $main = end($trace);
-
- if ($main !== false && isset($main['file'])) {
- return file_exists($this->script = $main['file']);
- }
-
- return false;
- }
-
- /**
- * Adds restart settings to the environment
- *
- * @param string[] $envArgs
- */
- private function setEnvRestartSettings(array $envArgs): void
- {
- $settings = [
- php_ini_loaded_file(),
- $envArgs[2],
- $envArgs[3],
- $envArgs[4],
- getenv($this->envOriginalInis),
- self::$skipped,
- ];
-
- Process::setEnv(self::RESTART_SETTINGS, implode('|', $settings));
- }
-
- /**
- * Syncs settings and the environment if called with existing settings
- *
- * @phpstan-param restartData $settings
- */
- private function syncSettings(array $settings): void
- {
- if (false === getenv($this->envOriginalInis)) {
- // Called by another app, so make original inis available
- Process::setEnv($this->envOriginalInis, implode(PATH_SEPARATOR, $settings['inis']));
- }
-
- self::$skipped = $settings['skipped'];
- $this->notify(Status::INFO, 'Process called with existing restart settings');
- }
-
- /**
- * Returns true if there are no known configuration issues
- */
- private function checkConfiguration(?string &$info): bool
- {
- if (!function_exists('proc_open')) {
- $info = 'proc_open function is disabled';
- return false;
- }
-
- if (extension_loaded('uopz') && !((bool) ini_get('uopz.disable'))) {
- // uopz works at opcode level and disables exit calls
- if (function_exists('uopz_allow_exit')) {
- @uopz_allow_exit(true);
- } else {
- $info = 'uopz extension is not compatible';
- return false;
- }
- }
-
- // Check UNC paths when using cmd.exe
- if (defined('PHP_WINDOWS_VERSION_BUILD') && PHP_VERSION_ID < 70400) {
- $workingDir = getcwd();
-
- if ($workingDir === false) {
- $info = 'unable to determine working directory';
- return false;
- }
-
- if (0 === strpos($workingDir, '\\\\')) {
- $info = 'cmd.exe does not support UNC paths: '.$workingDir;
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Enables async signals and control interrupts in the restarted process
- *
- * Available on Unix PHP 7.1+ with the pcntl extension and Windows PHP 7.4+.
- */
- private function tryEnableSignals(): void
- {
- if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
- pcntl_async_signals(true);
- $message = 'Async signals enabled';
-
- if (!self::$inRestart) {
- // Restarting, so ignore SIGINT in parent
- pcntl_signal(SIGINT, SIG_IGN);
- } elseif (is_int(pcntl_signal_get_handler(SIGINT))) {
- // Restarted, no handler set so force default action
- pcntl_signal(SIGINT, SIG_DFL);
- }
- }
-
- if (!self::$inRestart && function_exists('sapi_windows_set_ctrl_handler')) {
- // Restarting, so set a handler to ignore CTRL events in the parent.
- // This ensures that CTRL+C events will be available in the child
- // process without having to enable them there, which is unreliable.
- sapi_windows_set_ctrl_handler(function ($evt) {});
- }
- }
-
- /**
- * Sets static properties $xdebugActive, $xdebugVersion and $xdebugMode
- */
- private static function setXdebugDetails(): void
- {
- if (self::$xdebugActive !== null) {
- return;
- }
-
- self::$xdebugActive = false;
- if (!extension_loaded('xdebug')) {
- return;
- }
-
- $version = phpversion('xdebug');
- self::$xdebugVersion = $version !== false ? $version : 'unknown';
-
- if (version_compare(self::$xdebugVersion, '3.1', '>=')) {
- $modes = xdebug_info('mode');
- self::$xdebugMode = count($modes) === 0 ? 'off' : implode(',', $modes);
- self::$xdebugActive = self::$xdebugMode !== 'off';
- return;
- }
-
- // See if xdebug.mode is supported in this version
- $iniMode = ini_get('xdebug.mode');
- if ($iniMode === false) {
- self::$xdebugActive = true;
- return;
- }
-
- // Environment value wins but cannot be empty
- $envMode = (string) getenv('XDEBUG_MODE');
- if ($envMode !== '') {
- self::$xdebugMode = $envMode;
- } else {
- self::$xdebugMode = $iniMode !== '' ? $iniMode : 'off';
- }
-
- // An empty comma-separated list is treated as mode 'off'
- if (Preg::isMatch('/^,+$/', str_replace(' ', '', self::$xdebugMode))) {
- self::$xdebugMode = 'off';
- }
-
- self::$xdebugActive = self::$xdebugMode !== 'off';
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/.github/workflows/build.yml b/vendor/felixfbecker/advanced-json-rpc/.github/workflows/build.yml
deleted file mode 100644
index 4b0bd562..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/.github/workflows/build.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: build
-
-on: [push, pull_request]
-
-env:
- FORCE_COLOR: 1
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-jobs:
- test:
- strategy:
- matrix:
- php:
- - 7.1
- - 7.2
- - 7.3
- - 7.4
- - 8.0
- deps:
- - lowest
- - highest
- include:
- - php: 8.1
- deps: highest
- composer-options: --ignore-platform-reqs
- exclude:
- # that config currently breaks as older PHPUnit cannot generate coverage on PHP 8
- - php: 8
- deps: lowest
-
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php }}
-
- - uses: ramsey/composer-install@v1
- with:
- dependency-versions: ${{ matrix.deps }}
- composer-options: ${{ matrix.composer-options }}
-
- - run: vendor/bin/phpunit --coverage-clover=coverage.xml --whitelist lib --bootstrap vendor/autoload.php tests
-
- - uses: codecov/codecov-action@v1
-
- release:
- needs: test
- if: github.repository_owner == 'felixfbecker' && github.event_name == 'push' && github.ref == 'refs/heads/master'
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
-
- - name: Setup Node.js
- uses: actions/setup-node@v2
-
- - name: Install npm dependencies
- run: npm ci
-
- - name: Release
- run: npm run semantic-release
diff --git a/vendor/felixfbecker/advanced-json-rpc/LICENSE b/vendor/felixfbecker/advanced-json-rpc/LICENSE
deleted file mode 100644
index fc354170..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/LICENSE
+++ /dev/null
@@ -1,15 +0,0 @@
-ISC License
-
-Copyright (c) 2016, Felix Frederick Becker
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/vendor/felixfbecker/advanced-json-rpc/composer.json b/vendor/felixfbecker/advanced-json-rpc/composer.json
deleted file mode 100644
index c4d5739e..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/composer.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "name": "felixfbecker/advanced-json-rpc",
- "description": "A more advanced JSONRPC implementation",
- "type": "library",
- "license": "ISC",
- "authors": [
- {
- "name": "Felix Becker",
- "email": "felix.b@outlook.com"
- }
- ],
- "autoload": {
- "psr-4": {
- "AdvancedJsonRpc\\": "lib/"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "AdvancedJsonRpc\\Tests\\": "tests/"
- }
- },
- "require": {
- "php": "^7.1 || ^8.0",
- "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
- "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^7.0 || ^8.0"
- },
- "minimum-stability": "dev",
- "prefer-stable": true
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Dispatcher.php b/vendor/felixfbecker/advanced-json-rpc/lib/Dispatcher.php
deleted file mode 100644
index 5f045df6..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Dispatcher.php
+++ /dev/null
@@ -1,171 +0,0 @@
- ReflectionMethod[]
- *
- * @var ReflectionMethod
- */
- private $methods;
-
- /**
- * @var \phpDocumentor\Reflection\DocBlockFactory
- */
- private $docBlockFactory;
-
- /**
- * @var \phpDocumentor\Reflection\Types\ContextFactory
- */
- private $contextFactory;
-
- /**
- * @param object $target The target object that should receive the method calls
- * @param string $delimiter A delimiter for method calls on properties, for example someProperty->someMethod
- */
- public function __construct($target, $delimiter = '->')
- {
- $this->target = $target;
- $this->delimiter = $delimiter;
- $this->docBlockFactory = DocBlockFactory::createInstance();
- $this->contextFactory = new Types\ContextFactory();
- $this->mapper = new JsonMapper();
- }
-
- /**
- * Calls the appropriate method handler for an incoming Message
- *
- * @param string|object $msg The incoming message
- * @return mixed
- */
- public function dispatch($msg)
- {
- if (is_string($msg)) {
- $msg = json_decode($msg);
- if (json_last_error() !== JSON_ERROR_NONE) {
- throw new Error(json_last_error_msg(), ErrorCode::PARSE_ERROR);
- }
- }
- // Find out the object and function that should be called
- $obj = $this->target;
- $parts = explode($this->delimiter, $msg->method);
- // The function to call is always the last part of the method
- $fn = array_pop($parts);
- // For namespaced methods like textDocument/didOpen, call the didOpen method on the $textDocument property
- // For simple methods like initialize, shutdown, exit, this loop will simply not be entered and $obj will be
- // the target
- foreach ($parts as $part) {
- if (!isset($obj->$part)) {
- throw new Error("Method {$msg->method} is not implemented", ErrorCode::METHOD_NOT_FOUND);
- }
- $obj = $obj->$part;
- }
- if (!isset($this->methods[$msg->method])) {
- try {
- $method = new ReflectionMethod($obj, $fn);
- $this->methods[$msg->method] = $method;
- } catch (ReflectionException $e) {
- throw new Error($e->getMessage(), ErrorCode::METHOD_NOT_FOUND, null, $e);
- }
- }
- $method = $this->methods[$msg->method];
- $parameters = $method->getParameters();
- if ($method->getDocComment()) {
- $docBlock = $this->docBlockFactory->create(
- $method->getDocComment(),
- $this->contextFactory->createFromReflector($method->getDeclaringClass())
- );
- $paramTags = $docBlock->getTagsByName('param');
- }
- $args = [];
- if (isset($msg->params)) {
- // Find out the position
- if (is_array($msg->params)) {
- $args = $msg->params;
- } else if (is_object($msg->params)) {
- foreach ($parameters as $pos => $parameter) {
- $value = null;
- foreach(get_object_vars($msg->params) as $key => $val) {
- if ($parameter->name === $key) {
- $value = $val;
- break;
- }
- }
- $args[$pos] = $value;
- }
- } else {
- throw new Error('Params must be structured or omitted', ErrorCode::INVALID_REQUEST);
- }
- foreach ($args as $position => $value) {
- try {
- // If the type is structured (array or object), map it with JsonMapper
- if (is_object($value)) {
- // Does the parameter have a type hint?
- $param = $parameters[$position];
- if ($param->hasType()) {
- $paramType = $param->getType();
- if ($paramType instanceof ReflectionNamedType) {
- // We have object data to map and want the class name.
- // This should not include the `?` if the type was nullable.
- $class = $paramType->getName();
- } else {
- // Fallback for php 7.0, which is still supported (and doesn't have nullable).
- $class = (string)$paramType;
- }
- $value = $this->mapper->map($value, new $class());
- }
- } else if (is_array($value) && isset($docBlock)) {
- // Get the array type from the DocBlock
- $type = $paramTags[$position]->getType();
- // For union types, use the first one that is a class array (often it is SomeClass[]|null)
- if ($type instanceof Types\Compound) {
- for ($i = 0; $t = $type->get($i); $i++) {
- if (
- $t instanceof Types\Array_
- && $t->getValueType() instanceof Types\Object_
- && (string)$t->getValueType() !== 'object'
- ) {
- $class = (string)$t->getValueType()->getFqsen();
- $value = $this->mapper->mapArray($value, [], $class);
- break;
- }
- }
- } else if ($type instanceof Types\Array_) {
- $class = (string)$type->getValueType()->getFqsen();
- $value = $this->mapper->mapArray($value, [], $class);
- } else {
- throw new Error('Type is not matching @param tag', ErrorCode::INVALID_PARAMS);
- }
- }
- } catch (JsonMapper_Exception $e) {
- throw new Error($e->getMessage(), ErrorCode::INVALID_PARAMS, null, $e);
- }
- $args[$position] = $value;
- }
- }
- ksort($args);
- $result = $obj->$fn(...$args);
- return $result;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Error.php b/vendor/felixfbecker/advanced-json-rpc/lib/Error.php
deleted file mode 100644
index b2801918..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Error.php
+++ /dev/null
@@ -1,38 +0,0 @@
-data = $data;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/ErrorCode.php b/vendor/felixfbecker/advanced-json-rpc/lib/ErrorCode.php
deleted file mode 100644
index f0ef4792..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/ErrorCode.php
+++ /dev/null
@@ -1,48 +0,0 @@
-id) && isset($msg->error);
- }
-
- /**
- * @param int|string $id
- * @param \AdvancedJsonRpc\Error $error
- */
- public function __construct($id, Error $error)
- {
- parent::__construct($id);
- $this->error = $error;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Message.php b/vendor/felixfbecker/advanced-json-rpc/lib/Message.php
deleted file mode 100644
index e2231dc5..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Message.php
+++ /dev/null
@@ -1,52 +0,0 @@
-method, $decoded->params ?? null);
- } else if (Request::isRequest($decoded)) {
- $obj = new Request($decoded->id, $decoded->method, $decoded->params ?? null);
- } else if (SuccessResponse::isSuccessResponse($decoded)) {
- $obj = new SuccessResponse($decoded->id, $decoded->result);
- } else if (ErrorResponse::isErrorResponse($decoded)) {
- $obj = new ErrorResponse($decoded->id, new Error($decoded->error->message, $decoded->error->code, $decoded->error->data ?? null));
- } else {
- throw new Error('Invalid message', ErrorCode::INVALID_REQUEST);
- }
- return $obj;
- }
-
- public function __toString(): string
- {
- $encoded = json_encode($this);
- if ($encoded === false) {
- throw new Error(json_last_error_msg(), ErrorCode::INTERNAL_ERROR);
- }
- return $encoded;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Notification.php b/vendor/felixfbecker/advanced-json-rpc/lib/Notification.php
deleted file mode 100644
index 3440164d..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Notification.php
+++ /dev/null
@@ -1,56 +0,0 @@
-method);
- }
-
- /**
- * @param string $method
- * @param mixed $params
- */
- public function __construct(string $method, $params = null)
- {
- $this->method = $method;
- $this->params = $params;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Request.php b/vendor/felixfbecker/advanced-json-rpc/lib/Request.php
deleted file mode 100644
index 14290082..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Request.php
+++ /dev/null
@@ -1,63 +0,0 @@
-method);
- }
-
- /**
- * @param string|int $id
- * @param string $method
- * @param object|array $params
- */
- public function __construct($id, string $method, $params = null)
- {
- $this->id = $id;
- $this->method = $method;
- $this->params = $params;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/Response.php b/vendor/felixfbecker/advanced-json-rpc/lib/Response.php
deleted file mode 100644
index a871eeac..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/Response.php
+++ /dev/null
@@ -1,40 +0,0 @@
-error));
- }
-
- /**
- * @param int|string $id
- * @param mixed $result
- * @param ResponseError $error
- */
- public function __construct($id)
- {
- $this->id = $id;
- }
-}
diff --git a/vendor/felixfbecker/advanced-json-rpc/lib/SuccessResponse.php b/vendor/felixfbecker/advanced-json-rpc/lib/SuccessResponse.php
deleted file mode 100644
index 222fd46e..00000000
--- a/vendor/felixfbecker/advanced-json-rpc/lib/SuccessResponse.php
+++ /dev/null
@@ -1,40 +0,0 @@
-result = $result;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/LICENSE.txt b/vendor/microsoft/tolerant-php-parser/LICENSE.txt
deleted file mode 100644
index 79918eb7..00000000
--- a/vendor/microsoft/tolerant-php-parser/LICENSE.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Copyright (c) Microsoft Corporation
-
-All rights reserved.
-
-MIT License
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
-modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
-OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/microsoft/tolerant-php-parser/ThirdPartyNotices.txt b/vendor/microsoft/tolerant-php-parser/ThirdPartyNotices.txt
deleted file mode 100644
index d712c991..00000000
--- a/vendor/microsoft/tolerant-php-parser/ThirdPartyNotices.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Microsoft/tolerant-php-parser
-
-THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
-Do Not Translate or Localize
-
-This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
-
-1. php/php-langspec (https://github.com/php/php-langspec)
-
-
-%% php/php-langspec NOTICES AND INFORMATION BEGIN HERE
-=========================================
-Facebook has dedicated all copyright to this specification to the public
-domain worldwide under the CC0 Public Domain Dedication located at
-.
-
-The first draft of this specification was initially written in 2014 by
-Facebook, Inc.
-
-This specification is distributed without any warranty.
-=========================================
-END OF php/php-langspec NOTICES AND INFORMATION
diff --git a/vendor/microsoft/tolerant-php-parser/composer.json b/vendor/microsoft/tolerant-php-parser/composer.json
deleted file mode 100644
index 91df816a..00000000
--- a/vendor/microsoft/tolerant-php-parser/composer.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "microsoft/tolerant-php-parser",
- "description": "Tolerant PHP-to-AST parser designed for IDE usage scenarios",
- "type": "library",
- "require": {
- "php": ">=7.2"
- },
- "require-dev": {
- "phpunit/phpunit": "^8.5.15"
- },
- "license": "MIT",
- "authors": [
- {
- "name": "Rob Lourens",
- "email": "roblou@microsoft.com"
- }
- ],
- "autoload": {
- "psr-4": { "Microsoft\\PhpParser\\": ["src/"] }
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/phpstan.neon b/vendor/microsoft/tolerant-php-parser/phpstan.neon
deleted file mode 100644
index 3d24b87a..00000000
--- a/vendor/microsoft/tolerant-php-parser/phpstan.neon
+++ /dev/null
@@ -1,7 +0,0 @@
-parameters:
- level: 2
- paths:
- - src/
- ignoreErrors:
- # phpstan issue, see: https://github.com/phpstan/phpstan/issues/1306
- - "/Variable .unaryExpression might not be defined./"
diff --git a/vendor/microsoft/tolerant-php-parser/src/CharacterCodes.php b/vendor/microsoft/tolerant-php-parser/src/CharacterCodes.php
deleted file mode 100644
index c411ed60..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/CharacterCodes.php
+++ /dev/null
@@ -1,116 +0,0 @@
-
- const _hash = 0x23; // #
- const _lessThan = 0x3C; // <
- const _minus = 0x2D; // -
- const _openBrace = 0x7B; // {
- const _openBracket = 0x5B; // [
- const _openParen = 0x28; // (
- const _percent = 0x25; // %
- const _plus = 0x2B; // +
- const _question = 0x3F; // ?
- const _semicolon = 0x3B; // ;
- const _singleQuote = 0x27; // '
- const _slash = 0x2F; // /
- const _tilde = 0x7E; // ~
-
- const _backspace = 0x08; // \b
- const _formFeed = 0x0C; // \f
- const _byteOrderMark = 0xFEFF;
- const _space = 0x20;
- const _newline = 0x0A; // \n
- const _return = 0x0D; // \r
- const _tab = 0x09; // \t
- const _verticalTab = 0x0B; // \v
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/ClassLike.php b/vendor/microsoft/tolerant-php-parser/src/ClassLike.php
deleted file mode 100644
index 08b7fa8d..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/ClassLike.php
+++ /dev/null
@@ -1,12 +0,0 @@
-kind = $kind;
- $this->message = $message;
- $this->start = $start;
- $this->length = $length;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/DiagnosticKind.php b/vendor/microsoft/tolerant-php-parser/src/DiagnosticKind.php
deleted file mode 100644
index 012db9d0..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/DiagnosticKind.php
+++ /dev/null
@@ -1,12 +0,0 @@
-getDiagnosticForNode();
- }
- return null;
- }
-
- /**
- * @param Token $token
- * @return Diagnostic|null
- */
- private static function checkDiagnosticForUnexpectedToken($token) {
- if ($token instanceof SkippedToken) {
- // TODO - consider also attaching parse context information to skipped tokens
- // this would allow us to provide more helpful error messages that inform users what to do
- // about the problem rather than simply pointing out the mistake.
- return new Diagnostic(
- DiagnosticKind::Error,
- "Unexpected '" .
- (self::$tokenKindToText[$token->kind]
- ?? Token::getTokenKindNameFromValue($token->kind)) .
- "'",
- $token->start,
- $token->getEndPosition() - $token->start
- );
- } elseif ($token instanceof MissingToken) {
- return new Diagnostic(
- DiagnosticKind::Error,
- "'" .
- (self::$tokenKindToText[$token->kind]
- ?? Token::getTokenKindNameFromValue($token->kind)) .
- "' expected.",
- $token->start,
- $token->getEndPosition() - $token->start
- );
- }
- return null;
- }
-
- /**
- * Traverses AST to generate diagnostics.
- * @param \Microsoft\PhpParser\Node $n
- * @return Diagnostic[]
- */
- public static function getDiagnostics(Node $n) : array {
- $diagnostics = [];
-
- /**
- * @param \Microsoft\PhpParser\Node|\Microsoft\PhpParser\Token $node
- */
- $n->walkDescendantNodesAndTokens(function($node) use (&$diagnostics) {
- if (($diagnostic = self::checkDiagnostics($node)) !== null) {
- $diagnostics[] = $diagnostic;
- }
- });
-
- return $diagnostics;
- }
-}
-
-DiagnosticsProvider::initTokenKindToText();
diff --git a/vendor/microsoft/tolerant-php-parser/src/FilePositionMap.php b/vendor/microsoft/tolerant-php-parser/src/FilePositionMap.php
deleted file mode 100644
index c6c9f479..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/FilePositionMap.php
+++ /dev/null
@@ -1,115 +0,0 @@
-currentOffset (updated whenever currentOffset is updated) */
- private $lineForCurrentOffset;
-
- public function __construct(string $file_contents) {
- $this->fileContents = $file_contents;
- $this->fileContentsLength = \strlen($file_contents);
- $this->currentOffset = 0;
- $this->lineForCurrentOffset = 1;
- }
-
- /**
- * @param Node|Token $node
- */
- public function getStartLine($node) : int {
- return $this->getLineNumberForOffset($node->getStartPosition());
- }
-
- /**
- * @param Node|Token $node
- * Similar to getStartLine but includes the column
- */
- public function getStartLineCharacterPositionForOffset($node) : LineCharacterPosition {
- return $this->getLineCharacterPositionForOffset($node->getStartPosition());
- }
-
- /** @param Node|Token $node */
- public function getEndLine($node) : int {
- return $this->getLineNumberForOffset($node->getEndPosition());
- }
-
- /**
- * @param Node|Token $node
- * Similar to getStartLine but includes the column
- */
- public function getEndLineCharacterPosition($node) : LineCharacterPosition {
- return $this->getLineCharacterPositionForOffset($node->getEndPosition());
- }
-
- /**
- * @param int $offset
- * Similar to getStartLine but includes both the line and the column
- */
- public function getLineCharacterPositionForOffset(int $offset) : LineCharacterPosition {
- $line = $this->getLineNumberForOffset($offset);
- $character = $this->getColumnForOffset($offset);
- return new LineCharacterPosition($line, $character);
- }
-
- /**
- * @param int $offset - A 0-based byte offset
- * @return int - gets the 1-based line number for $offset
- */
- public function getLineNumberForOffset(int $offset) : int {
- if ($offset < 0) {
- $offset = 0;
- } elseif ($offset > $this->fileContentsLength) {
- $offset = $this->fileContentsLength;
- }
- $currentOffset = $this->currentOffset;
- if ($offset > $currentOffset) {
- $this->lineForCurrentOffset += \substr_count($this->fileContents, "\n", $currentOffset, $offset - $currentOffset);
- $this->currentOffset = $offset;
- } elseif ($offset < $currentOffset) {
- $this->lineForCurrentOffset -= \substr_count($this->fileContents, "\n", $offset, $currentOffset - $offset);
- $this->currentOffset = $offset;
- }
- return $this->lineForCurrentOffset;
- }
-
- /**
- * @param int $offset - A 0-based byte offset
- * @return int - gets the 1-based column number for $offset
- */
- public function getColumnForOffset(int $offset) : int {
- $length = $this->fileContentsLength;
- if ($offset <= 1) {
- return 1;
- } elseif ($offset > $length) {
- $offset = $length;
- }
- // Postcondition: offset >= 1, ($lastNewlinePos < $offset)
- // If there was no previous newline, lastNewlinePos = 0
-
- // Start strrpos check from the character before the current character,
- // in case the current character is a newline.
- $lastNewlinePos = \strrpos($this->fileContents, "\n", -$length + $offset - 1);
- return 1 + $offset - ($lastNewlinePos === false ? 0 : $lastNewlinePos + 1);
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/FunctionLike.php b/vendor/microsoft/tolerant-php-parser/src/FunctionLike.php
deleted file mode 100644
index 5ef91b52..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/FunctionLike.php
+++ /dev/null
@@ -1,17 +0,0 @@
-line = $line;
- $this->character = $character;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/MissingToken.php b/vendor/microsoft/tolerant-php-parser/src/MissingToken.php
deleted file mode 100644
index 177b46be..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/MissingToken.php
+++ /dev/null
@@ -1,23 +0,0 @@
- $this->getTokenKindNameFromValue(TokenKind::MissingToken)],
- parent::jsonSerialize()
- );
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/ModifiedTypeInterface.php b/vendor/microsoft/tolerant-php-parser/src/ModifiedTypeInterface.php
deleted file mode 100644
index f04ca238..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/ModifiedTypeInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-modifiers === null) {
- return false;
- }
-
- foreach ($this->modifiers as $modifier) {
- if ($modifier->kind === $targetModifier) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Convenience method to check for the existence of the "public" modifier.
- * Does not necessarily need to be defined for that type.
- *
- * @return bool
- */
- public function isPublic(): bool {
- return $this->hasModifier(TokenKind::PublicKeyword);
- }
-
- /**
- * Convenience method to check for the existence of the "static" modifier.
- * Does not necessarily need to be defined for that type.
- *
- * @return bool
- */
- public function isStatic(): bool {
- return $this->hasModifier(TokenKind::StaticKeyword);
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/NamespacedNameInterface.php b/vendor/microsoft/tolerant-php-parser/src/NamespacedNameInterface.php
deleted file mode 100644
index d4431d50..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/NamespacedNameInterface.php
+++ /dev/null
@@ -1,11 +0,0 @@
-getNamespaceDefinition();
- $content = $this->getFileContents();
- if ($namespaceDefinition === null) {
- // global namespace -> strip namespace\ prefix
- return ResolvedName::buildName($this->getNameParts(), $content);
- }
-
- if ($namespaceDefinition->name !== null) {
- $resolvedName = ResolvedName::buildName($namespaceDefinition->name->nameParts, $content);
- } else {
- $resolvedName = ResolvedName::buildName([], $content);
- }
- if (
- !($this instanceof QualifiedName && (
- ($this->parent instanceof NamespaceDefinition) ||
- ($this->parent instanceof NamespaceUseDeclaration) ||
- ($this->parent instanceof NamespaceUseClause) ||
- ($this->parent instanceof NamespaceUseGroupClause)))
- ) {
- $resolvedName->addNameParts($this->getNameParts(), $content);
- }
- return $resolvedName;
- }
-}
\ No newline at end of file
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node.php b/vendor/microsoft/tolerant-php-parser/src/Node.php
deleted file mode 100644
index d1288876..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node.php
+++ /dev/null
@@ -1,686 +0,0 @@
-getChildNodesAndTokens()->current()->getStartPosition();
- }
-
- /**
- * Gets start position of Node, including leading comments and whitespace
- * @return int
- * @throws \Exception
- */
- public function getFullStartPosition() : int {
- foreach($this::CHILD_NAMES as $name) {
-
- if (($child = $this->$name) !== null) {
-
- if (\is_array($child)) {
- if(!isset($child[0])) {
- continue;
- }
- $child = $child[0];
- }
-
- return $child->getFullStartPosition();
- }
- };
-
- throw new \RuntimeException("Could not resolve full start position");
- }
-
- /**
- * Gets parent of current node (returns null if has no parent)
- * @return null|Node
- */
- public function getParent() {
- return $this->parent;
- }
-
- /**
- * Gets first ancestor that is an instance of one of the provided classes.
- * Returns null if there is no match.
- *
- * @param string ...$classNames
- * @return Node|null
- */
- public function getFirstAncestor(...$classNames) {
- $ancestor = $this;
- while (($ancestor = $ancestor->parent) !== null) {
- foreach ($classNames as $className) {
- if ($ancestor instanceof $className) {
- return $ancestor;
- }
- }
- }
- return null;
- }
-
- /**
- * Gets first child that is an instance of one of the provided classes.
- * Returns null if there is no match.
- *
- * @param array ...$classNames
- * @return Node|null
- */
- public function getFirstChildNode(...$classNames) {
- foreach ($this::CHILD_NAMES as $name) {
- $val = $this->$name;
- foreach ($classNames as $className) {
- if (\is_array($val)) {
- foreach ($val as $child) {
- if ($child instanceof $className) {
- return $child;
- }
- }
- continue;
- } elseif ($val instanceof $className) {
- return $val;
- }
- }
- }
- return null;
- }
-
- /**
- * Gets first descendant node that is an instance of one of the provided classes.
- * Returns null if there is no match.
- *
- * @param array ...$classNames
- * @return Node|null
- */
- public function getFirstDescendantNode(...$classNames) {
- foreach ($this->getDescendantNodes() as $descendant) {
- foreach ($classNames as $className) {
- if ($descendant instanceof $className) {
- return $descendant;
- }
- }
- }
- return null;
- }
-
- /**
- * Gets root of the syntax tree (returns self if has no parents)
- * @return SourceFileNode (expect root to be SourceFileNode unless the tree was manipulated)
- */
- public function getRoot() : Node {
- $node = $this;
- while ($node->parent !== null) {
- $node = $node->parent;
- }
- return $node;
- }
-
- /**
- * Gets generator containing all descendant Nodes and Tokens.
- *
- * @param callable|null $shouldDescendIntoChildrenFn
- * @return \Generator|Node[]|Token[]
- */
- public function getDescendantNodesAndTokens(callable $shouldDescendIntoChildrenFn = null) {
- // TODO - write unit tests to prove invariants
- // (concatenating all descendant Tokens should produce document, concatenating all Nodes should produce document)
- foreach ($this->getChildNodesAndTokens() as $child) {
- // Check possible types of $child, most frequent first
- if ($child instanceof Node) {
- yield $child;
- if ($shouldDescendIntoChildrenFn === null || $shouldDescendIntoChildrenFn($child)) {
- yield from $child->getDescendantNodesAndTokens($shouldDescendIntoChildrenFn);
- }
- } elseif ($child instanceof Token) {
- yield $child;
- }
- }
- }
-
- /**
- * Iterate over all descendant Nodes and Tokens, calling $callback.
- * This can often be faster than getDescendantNodesAndTokens
- * if you just need to call something and don't need a generator.
- *
- * @param callable $callback a callback that accepts Node|Token
- * @param callable|null $shouldDescendIntoChildrenFn
- * @return void
- */
- public function walkDescendantNodesAndTokens(callable $callback, callable $shouldDescendIntoChildrenFn = null) {
- // TODO - write unit tests to prove invariants
- // (concatenating all descendant Tokens should produce document, concatenating all Nodes should produce document)
- foreach (static::CHILD_NAMES as $name) {
- $child = $this->$name;
- // Check possible types of $child, most frequent first
- if ($child instanceof Token) {
- $callback($child);
- } elseif ($child instanceof Node) {
- $callback($child);
- if ($shouldDescendIntoChildrenFn === null || $shouldDescendIntoChildrenFn($child)) {
- $child->walkDescendantNodesAndTokens($callback, $shouldDescendIntoChildrenFn);
- }
- } elseif (\is_array($child)) {
- foreach ($child as $childElement) {
- if ($childElement instanceof Token) {
- $callback($childElement);
- } elseif ($childElement instanceof Node) {
- $callback($childElement);
- if ($shouldDescendIntoChildrenFn === null || $shouldDescendIntoChildrenFn($childElement)) {
- $childElement->walkDescendantNodesAndTokens($callback, $shouldDescendIntoChildrenFn);
- }
- }
- }
- }
- }
- }
-
- /**
- * Gets a generator containing all descendant Nodes.
- * @param callable|null $shouldDescendIntoChildrenFn
- * @return \Generator|Node[]
- */
- public function getDescendantNodes(callable $shouldDescendIntoChildrenFn = null) {
- foreach ($this->getChildNodes() as $child) {
- yield $child;
- if ($shouldDescendIntoChildrenFn === null || $shouldDescendIntoChildrenFn($child)) {
- yield from $child->getDescendantNodes($shouldDescendIntoChildrenFn);
- }
- }
- }
-
- /**
- * Gets generator containing all descendant Tokens.
- * @param callable|null $shouldDescendIntoChildrenFn
- * @return \Generator|Token[]
- */
- public function getDescendantTokens(callable $shouldDescendIntoChildrenFn = null) {
- foreach ($this->getChildNodesAndTokens() as $child) {
- if ($child instanceof Node) {
- if ($shouldDescendIntoChildrenFn == null || $shouldDescendIntoChildrenFn($child)) {
- yield from $child->getDescendantTokens($shouldDescendIntoChildrenFn);
- }
- } elseif ($child instanceof Token) {
- yield $child;
- }
- }
- }
-
- /**
- * Gets generator containing all child Nodes and Tokens (direct descendants).
- * Does not return null elements.
- *
- * @return \Generator|Token[]|Node[]
- */
- public function getChildNodesAndTokens() : \Generator {
- foreach ($this::CHILD_NAMES as $name) {
- $val = $this->$name;
-
- if (\is_array($val)) {
- foreach ($val as $child) {
- if ($child !== null) {
- yield $name => $child;
- }
- }
- continue;
- }
- if ($val !== null) {
- yield $name => $val;
- }
- }
- }
-
- /**
- * Gets generator containing all child Nodes (direct descendants)
- * @return \Generator|Node[]
- */
- public function getChildNodes() : \Generator {
- foreach ($this::CHILD_NAMES as $name) {
- $val = $this->$name;
- if (\is_array($val)) {
- foreach ($val as $child) {
- if ($child instanceof Node) {
- yield $child;
- }
- }
- continue;
- } elseif ($val instanceof Node) {
- yield $val;
- }
- }
- }
-
- /**
- * Gets generator containing all child Tokens (direct descendants)
- *
- * @return \Generator|Token[]
- */
- public function getChildTokens() {
- foreach ($this::CHILD_NAMES as $name) {
- $val = $this->$name;
- if (\is_array($val)) {
- foreach ($val as $child) {
- if ($child instanceof Token) {
- yield $child;
- }
- }
- continue;
- } elseif ($val instanceof Token) {
- yield $val;
- }
- }
- }
-
- /**
- * Gets array of declared child names (cached).
- *
- * This is used as an optimization when iterating over nodes: For direct iteration
- * PHP will create a properties hashtable on the object, thus doubling memory usage.
- * We avoid this by iterating over just the names instead.
- *
- * @return string[]
- */
- public function getChildNames() {
- return $this::CHILD_NAMES;
- }
-
- /**
- * Gets width of a Node (not including comment / whitespace trivia)
- *
- * @return int
- */
- public function getWidth() : int {
- $first = $this->getStartPosition();
- $last = $this->getEndPosition();
-
- return $last - $first;
- }
-
- /**
- * Gets width of a Node (including comment / whitespace trivia)
- *
- * @return int
- */
- public function getFullWidth() : int {
- $first = $this->getFullStartPosition();
- $last = $this->getEndPosition();
-
- return $last - $first;
- }
-
- /**
- * Gets string representing Node text (not including leading comment + whitespace trivia)
- * @return string
- */
- public function getText() : string {
- $start = $this->getStartPosition();
- $end = $this->getEndPosition();
-
- $fileContents = $this->getFileContents();
- return \substr($fileContents, $start, $end - $start);
- }
-
- /**
- * Gets full text of Node (including leading comment + whitespace trivia)
- * @return string
- */
- public function getFullText() : string {
- $start = $this->getFullStartPosition();
- $end = $this->getEndPosition();
-
- $fileContents = $this->getFileContents();
- return \substr($fileContents, $start, $end - $start);
-
- }
-
- /**
- * Gets string representing Node's leading comment and whitespace text.
- * @return string
- */
- public function getLeadingCommentAndWhitespaceText() : string {
- // TODO re-tokenize comments and whitespace
- $fileContents = $this->getFileContents();
- foreach ($this->getDescendantTokens() as $token) {
- return $token->getLeadingCommentsAndWhitespaceText($fileContents);
- }
- return '';
- }
-
- protected function getChildrenKvPairs() {
- $result = [];
- foreach ($this::CHILD_NAMES as $name) {
- $result[$name] = $this->$name;
- }
- return $result;
- }
-
- #[ReturnTypeWillChange]
- public function jsonSerialize() {
- $kindName = $this->getNodeKindName();
- return ["$kindName" => $this->getChildrenKvPairs()];
- }
-
- /**
- * Get the end index of a Node.
- * @return int
- * @throws \Exception
- */
- public function getEndPosition() {
- // TODO test invariant - start of next node is end of previous node
- for ($i = \count($childKeys = $this::CHILD_NAMES) - 1; $i >= 0; $i--) {
- $lastChildKey = $childKeys[$i];
- $lastChild = $this->$lastChildKey;
-
- if (\is_array($lastChild)) {
- $lastChild = \end($lastChild);
- }
-
- if ($lastChild instanceof Token) {
- return $lastChild->fullStart + $lastChild->length;
- } elseif ($lastChild instanceof Node) {
- return $lastChild->getEndPosition();
- }
- }
-
- throw new \Exception("Unhandled node type");
- }
-
- public function getFileContents() : string {
- // TODO consider renaming to getSourceText
- return $this->getRoot()->fileContents;
- }
-
- public function getUri() : string {
- return $this->getRoot()->uri;
- }
-
- public function getLastChild() {
- $a = iterator_to_array($this->getChildNodesAndTokens());
- return \end($a);
- }
-
- /**
- * Searches descendants to find a Node at the given position.
- *
- * @param int $pos
- * @return Node
- */
- public function getDescendantNodeAtPosition(int $pos) {
- foreach ($this->getChildNodes() as $child) {
- if ($child->containsPosition($pos)) {
- $node = $child->getDescendantNodeAtPosition($pos);
- if (!is_null($node)) {
- return $node;
- }
- }
- }
-
- return $this;
- }
-
- /**
- * Returns true if the given Node or Token contains the given position.
- * @param int $pos
- * @return bool
- */
- private function containsPosition(int $pos): bool {
- return $this->getStartPosition() <= $pos && $pos <= $this->getEndPosition();
- }
-
- /**
- * Gets leading PHP Doc Comment text corresponding to the current Node.
- * Returns last doc comment in leading comment / whitespace trivia,
- * and returns null if there is no preceding doc comment.
- *
- * @return string|null
- */
- public function getDocCommentText() {
- $leadingTriviaText = $this->getLeadingCommentAndWhitespaceText();
- $leadingTriviaTokens = PhpTokenizer::getTokensArrayFromContent(
- $leadingTriviaText, ParseContext::SourceElements, $this->getFullStartPosition(), false
- );
- for ($i = \count($leadingTriviaTokens) - 1; $i >= 0; $i--) {
- $token = $leadingTriviaTokens[$i];
- if ($token->kind === TokenKind::DocCommentToken) {
- return $token->getText($this->getFileContents());
- }
- }
- return null;
- }
-
- public function __toString() {
- return $this->getText();
- }
-
- /**
- * @return array|ResolvedName[][]
- * @throws \Exception
- */
- public function getImportTablesForCurrentScope() {
- $namespaceDefinition = $this->getNamespaceDefinition();
-
- // Use declarations can exist in either the global scope, or inside namespace declarations.
- // http://php.net/manual/en/language.namespaces.importing.php#language.namespaces.importing.scope
- //
- // The only code allowed before a namespace declaration is a declare statement, and sub-namespaces are
- // additionally unaffected by by import rules of higher-level namespaces. Therefore, we can make the assumption
- // that we need not travel up the spine any further once we've found the current namespace.
- // http://php.net/manual/en/language.namespaces.definition.php
- if ($namespaceDefinition instanceof NamespaceDefinition) {
- $topLevelNamespaceStatements = $namespaceDefinition->compoundStatementOrSemicolon instanceof Token
- ? $namespaceDefinition->parent->statementList // we need to start from the namespace definition.
- : $namespaceDefinition->compoundStatementOrSemicolon->statements;
- $namespaceFullStart = $namespaceDefinition->getFullStartPosition();
- } else {
- $topLevelNamespaceStatements = $this->getRoot()->statementList;
- $namespaceFullStart = 0;
- }
-
- $nodeFullStart = $this->getFullStartPosition();
-
- // TODO optimize performance
- // Currently we rebuild the import tables on every call (and therefore every name resolution operation)
- // It is likely that a consumer will attempt many consecutive name resolution requests within the same file.
- // Therefore, we can consider optimizing on the basis of the "most recently used" import table set.
- // The idea: Keep a single set of import tables cached based on a unique root node id, and invalidate
- // cache whenever we attempt to resolve a qualified name with a different root node.
- //
- // In order to make this work, it will probably make sense to change the way we parse namespace definitions.
- // https://github.com/Microsoft/tolerant-php-parser/issues/81
- //
- // Currently the namespace definition only includes a compound statement or semicolon token as one if it's children.
- // Instead, we should move to a model where we parse future statements as a child rather than as a separate
- // statement. This would enable us to retrieve all the information we would need to find the fully qualified
- // name by simply traveling up the spine to find the first ancestor of type NamespaceDefinition.
- $namespaceImportTable = $functionImportTable = $constImportTable = [];
- $contents = $this->getFileContents();
-
- foreach ($topLevelNamespaceStatements as $useDeclaration) {
- if ($useDeclaration->getFullStartPosition() <= $namespaceFullStart) {
- continue;
- }
- if ($useDeclaration->getFullStartPosition() > $nodeFullStart) {
- break;
- } elseif (!($useDeclaration instanceof NamespaceUseDeclaration)) {
- continue;
- }
-
- // TODO fix getValues
- foreach ((isset($useDeclaration->useClauses) ? $useDeclaration->useClauses->getValues() : []) as $useClause) {
- $namespaceNamePartsPrefix = $useClause->namespaceName !== null ? $useClause->namespaceName->nameParts : [];
-
- if ($useClause->groupClauses !== null && $useClause instanceof NamespaceUseClause) {
- // use A\B\C\{D\E}; namespace import: ["E" => [A,B,C,D,E]]
- // use A\B\C\{D\E as F}; namespace import: ["F" => [A,B,C,D,E]]
- // use function A\B\C\{A, B} function import: ["A" => [A,B,C,A], "B" => [A,B,C]]
- // use function A\B\C\{const A} const import: ["A" => [A,B,C,A]]
- foreach ($useClause->groupClauses->children as $groupClause) {
- if (!($groupClause instanceof NamespaceUseGroupClause)) {
- continue;
- }
- $namespaceNameParts = \array_merge($namespaceNamePartsPrefix, $groupClause->namespaceName->nameParts);
- $functionOrConst = $groupClause->functionOrConst ?? $useDeclaration->functionOrConst;
- $alias = $groupClause->namespaceAliasingClause === null
- ? $groupClause->namespaceName->getLastNamePart()->getText($contents)
- : $groupClause->namespaceAliasingClause->name->getText($contents);
-
- $this->addToImportTable(
- $alias, $functionOrConst, $namespaceNameParts, $contents,
- $namespaceImportTable, $functionImportTable, $constImportTable
- );
- }
- } else {
- // use A\B\C; namespace import: ["C" => [A,B,C]]
- // use A\B\C as D; namespace import: ["D" => [A,B,C]]
- // use function A\B\C as D function import: ["D" => [A,B,C]]
- // use A\B, C\D; namespace import: ["B" => [A,B], "D" => [C,D]]
- $alias = $useClause->namespaceAliasingClause === null
- ? $useClause->namespaceName->getLastNamePart()->getText($contents)
- : $useClause->namespaceAliasingClause->name->getText($contents);
- $functionOrConst = $useDeclaration->functionOrConst;
- $namespaceNameParts = $namespaceNamePartsPrefix;
-
- $this->addToImportTable(
- $alias, $functionOrConst, $namespaceNameParts, $contents,
- $namespaceImportTable, $functionImportTable, $constImportTable
- );
- }
- }
- }
-
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- }
-
- /**
- * Gets corresponding NamespaceDefinition for Node. Returns null if in global namespace.
- *
- * @return NamespaceDefinition|null
- */
- public function getNamespaceDefinition() {
- $namespaceDefinition = ($this instanceof NamespaceDefinition || $this instanceof SourceFileNode)
- ? $this
- : $this->getFirstAncestor(NamespaceDefinition::class, SourceFileNode::class);
-
- if ($namespaceDefinition instanceof NamespaceDefinition && !($namespaceDefinition->parent instanceof SourceFileNode)) {
- $namespaceDefinition = $namespaceDefinition->getFirstAncestor(SourceFileNode::class);
- }
-
- if ($namespaceDefinition === null) {
- // TODO provide a way to throw errors without crashing consumer
- throw new \Exception("Invalid tree - SourceFileNode must always exist at root of tree.");
- }
-
- $fullStart = $this->getFullStartPosition();
- $lastNamespaceDefinition = null;
- if ($namespaceDefinition instanceof SourceFileNode) {
- foreach ($namespaceDefinition->getChildNodes() as $childNode) {
- if ($childNode instanceof NamespaceDefinition && $childNode->getFullStartPosition() < $fullStart) {
- $lastNamespaceDefinition = $childNode;
- }
- }
- }
-
- if ($lastNamespaceDefinition !== null && $lastNamespaceDefinition->compoundStatementOrSemicolon instanceof Token) {
- $namespaceDefinition = $lastNamespaceDefinition;
- } elseif ($namespaceDefinition instanceof SourceFileNode) {
- $namespaceDefinition = null;
- }
-
- return $namespaceDefinition;
- }
-
- public function getPreviousSibling() {
- // TODO make more efficient
- $parent = $this->parent;
- if ($parent === null) {
- return null;
- }
-
- $prevSibling = null;
-
- foreach ($parent::CHILD_NAMES as $name) {
- $val = $parent->$name;
- if (\is_array($val)) {
- foreach ($val as $sibling) {
- if ($sibling === $this) {
- return $prevSibling;
- } elseif ($sibling instanceof Node) {
- $prevSibling = $sibling;
- }
- }
- continue;
- } elseif ($val instanceof Node) {
- if ($val === $this) {
- return $prevSibling;
- }
- $prevSibling = $val;
- }
- }
- return null;
- }
-
- /**
- * Add the alias and resolved name to the corresponding namespace, function, or const import table.
- * If the alias already exists, it will get replaced by the most recent using.
- *
- * TODO - worth throwing an error here instead?
- */
- private function addToImportTable($alias, $functionOrConst, $namespaceNameParts, $contents, & $namespaceImportTable, & $functionImportTable, & $constImportTable):array
- {
- if ($alias !== null) {
- if ($functionOrConst === null) {
- // namespaces are case-insensitive
-// $alias = \strtolower($alias);
- $namespaceImportTable[$alias] = ResolvedName::buildName($namespaceNameParts, $contents);
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- } elseif ($functionOrConst->kind === TokenKind::FunctionKeyword) {
- // functions are case-insensitive
-// $alias = \strtolower($alias);
- $functionImportTable[$alias] = ResolvedName::buildName($namespaceNameParts, $contents);
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- } elseif ($functionOrConst->kind === TokenKind::ConstKeyword) {
- // constants are case-sensitive
- $constImportTable[$alias] = ResolvedName::buildName($namespaceNameParts, $contents);
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- }
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- }
- return [$namespaceImportTable, $functionImportTable, $constImportTable];
- }
-
- /**
- * This is overridden in subclasses
- * @return Diagnostic|null - Callers should use DiagnosticsProvider::getDiagnostics instead
- * @internal
- */
- public function getDiagnosticForNode() {
- return null;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/AnonymousFunctionUseClause.php b/vendor/microsoft/tolerant-php-parser/src/Node/AnonymousFunctionUseClause.php
deleted file mode 100644
index 26460ea6..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/AnonymousFunctionUseClause.php
+++ /dev/null
@@ -1,33 +0,0 @@
-name];
- }
-
- public function getName() {
- return $this->name->getText($this->getFileContents());
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/DeclareDirective.php b/vendor/microsoft/tolerant-php-parser/src/Node/DeclareDirective.php
deleted file mode 100644
index 55e9af21..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/DeclareDirective.php
+++ /dev/null
@@ -1,25 +0,0 @@
-children as $child) {
- if ($child instanceof Node) {
- yield $child;
- } elseif ($child instanceof Token && !\in_array($child->kind, self::DELIMITERS)) {
- yield $child;
- }
- }
- }
-
- public function getValues() {
- foreach ($this->children as $idx=>$value) {
- if ($idx % 2 == 0) {
- yield $value;
- }
- }
- }
-
- public function addElement($node) {
- if ($node === null) {
- return;
- }
- if ($this->children === null) {
- $this->children = [$node];
- return;
- }
- $this->children[] = $node;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/DelimitedList/ArgumentExpressionList.php b/vendor/microsoft/tolerant-php-parser/src/Node/DelimitedList/ArgumentExpressionList.php
deleted file mode 100644
index 0815ee5f..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/DelimitedList/ArgumentExpressionList.php
+++ /dev/null
@@ -1,12 +0,0 @@
-` */
- public $arrowToken;
-
- /** @var Node|Token */
- public $resultExpression;
-
- const CHILD_NAMES = [
- 'attributes',
- 'staticModifier',
-
- // FunctionHeader
- 'functionKeyword',
- 'byRefToken',
- 'name',
- 'openParen',
- 'parameters',
- 'closeParen',
-
- // FunctionReturnType
- 'colonToken',
- 'questionToken',
- 'returnTypeList',
-
- // body
- 'arrowToken',
- 'resultExpression',
- ];
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Expression/AssignmentExpression.php b/vendor/microsoft/tolerant-php-parser/src/Node/Expression/AssignmentExpression.php
deleted file mode 100644
index dc1683bc..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Expression/AssignmentExpression.php
+++ /dev/null
@@ -1,32 +0,0 @@
-name instanceof Token &&
- $name = ltrim($this->name->getText($this->getFileContents()), '$')
- ) {
- return $name;
- }
- return null;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Expression/YieldExpression.php b/vendor/microsoft/tolerant-php-parser/src/Node/Expression/YieldExpression.php
deleted file mode 100644
index 63da2956..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Expression/YieldExpression.php
+++ /dev/null
@@ -1,21 +0,0 @@
-name->getText($this->getFileContents());
- }
-
- /**
- * @return Diagnostic|null - Callers should use DiagnosticsProvider::getDiagnostics instead
- * @internal
- * @override
- */
- public function getDiagnosticForNode() {
- foreach ($this->modifiers as $modifier) {
- if ($modifier->kind === TokenKind::VarKeyword) {
- return new Diagnostic(
- DiagnosticKind::Error,
- "Unexpected modifier '" . DiagnosticsProvider::getTextForTokenKind($modifier->kind) . "'",
- $modifier->start,
- $modifier->length
- );
- }
- }
- return null;
- }
-
- /**
- * Returns the signature parts as an array. Use $this::getSignatureFormatted for a user-friendly string version.
- *
- * @return array
- */
- private function getSignatureParts(): array {
- $parts = [];
-
- foreach ($this->getChildNodesAndTokens() as $i => $child) {
- if ($i === "compoundStatementOrSemicolon") {
- return $parts;
- }
-
- $parts[] = $child instanceof Token
- ? $child->getText($this->getFileContents())
- : $child->getText();
- };
-
- return $parts;
- }
-
- /**
- * Returns the signature of the method as a formatted string.
- *
- * @return string
- */
- public function getSignatureFormatted(): string {
- $signature = implode(" ", $this->getSignatureParts());
- return $signature;
- }
-
- /**
- * Returns the description part of the doc string.
- *
- * @return string
- */
- public function getDescriptionFormatted(): string {
- $comment = trim($this->getLeadingCommentAndWhitespaceText(), "\r\n");
- $commentParts = explode("\n", $comment);
-
- $description = [];
-
- foreach ($commentParts as $i => $part) {
- $part = trim($part, "*\r\t /");
-
- if (strlen($part) <= 0) {
- continue;
- }
-
- if ($part[0] === "@") {
- break;
- }
-
- $description[] = $part;
- }
-
- $descriptionFormatted = implode(" ", $description);
- return $descriptionFormatted;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/MissingDeclaration.php b/vendor/microsoft/tolerant-php-parser/src/Node/MissingDeclaration.php
deleted file mode 100644
index 82867e25..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/MissingDeclaration.php
+++ /dev/null
@@ -1,23 +0,0 @@
-byRefToken !== null;
- }
-
- public function getName() {
- if (
- $this->variableName instanceof Token &&
- $name = substr($this->variableName->getText($this->getFileContents()), 1)
- ) {
- return $name;
- }
- return null;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/PropertyDeclaration.php b/vendor/microsoft/tolerant-php-parser/src/Node/PropertyDeclaration.php
deleted file mode 100644
index 1995aed5..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/PropertyDeclaration.php
+++ /dev/null
@@ -1,42 +0,0 @@
-globalSpecifier);
- }
-
- /**
- * Checks whether a QualifiedName begins with a "namespace" keyword
- * @return bool
- */
- public function isRelativeName() : bool {
- return isset($this->relativeSpecifier);
- }
-
- /**
- * Checks whether a Name includes at least one namespace separator (and is neither fully-qualified nor relative)
- * @return bool
- */
- public function isQualifiedName() : bool {
- return
- !$this->isFullyQualifiedName() &&
- !$this->isRelativeName() &&
- \count($this->nameParts) > 1; // at least one namespace separator
- }
-
- /**
- * Checks whether a name is does not include a namespace separator.
- * @return bool
- */
- public function isUnqualifiedName() : bool {
- return !($this->isFullyQualifiedName() || $this->isRelativeName() || $this->isQualifiedName());
- }
-
- /**
- * Gets resolved name based on name resolution rules defined in:
- * http://php.net/manual/en/language.namespaces.rules.php
- *
- * Returns null if one of the following conditions is met:
- * - name resolution is not valid for this element (e.g. part of the name in a namespace definition)
- * - name cannot be resolved (unqualified namespaced function/constant names that are not explicitly imported.)
- *
- * @return null|string|ResolvedName
- * @throws \Exception
- */
- public function getResolvedName($namespaceDefinition = null) {
- // Name resolution not applicable to constructs that define symbol names or aliases.
- if (($this->parent instanceof Node\Statement\NamespaceDefinition && $this->parent->name->getStartPosition() === $this->getStartPosition()) ||
- $this->parent instanceof Node\Statement\NamespaceUseDeclaration ||
- $this->parent instanceof Node\NamespaceUseClause ||
- $this->parent instanceof Node\NamespaceUseGroupClause ||
- $this->parent->parent instanceof Node\TraitUseClause ||
- $this->parent instanceof Node\TraitSelectOrAliasClause ||
- ($this->parent instanceof TraitSelectOrAliasClause &&
- ($this->parent->asOrInsteadOfKeyword == null || $this->parent->asOrInsteadOfKeyword->kind === TokenKind::AsKeyword))
- ) {
- return null;
- }
-
- if (in_array($lowerText = strtolower($this->getText()), ["self", "static", "parent"])) {
- return $lowerText;
- }
-
- // FULLY QUALIFIED NAMES
- // - resolve to the name without leading namespace separator.
- if ($this->isFullyQualifiedName()) {
- return ResolvedName::buildName($this->nameParts, $this->getFileContents());
- }
-
- // RELATIVE NAMES
- // - resolve to the name with namespace replaced by the current namespace.
- // - if current namespace is global, strip leading namespace\ prefix.
- if ($this->isRelativeName()) {
- return $this->getNamespacedName();
- }
-
- [$namespaceImportTable, $functionImportTable, $constImportTable] = $this->getImportTablesForCurrentScope();
-
- // QUALIFIED NAMES
- // - first segment of the name is translated according to the current class/namespace import table.
- // - If no import rule applies, the current namespace is prepended to the name.
- if ($this->isQualifiedName()) {
- return $this->tryResolveFromImportTable($namespaceImportTable) ?? $this->getNamespacedName();
- }
-
- // UNQUALIFIED NAMES
- // - translated according to the current import table for the respective symbol type.
- // (class-like => namespace import table, constant => const import table, function => function import table)
- // - if no import rule applies:
- // - all symbol types: if current namespace is global, resolve to global namespace.
- // - class-like symbols: resolve from current namespace.
- // - function or const: resolved at runtime (from current namespace, with fallback to global namespace).
- if ($this->isConstantName()) {
- $resolvedName = $this->tryResolveFromImportTable($constImportTable, /* case-sensitive */ true);
- $namespaceDefinition = $this->getNamespaceDefinition();
- if ($namespaceDefinition !== null && $namespaceDefinition->name === null) {
- $resolvedName = $resolvedName ?? ResolvedName::buildName($this->nameParts, $this->getFileContents());
- }
- return $resolvedName;
- } elseif ($this->parent instanceof CallExpression) {
- $resolvedName = $this->tryResolveFromImportTable($functionImportTable);
- if (($namespaceDefinition = $this->getNamespaceDefinition()) === null || $namespaceDefinition->name === null) {
- $resolvedName = $resolvedName ?? ResolvedName::buildName($this->nameParts, $this->getFileContents());
- }
- return $resolvedName;
- }
-
- return $this->tryResolveFromImportTable($namespaceImportTable) ?? $this->getNamespacedName();
- }
-
- public function getLastNamePart() {
- $parts = $this->nameParts;
- for ($i = \count($parts) - 1; $i >= 0; $i--) {
- // TODO - also handle reserved word tokens
- if ($parts[$i]->kind === TokenKind::Name) {
- return $parts[$i];
- }
- }
- return null;
- }
-
- /**
- * @param ResolvedName[] $importTable
- * @param bool $isCaseSensitive
- * @return string|null
- */
- private function tryResolveFromImportTable($importTable, bool $isCaseSensitive = false) {
- $content = $this->getFileContents();
- $index = $this->nameParts[0]->getText($content);
-// if (!$isCaseSensitive) {
-// $index = strtolower($index);
-// }
- if(isset($importTable[$index])) {
- $resolvedName = $importTable[$index];
- $resolvedName->addNameParts(\array_slice($this->nameParts, 1), $content);
- return $resolvedName;
- }
- return null;
- }
-
- private function isConstantName() : bool {
- return
- ($this->parent instanceof Node\Statement\ExpressionStatement || $this->parent instanceof Expression) &&
- !(
- $this->parent instanceof Node\Expression\MemberAccessExpression || $this->parent instanceof CallExpression ||
- $this->parent instanceof ObjectCreationExpression ||
- $this->parent instanceof Node\Expression\ScopedPropertyAccessExpression || $this->parent instanceof AnonymousFunctionCreationExpression ||
- $this->parent instanceof ArrowFunctionCreationExpression ||
- ($this->parent instanceof Node\Expression\BinaryExpression && $this->parent->operator->kind === TokenKind::InstanceOfKeyword)
- );
- }
-
- public function getNameParts() : array {
- return $this->nameParts;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/RelativeSpecifier.php b/vendor/microsoft/tolerant-php-parser/src/Node/RelativeSpecifier.php
deleted file mode 100644
index 8c301edb..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/RelativeSpecifier.php
+++ /dev/null
@@ -1,23 +0,0 @@
-breakoutLevel === null) {
- return null;
- }
-
- $breakoutLevel = $this->breakoutLevel;
- while ($breakoutLevel instanceof Node\Expression\ParenthesizedExpression) {
- $breakoutLevel = $breakoutLevel->expression;
- }
-
- if (
- $breakoutLevel instanceof Node\NumericLiteral
- && $breakoutLevel->children->kind === TokenKind::IntegerLiteralToken
- ) {
- $literalString = $breakoutLevel->getText();
- $firstTwoChars = \substr($literalString, 0, 2);
-
- if ($firstTwoChars === '0b' || $firstTwoChars === '0B') {
- if (\bindec(\substr($literalString, 2)) > 0) {
- return null;
- }
- }
- else if (\intval($literalString, 0) > 0) {
- return null;
- }
- }
-
- $start = $breakoutLevel->getStartPosition();
- $end = $breakoutLevel->getEndPosition();
-
- return new Diagnostic(
- DiagnosticKind::Error,
- "Positive integer literal expected.",
- $start,
- $end - $start
- );
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ClassDeclaration.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ClassDeclaration.php
deleted file mode 100644
index 8a4e6df0..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ClassDeclaration.php
+++ /dev/null
@@ -1,56 +0,0 @@
-name];
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/CompoundStatementNode.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/CompoundStatementNode.php
deleted file mode 100644
index 739e5b38..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/CompoundStatementNode.php
+++ /dev/null
@@ -1,28 +0,0 @@
-name];
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ExpressionStatement.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ExpressionStatement.php
deleted file mode 100644
index 96e7afef..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ExpressionStatement.php
+++ /dev/null
@@ -1,23 +0,0 @@
-name];
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/FunctionStaticDeclaration.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/FunctionStaticDeclaration.php
deleted file mode 100644
index b96608de..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/FunctionStaticDeclaration.php
+++ /dev/null
@@ -1,29 +0,0 @@
-name];
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/NamedLabelStatement.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/NamedLabelStatement.php
deleted file mode 100644
index 29e66260..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/NamedLabelStatement.php
+++ /dev/null
@@ -1,22 +0,0 @@
-useClauses != null
- && \count($this->useClauses->children) > 1
- ) {
- foreach ($this->useClauses->children as $useClause) {
- if($useClause instanceof Node\NamespaceUseClause && !is_null($useClause->openBrace)) {
- return new Diagnostic(
- DiagnosticKind::Error,
- "; expected.",
- $useClause->getEndPosition(),
- 1
- );
- }
- }
- }
- return null;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ReturnStatement.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ReturnStatement.php
deleted file mode 100644
index 50353c16..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/ReturnStatement.php
+++ /dev/null
@@ -1,26 +0,0 @@
-name];
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/TryStatement.php b/vendor/microsoft/tolerant-php-parser/src/Node/Statement/TryStatement.php
deleted file mode 100644
index b0e503ca..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/Statement/TryStatement.php
+++ /dev/null
@@ -1,30 +0,0 @@
-startQuote)) {
- foreach ($this->children as $child) {
- $contents = $this->getFileContents();
- $stringContents .= $child->getFullText($contents);
- }
- } else {
- // TODO ensure string consistency (all strings should have start / end quote)
- $stringContents = trim($this->children->getText($this->getFileContents()), '"\'');
- }
- return $stringContents;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Node/TraitMembers.php b/vendor/microsoft/tolerant-php-parser/src/Node/TraitMembers.php
deleted file mode 100644
index 53c375c1..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Node/TraitMembers.php
+++ /dev/null
@@ -1,27 +0,0 @@
-variableName instanceof Token &&
- $name = substr($this->variableName->getText($this->getFileContents()), 1)
- ) {
- return $name;
- }
- return null;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/ParseContext.php b/vendor/microsoft/tolerant-php-parser/src/ParseContext.php
deleted file mode 100644
index 33c73c46..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/ParseContext.php
+++ /dev/null
@@ -1,24 +0,0 @@
-reservedWordTokens = \array_values(TokenStringMaps::RESERVED_WORDS);
- $this->keywordTokens = \array_values(TokenStringMaps::KEYWORDS);
- $this->argumentStartTokensSet = \array_flip(TokenStringMaps::KEYWORDS);
- unset($this->argumentStartTokensSet[TokenKind::YieldFromKeyword]);
- $this->argumentStartTokensSet[TokenKind::DotDotDotToken] = '...';
- $this->nameOrKeywordOrReservedWordTokens = \array_merge([TokenKind::Name], $this->keywordTokens, $this->reservedWordTokens);
- $this->nameOrReservedWordTokens = \array_merge([TokenKind::Name], $this->reservedWordTokens);
- $this->nameOrStaticOrReservedWordTokens = \array_merge([TokenKind::Name, TokenKind::StaticKeyword], $this->reservedWordTokens);
- $this->parameterTypeDeclarationTokens =
- [TokenKind::ArrayKeyword, TokenKind::CallableKeyword, TokenKind::BoolReservedWord,
- TokenKind::FloatReservedWord, TokenKind::IntReservedWord, TokenKind::StringReservedWord,
- TokenKind::ObjectReservedWord, TokenKind::NullReservedWord, TokenKind::FalseReservedWord,
- TokenKind::IterableReservedWord, TokenKind::MixedReservedWord]; // TODO update spec
- $this->returnTypeDeclarationTokens = \array_merge([TokenKind::VoidReservedWord, TokenKind::NullReservedWord, TokenKind::FalseReservedWord, TokenKind::StaticKeyword], $this->parameterTypeDeclarationTokens);
- }
-
- /**
- * This method exists so that it can be overridden in subclasses.
- * Any subclass must return a token stream that is equivalent to the contents in $fileContents for this to work properly.
- *
- * Possible reasons for applications to override the lexer:
- *
- * - Imitate token stream of a newer/older PHP version (e.g. T_FN is only available in php 7.4)
- * - Reuse the result of token_get_all to create a Node again.
- * - Reuse the result of token_get_all in a different library.
- */
- protected function makeLexer(string $fileContents): TokenStreamProviderInterface
- {
- return TokenStreamProviderFactory::GetTokenStreamProvider($fileContents);
- }
-
- /**
- * Generates AST from source file contents. Returns an instance of SourceFileNode, which is always the top-most
- * Node-type of the tree.
- *
- * @param string $fileContents
- * @return SourceFileNode
- */
- public function parseSourceFile(string $fileContents, string $uri = null) : SourceFileNode {
- $this->lexer = $this->makeLexer($fileContents);
-
- $this->reset();
-
- $sourceFile = new SourceFileNode();
- $this->sourceFile = $sourceFile;
- $sourceFile->fileContents = $fileContents;
- $sourceFile->uri = $uri;
- $sourceFile->statementList = [];
- if ($this->getCurrentToken()->kind !== TokenKind::EndOfFileToken) {
- $inlineHTML = $this->parseInlineHtml($sourceFile);
- $sourceFile->statementList[] = $inlineHTML;
- if ($inlineHTML->echoStatement) {
- $sourceFile->statementList[] = $inlineHTML->echoStatement;
- $inlineHTML->echoStatement->parent = $sourceFile;
- $inlineHTML->echoStatement = null;
- }
- }
- $sourceFile->statementList =
- \array_merge($sourceFile->statementList, $this->parseList($sourceFile, ParseContext::SourceElements));
-
- $this->sourceFile->endOfFileToken = $this->eat1(TokenKind::EndOfFileToken);
- $this->advanceToken();
-
- $sourceFile->parent = null;
-
- return $sourceFile;
- }
-
- private function reset() {
- $this->advanceToken();
-
- // Stores the current parse context, which includes the current and enclosing lists.
- $this->currentParseContext = 0;
- }
-
- /**
- * Parse a list of elements for a given ParseContext until a list terminator associated
- * with that ParseContext is reached. Additionally abort parsing when an element is reached
- * that is invalid in the current context, but valid in an enclosing context. If an element
- * is invalid in both current and enclosing contexts, generate a SkippedToken, and continue.
- * @param Node $parentNode
- * @param int $listParseContext
- * @return array
- */
- private function parseList($parentNode, int $listParseContext) {
- $savedParseContext = $this->currentParseContext;
- $this->currentParseContext |= 1 << $listParseContext;
- $parseListElementFn = $this->getParseListElementFn($listParseContext);
-
- $nodeArray = [];
- while (!$this->isListTerminator($listParseContext)) {
- if ($this->isValidListElement($listParseContext, $this->getCurrentToken())) {
- $element = $parseListElementFn($parentNode);
- $nodeArray[] = $element;
- if ($element instanceof Node) {
- $element->parent = $parentNode;
- if ($element instanceof InlineHtml && $element->echoStatement) {
- $nodeArray[] = $element->echoStatement;
- $element->echoStatement->parent = $parentNode;
- $element->echoStatement = null;
- }
- }
- continue;
- }
-
- // Error handling logic:
- // The current parse context does not know how to handle the current token,
- // so check if the enclosing contexts know what to do. If so, we assume that
- // the list has completed parsing, and return to the enclosing context.
- //
- // Example:
- // class A {
- // function foo() {
- // return;
- // // } <- MissingToken (generated when we try to "eat" the closing brace)
- //
- // public function bar() {
- // }
- // }
- //
- // In the case above, the Method ParseContext doesn't know how to handle "public", but
- // the Class ParseContext will know what to do with it. So we abort the Method ParseContext,
- // and return to the Class ParseContext. This enables us to generate a tree with a single
- // class that contains two method nodes, even though there was an error present in the first method.
- if ($this->isCurrentTokenValidInEnclosingContexts()) {
- break;
- }
-
- // None of the enclosing contexts know how to handle the token. Generate a
- // SkippedToken, and continue parsing in the current context.
- // Example:
- // class A {
- // function foo() {
- // return;
- // & // <- SkippedToken
- // }
- // }
- $token = new SkippedToken($this->getCurrentToken());
- $nodeArray[] = $token;
- $this->advanceToken();
- }
-
- $this->currentParseContext = $savedParseContext;
-
- return $nodeArray;
- }
-
- private function isListTerminator(int $parseContext) {
- $tokenKind = $this->getCurrentToken()->kind;
- if ($tokenKind === TokenKind::EndOfFileToken) {
- // Being at the end of the file ends all lists.
- return true;
- }
-
- switch ($parseContext) {
- case ParseContext::SourceElements:
- return false;
-
- case ParseContext::InterfaceMembers:
- case ParseContext::ClassMembers:
- case ParseContext::BlockStatements:
- case ParseContext::TraitMembers:
- case ParseContext::EnumMembers:
- return $tokenKind === TokenKind::CloseBraceToken;
- case ParseContext::SwitchStatementElements:
- return $tokenKind === TokenKind::CloseBraceToken || $tokenKind === TokenKind::EndSwitchKeyword;
- case ParseContext::IfClause2Elements:
- return
- $tokenKind === TokenKind::ElseIfKeyword ||
- $tokenKind === TokenKind::ElseKeyword ||
- $tokenKind === TokenKind::EndIfKeyword;
-
- case ParseContext::WhileStatementElements:
- return $tokenKind === TokenKind::EndWhileKeyword;
-
- case ParseContext::CaseStatementElements:
- return
- $tokenKind === TokenKind::CaseKeyword ||
- $tokenKind === TokenKind::DefaultKeyword;
-
- case ParseContext::ForStatementElements:
- return
- $tokenKind === TokenKind::EndForKeyword;
-
- case ParseContext::ForeachStatementElements:
- return $tokenKind === TokenKind::EndForEachKeyword;
-
- case ParseContext::DeclareStatementElements:
- return $tokenKind === TokenKind::EndDeclareKeyword;
- }
- // TODO warn about unhandled parse context
- return false;
- }
-
- private function isValidListElement($context, Token $token) {
-
- // TODO
- switch ($context) {
- case ParseContext::SourceElements:
- case ParseContext::BlockStatements:
- case ParseContext::IfClause2Elements:
- case ParseContext::CaseStatementElements:
- case ParseContext::WhileStatementElements:
- case ParseContext::ForStatementElements:
- case ParseContext::ForeachStatementElements:
- case ParseContext::DeclareStatementElements:
- return $this->isStatementStart($token);
-
- case ParseContext::ClassMembers:
- return $this->isClassMemberDeclarationStart($token);
-
- case ParseContext::TraitMembers:
- return $this->isTraitMemberDeclarationStart($token);
-
- case ParseContext::EnumMembers:
- return $this->isEnumMemberDeclarationStart($token);
-
- case ParseContext::InterfaceMembers:
- return $this->isInterfaceMemberDeclarationStart($token);
-
- case ParseContext::SwitchStatementElements:
- return
- $token->kind === TokenKind::CaseKeyword ||
- $token->kind === TokenKind::DefaultKeyword;
- }
- return false;
- }
-
- private function getParseListElementFn($context) {
- switch ($context) {
- case ParseContext::SourceElements:
- case ParseContext::BlockStatements:
- case ParseContext::IfClause2Elements:
- case ParseContext::CaseStatementElements:
- case ParseContext::WhileStatementElements:
- case ParseContext::ForStatementElements:
- case ParseContext::ForeachStatementElements:
- case ParseContext::DeclareStatementElements:
- return $this->parseStatementFn();
- case ParseContext::ClassMembers:
- return $this->parseClassElementFn();
-
- case ParseContext::TraitMembers:
- return $this->parseTraitElementFn();
-
- case ParseContext::InterfaceMembers:
- return $this->parseInterfaceElementFn();
-
- case ParseContext::EnumMembers:
- return $this->parseEnumElementFn();
-
- case ParseContext::SwitchStatementElements:
- return $this->parseCaseOrDefaultStatement();
- default:
- throw new \Exception("Unrecognized parse context");
- }
- }
-
- /**
- * Aborts parsing list when one of the parent contexts understands something
- * @return bool
- */
- private function isCurrentTokenValidInEnclosingContexts() {
- for ($contextKind = 0; $contextKind < ParseContext::Count; $contextKind++) {
- if ($this->isInParseContext($contextKind)) {
- if ($this->isValidListElement($contextKind, $this->getCurrentToken()) || $this->isListTerminator($contextKind)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private function isInParseContext($contextToCheck) {
- return ($this->currentParseContext & (1 << $contextToCheck));
- }
-
- /**
- * Retrieve the current token, and check that it's of the expected TokenKind.
- * If so, advance and return the token. Otherwise return a MissingToken for
- * the expected token.
- * @param int|int[] ...$kinds
- * @return Token
- */
- private function eat(...$kinds) {
- $token = $this->token;
- if (\is_array($kinds[0])) {
- $kinds = $kinds[0];
- }
- foreach ($kinds as $kind) {
- if ($token->kind === $kind) {
- $this->token = $this->lexer->scanNextToken();
- return $token;
- }
- }
- // TODO include optional grouping for token kinds
- return new MissingToken($kinds[0], $token->fullStart);
- }
-
- /**
- * Retrieve the current token, and check that it's of the kind $kind.
- * If so, advance and return the token. Otherwise return a MissingToken for
- * the expected token.
- *
- * This is faster than calling eat() if there is a single token.
- *
- * @param int $kind
- * @return Token
- */
- private function eat1($kind) {
- $token = $this->token;
- if ($token->kind === $kind) {
- $this->token = $this->lexer->scanNextToken();
- return $token;
- }
- // TODO include optional grouping for token kinds
- return new MissingToken($kind, $token->fullStart);
- }
-
- /**
- * @param int|int[] ...$kinds (Can provide a single value with a list of kinds, or multiple kinds)
- * @return Token|null
- */
- private function eatOptional(...$kinds) {
- $token = $this->token;
- if (\is_array($kinds[0])) {
- $kinds = $kinds[0];
- }
- if (\in_array($token->kind, $kinds)) {
- $this->token = $this->lexer->scanNextToken();
- return $token;
- }
- return null;
- }
-
- /**
- * @param int $kind a single kind
- * @return Token|null
- */
- private function eatOptional1($kind) {
- $token = $this->token;
- if ($token->kind === $kind) {
- $this->token = $this->lexer->scanNextToken();
- return $token;
- }
- return null;
- }
-
- private $token;
-
- private function getCurrentToken() : Token {
- return $this->token;
- }
-
- private function advanceToken() {
- $this->token = $this->lexer->scanNextToken();
- }
-
- private function parseStatement($parentNode) {
- return ($this->parseStatementFn())($parentNode);
- }
-
- private function parseStatementFn() {
- return function ($parentNode) {
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- // compound-statement
- case TokenKind::OpenBraceToken:
- return $this->parseCompoundStatement($parentNode);
-
- // labeled-statement
- case TokenKind::Name:
- if ($this->lookahead(TokenKind::ColonToken)) {
- return $this->parseNamedLabelStatement($parentNode);
- }
- break;
-
- // selection-statement
- case TokenKind::IfKeyword:
- return $this->parseIfStatement($parentNode);
- case TokenKind::SwitchKeyword:
- return $this->parseSwitchStatement($parentNode);
-
- // iteration-statement
- case TokenKind::WhileKeyword: // while-statement
- return $this->parseWhileStatement($parentNode);
- case TokenKind::DoKeyword: // do-statement
- return $this->parseDoStatement($parentNode);
- case TokenKind::ForKeyword: // for-statement
- return $this->parseForStatement($parentNode);
- case TokenKind::ForeachKeyword: // foreach-statement
- return $this->parseForeachStatement($parentNode);
-
- // jump-statement
- case TokenKind::GotoKeyword: // goto-statement
- return $this->parseGotoStatement($parentNode);
- case TokenKind::ContinueKeyword: // continue-statement
- case TokenKind::BreakKeyword: // break-statement
- return $this->parseBreakOrContinueStatement($parentNode);
- case TokenKind::ReturnKeyword: // return-statement
- return $this->parseReturnStatement($parentNode);
-
- // try-statement
- case TokenKind::TryKeyword:
- return $this->parseTryStatement($parentNode);
-
- // declare-statement
- case TokenKind::DeclareKeyword:
- return $this->parseDeclareStatement($parentNode);
-
- // attribute before statement or anonymous function
- case TokenKind::AttributeToken:
- return $this->parseAttributeStatement($parentNode);
-
- // function-declaration
- case TokenKind::FunctionKeyword:
- // Check that this is not an anonymous-function-creation-expression
- if ($this->lookahead($this->nameOrKeywordOrReservedWordTokens) || $this->lookahead(TokenKind::AmpersandToken, $this->nameOrKeywordOrReservedWordTokens)) {
- return $this->parseFunctionDeclaration($parentNode);
- }
- break;
-
- // class-declaration
- case TokenKind::FinalKeyword:
- case TokenKind::AbstractKeyword:
- if (!$this->lookahead(TokenKind::ClassKeyword)) {
- $this->advanceToken();
- return new SkippedToken($token);
- }
- case TokenKind::ClassKeyword:
- return $this->parseClassDeclaration($parentNode);
-
- // interface-declaration
- case TokenKind::InterfaceKeyword:
- return $this->parseInterfaceDeclaration($parentNode);
-
- // namespace-definition
- case TokenKind::NamespaceKeyword:
- if (!$this->lookahead(TokenKind::BackslashToken)) {
- // TODO add error handling for the case where a namespace definition does not occur in the outer-most scope
- return $this->parseNamespaceDefinition($parentNode);
- }
- break;
-
- // namespace-use-declaration
- case TokenKind::UseKeyword:
- return $this->parseNamespaceUseDeclaration($parentNode);
-
- case TokenKind::SemicolonToken:
- return $this->parseEmptyStatement($parentNode);
-
- case TokenKind::EchoKeyword:
- return $this->parseEchoStatement($parentNode);
-
- // trait-declaration
- case TokenKind::TraitKeyword:
- return $this->parseTraitDeclaration($parentNode);
-
- case TokenKind::EnumKeyword:
- return $this->parseEnumDeclaration($parentNode);
-
- // global-declaration
- case TokenKind::GlobalKeyword:
- return $this->parseGlobalDeclaration($parentNode);
-
- // const-declaration
- case TokenKind::ConstKeyword:
- return $this->parseConstDeclaration($parentNode);
-
- // function-static-declaration
- case TokenKind::StaticKeyword:
- // Check that this is not an anonymous-function-creation-expression
- if (!$this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::OpenParenToken, TokenKind::ColonColonToken])) {
- return $this->parseFunctionStaticDeclaration($parentNode);
- }
- break;
-
- case TokenKind::ScriptSectionEndTag:
- return $this->parseInlineHtml($parentNode);
-
- case TokenKind::UnsetKeyword:
- return $this->parseUnsetStatement($parentNode);
- }
-
- $expressionStatement = new ExpressionStatement();
- $expressionStatement->parent = $parentNode;
- $expressionStatement->expression = $this->parseExpression($expressionStatement, true);
- $expressionStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- return $expressionStatement;
- };
- }
-
- private function parseClassElementFn() {
- return function ($parentNode) {
- $modifiers = $this->parseModifiers();
-
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- case TokenKind::ConstKeyword:
- return $this->parseClassConstDeclaration($parentNode, $modifiers);
-
- case TokenKind::FunctionKeyword:
- return $this->parseMethodDeclaration($parentNode, $modifiers);
-
- case TokenKind::QuestionToken:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration(
- $parentNode,
- $modifiers,
- $this->eat1(TokenKind::QuestionToken)
- );
- case TokenKind::VariableName:
- return $this->parsePropertyDeclaration($parentNode, $modifiers);
-
- case TokenKind::UseKeyword:
- return $this->parseTraitUseClause($parentNode);
-
- case TokenKind::AttributeToken:
- return $this->parseAttributeStatement($parentNode);
-
- default:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration($parentNode, $modifiers);
- }
- };
- }
-
- private function parseClassDeclaration($parentNode) : Node {
- $classNode = new ClassDeclaration(); // TODO verify not nested
- $classNode->parent = $parentNode;
- $classNode->abstractOrFinalModifier = $this->eatOptional(TokenKind::AbstractKeyword, TokenKind::FinalKeyword);
- $classNode->classKeyword = $this->eat1(TokenKind::ClassKeyword);
- $classNode->name = $this->eat($this->nameOrReservedWordTokens); // TODO should be any
- $classNode->name->kind = TokenKind::Name;
- $classNode->classBaseClause = $this->parseClassBaseClause($classNode);
- $classNode->classInterfaceClause = $this->parseClassInterfaceClause($classNode);
- $classNode->classMembers = $this->parseClassMembers($classNode);
- return $classNode;
- }
-
- private function parseClassMembers($parentNode) : Node {
- $classMembers = new ClassMembersNode();
- $classMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $classMembers->classMemberDeclarations = $this->parseList($classMembers, ParseContext::ClassMembers);
- $classMembers->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- $classMembers->parent = $parentNode;
- return $classMembers;
- }
-
- private function parseFunctionDeclaration($parentNode) {
- $functionNode = new FunctionDeclaration();
- $this->parseFunctionType($functionNode);
- $functionNode->parent = $parentNode;
- return $functionNode;
- }
-
- /**
- * @return Node
- */
- private function parseAttributeExpression($parentNode) {
- $attributeGroups = $this->parseAttributeGroups(null);
- // Warn about invalid syntax for attributed declarations
- // Lookahead for static, function, or fn for the only type of expressions that can have attributes (anonymous functions)
- if (in_array($this->token->kind, [TokenKind::FunctionKeyword, TokenKind::FnKeyword], true) ||
- $this->token->kind === TokenKind::StaticKeyword && $this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
- $expression = $this->parsePrimaryExpression($parentNode);
- } else {
- // Create a MissingToken so that diagnostics indicate that the attributes did not match up with an expression/declaration.
- $expression = new MissingDeclaration();
- $expression->parent = $parentNode;
- $expression->declaration = new MissingToken(TokenKind::Expression, $this->token->fullStart);
- }
- if ($expression instanceof AnonymousFunctionCreationExpression ||
- $expression instanceof ArrowFunctionCreationExpression ||
- $expression instanceof MissingDeclaration) {
- $expression->attributes = $attributeGroups;
- foreach ($attributeGroups as $attributeGroup) {
- $attributeGroup->parent = $expression;
- }
- }
- return $expression;
- }
-
- /**
- * Precondition: The next token is an AttributeToken
- * @return Node
- */
- private function parseAttributeStatement($parentNode) {
- $attributeGroups = $this->parseAttributeGroups(null);
- if ($parentNode instanceof ClassMembersNode) {
- // Create a class element or a MissingMemberDeclaration
- $statement = $this->parseClassElementFn()($parentNode);
- } elseif ($parentNode instanceof TraitMembers) {
- // Create a trait element or a MissingMemberDeclaration
- $statement = $this->parseTraitElementFn()($parentNode);
- } elseif ($parentNode instanceof EnumMembers) {
- // Create a enum element or a MissingMemberDeclaration
- $statement = $this->parseEnumElementFn()($parentNode);
- } elseif ($parentNode instanceof InterfaceMembers) {
- // Create an interface element or a MissingMemberDeclaration
- $statement = $this->parseInterfaceElementFn()($parentNode);
- } else {
- // Classlikes, anonymous functions, global functions, and arrow functions can have attributes. Global constants cannot.
- if (in_array($this->token->kind, [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::EnumKeyword], true) ||
- $this->token->kind === TokenKind::StaticKeyword && $this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
- $statement = $this->parseStatement($parentNode);
- } else {
- // Create a MissingToken so that diagnostics indicate that the attributes did not match up with an expression/declaration.
- $statement = new MissingDeclaration();
- $statement->parent = $parentNode;
- $statement->declaration = new MissingToken(TokenKind::Expression, $this->token->fullStart);
- }
- }
-
- if ($statement instanceof FunctionLike ||
- $statement instanceof ClassDeclaration ||
- $statement instanceof TraitDeclaration ||
- $statement instanceof EnumDeclaration ||
- $statement instanceof EnumCaseDeclaration ||
- $statement instanceof InterfaceDeclaration ||
- $statement instanceof ClassConstDeclaration ||
- $statement instanceof PropertyDeclaration ||
- $statement instanceof MissingDeclaration ||
- $statement instanceof MissingMemberDeclaration) {
-
- $statement->attributes = $attributeGroups;
- foreach ($attributeGroups as $attributeGroup) {
- $attributeGroup->parent = $statement;
- }
- }
- return $statement;
- }
-
- /**
- * @param Node|null $parentNode
- * @return AttributeGroup[]
- */
- private function parseAttributeGroups($parentNode): array
- {
- $attributeGroups = [];
- while ($attributeToken = $this->eatOptional1(TokenKind::AttributeToken)) {
- $attributeGroup = new AttributeGroup();
- $attributeGroup->startToken = $attributeToken;
- $attributeGroup->attributes = $this->parseAttributeElementList($attributeGroup)
- ?: (new MissingToken(TokenKind::Name, $this->token->fullStart));
- $attributeGroup->endToken = $this->eat1(TokenKind::CloseBracketToken);
- $attributeGroup->parent = $parentNode;
- $attributeGroups[] = $attributeGroup;
- }
- return $attributeGroups;
- }
-
- /**
- * @return DelimitedList\AttributeElementList
- */
- private function parseAttributeElementList(AttributeGroup $parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\AttributeElementList::class,
- TokenKind::CommaToken,
- $this->isQualifiedNameStartFn(),
- $this->parseAttributeFn(),
- $parentNode,
- false);
- }
-
- private function parseAttributeFn()
- {
- return function ($parentNode): Attribute {
- $attribute = new Attribute();
- $attribute->parent = $parentNode;
- $attribute->name = $this->parseQualifiedName($attribute);
- $attribute->openParen = $this->eatOptional1(TokenKind::OpenParenToken);
- if ($attribute->openParen) {
- $attribute->argumentExpressionList = $this->parseArgumentExpressionList($attribute);
- $attribute->closeParen = $this->eat1(TokenKind::CloseParenToken);
- }
- return $attribute;
- };
- }
-
- private function parseMethodDeclaration($parentNode, $modifiers) {
- $methodDeclaration = new MethodDeclaration();
- $methodDeclaration->modifiers = $modifiers;
- $this->parseFunctionType($methodDeclaration, true);
- $methodDeclaration->parent = $parentNode;
- return $methodDeclaration;
- }
-
- private function parseParameterFn() {
- return function ($parentNode) {
- $parameter = new Parameter();
- $parameter->parent = $parentNode;
- if ($this->token->kind === TokenKind::AttributeToken) {
- $parameter->attributes = $this->parseAttributeGroups($parameter);
- }
- // Note that parameter modifiers are allowed to be repeated by the parser in php 8.1 (it is a compiler error)
- //
- // TODO: Remove the visibilityToken in a future backwards incompatible release
- $parameter->visibilityToken = $this->eatOptional([TokenKind::PublicKeyword, TokenKind::ProtectedKeyword, TokenKind::PrivateKeyword]);
- $parameter->modifiers = $this->parseParameterModifiers() ?: null;
-
- $parameter->questionToken = $this->eatOptional1(TokenKind::QuestionToken);
- $parameter->typeDeclarationList = $this->tryParseParameterTypeDeclarationList($parameter);
- if ($parameter->typeDeclarationList) {
- $children = $parameter->typeDeclarationList->children;
- if (end($children) instanceof MissingToken && ($children[\count($children) - 2]->kind ?? null) === TokenKind::AmpersandToken) {
- array_pop($parameter->typeDeclarationList->children);
- $parameter->byRefToken = array_pop($parameter->typeDeclarationList->children);
- if (!$parameter->typeDeclarationList->children) {
- unset($parameter->typeDeclarationList);
- }
- }
- } elseif ($parameter->questionToken) {
- // TODO ParameterType?
- $parameter->typeDeclarationList = new MissingToken(TokenKind::PropertyType, $this->token->fullStart);
- }
- if (!$parameter->byRefToken) {
- $parameter->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
- }
- // TODO add post-parse rule that prevents assignment
- // TODO add post-parse rule that requires only last parameter be variadic
- $parameter->dotDotDotToken = $this->eatOptional1(TokenKind::DotDotDotToken);
- $parameter->variableName = $this->eat1(TokenKind::VariableName);
- $parameter->equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
- if ($parameter->equalsToken !== null) {
- // TODO add post-parse rule that checks for invalid assignments
- $parameter->default = $this->parseExpression($parameter);
- }
- return $parameter;
- };
- }
-
- /**
- * @param ArrowFunctionCreationExpression|AnonymousFunctionCreationExpression|FunctionDeclaration|MethodDeclaration $parentNode a node with FunctionReturnType trait
- */
- private function parseAndSetReturnTypeDeclarationList($parentNode) {
- $returnTypeList = $this->parseReturnTypeDeclarationList($parentNode);
- if (!$returnTypeList) {
- $parentNode->returnTypeList = new MissingToken(TokenKind::ReturnType, $this->token->fullStart);
- return;
- }
- $parentNode->returnTypeList = $returnTypeList;
- }
-
- const TYPE_DELIMITER_TOKENS = [
- TokenKind::BarToken,
- TokenKind::AmpersandToken,
- ];
-
- /**
- * Attempt to parse the return type after the `:` and optional `?` token.
- *
- * @return DelimitedList\QualifiedNameList|null
- */
- private function parseReturnTypeDeclarationList($parentNode) {
- $result = $this->parseDelimitedList(
- DelimitedList\QualifiedNameList::class,
- self::TYPE_DELIMITER_TOKENS,
- function ($token) {
- return \in_array($token->kind, $this->returnTypeDeclarationTokens, true) || $this->isQualifiedNameStart($token);
- },
- function ($parentNode) {
- return $this->parseReturnTypeDeclaration($parentNode);
- },
- $parentNode,
- false);
-
- // Add a MissingToken so that this will warn about `function () : T| {}`
- // TODO: Make this a reusable abstraction?
- if ($result && in_array(end($result->children)->kind ?? null, self::TYPE_DELIMITER_TOKENS)) {
- $result->children[] = new MissingToken(TokenKind::ReturnType, $this->token->fullStart);
- }
- return $result;
- }
-
- private function parseReturnTypeDeclaration($parentNode) {
- return $this->eatOptional($this->returnTypeDeclarationTokens)
- ?? $this->parseQualifiedName($parentNode);
- }
-
- private function tryParseParameterTypeDeclaration($parentNode) {
- $parameterTypeDeclaration =
- $this->eatOptional($this->parameterTypeDeclarationTokens) ?? $this->parseQualifiedName($parentNode);
- return $parameterTypeDeclaration;
- }
-
- /**
- * @param Node $parentNode
- * @return DelimitedList\QualifiedNameList|null
- */
- private function tryParseParameterTypeDeclarationList($parentNode) {
- $result = $this->parseDelimitedList(
- DelimitedList\QualifiedNameList::class,
- self::TYPE_DELIMITER_TOKENS,
- function ($token) {
- return \in_array($token->kind, $this->parameterTypeDeclarationTokens, true) || $this->isQualifiedNameStart($token);
- },
- function ($parentNode) {
- return $this->tryParseParameterTypeDeclaration($parentNode);
- },
- $parentNode,
- true);
-
- // Add a MissingToken so that this will Warn about `function (T| $x) {}`
- // TODO: Make this a reusable abstraction?
- if ($result && in_array(end($result->children)->kind ?? null, self::TYPE_DELIMITER_TOKENS)) {
- $result->children[] = new MissingToken(TokenKind::Name, $this->token->fullStart);
- }
- return $result;
- }
-
- private function parseCompoundStatement($parentNode) {
- $compoundStatement = new CompoundStatementNode();
- $compoundStatement->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $compoundStatement->statements = $this->parseList($compoundStatement, ParseContext::BlockStatements);
- $compoundStatement->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- $compoundStatement->parent = $parentNode;
- return $compoundStatement;
- }
-
- private function array_push_list(& $array, $list) {
- foreach ($list as $item) {
- $array[] = $item;
- }
- }
-
- private function isClassMemberDeclarationStart(Token $token) {
- switch ($token->kind) {
- // const-modifier
- case TokenKind::ConstKeyword:
-
- // visibility-modifier
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
-
- // static-modifier
- case TokenKind::StaticKeyword:
-
- // readonly-modifier
- case TokenKind::ReadonlyKeyword:
-
- // class-modifier
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
-
- case TokenKind::VarKeyword:
-
- case TokenKind::FunctionKeyword:
-
- case TokenKind::UseKeyword:
-
- // attributes
- case TokenKind::AttributeToken:
- return true;
-
- }
-
- return false;
- }
-
- private function isStatementStart(Token $token) {
- // https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#statements
- switch ($token->kind) {
- // Compound Statements
- case TokenKind::OpenBraceToken:
-
- // Labeled Statements
- case TokenKind::Name:
-// case TokenKind::CaseKeyword: // TODO update spec
-// case TokenKind::DefaultKeyword:
-
- // Expression Statements
- case TokenKind::SemicolonToken:
- case TokenKind::IfKeyword:
- case TokenKind::SwitchKeyword:
-
- // Iteration Statements
- case TokenKind::WhileKeyword:
- case TokenKind::DoKeyword:
- case TokenKind::ForKeyword:
- case TokenKind::ForeachKeyword:
-
- // Jump Statements
- case TokenKind::GotoKeyword:
- case TokenKind::ContinueKeyword:
- case TokenKind::BreakKeyword:
- case TokenKind::ReturnKeyword:
- case TokenKind::ThrowKeyword:
-
- // The try Statement
- case TokenKind::TryKeyword:
-
- // The declare Statement
- case TokenKind::DeclareKeyword:
-
- // const-declaration
- case TokenKind::ConstKeyword:
-
- // function-definition
- case TokenKind::FunctionKeyword:
-
- // class-declaration
- case TokenKind::ClassKeyword:
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
-
- // interface-declaration
- case TokenKind::InterfaceKeyword:
-
- // trait-declaration
- case TokenKind::TraitKeyword:
-
- // enum-declaration
- case TokenKind::EnumKeyword:
-
- // namespace-definition
- case TokenKind::NamespaceKeyword:
-
- // namespace-use-declaration
- case TokenKind::UseKeyword:
-
- // global-declaration
- case TokenKind::GlobalKeyword:
-
- // function-static-declaration
- case TokenKind::StaticKeyword:
-
- case TokenKind::ScriptSectionEndTag:
-
- // attributes
- case TokenKind::AttributeToken:
- return true;
-
- default:
- return $this->isExpressionStart($token);
- }
- }
-
- private function isExpressionStart($token) {
- return ($this->isExpressionStartFn())($token);
- }
-
- private function isExpressionStartFn() {
- return function ($token) {
- switch ($token->kind) {
- // Script Inclusion Expression
- case TokenKind::RequireKeyword:
- case TokenKind::RequireOnceKeyword:
- case TokenKind::IncludeKeyword:
- case TokenKind::IncludeOnceKeyword:
-
- // yield-expression
- case TokenKind::YieldKeyword:
- case TokenKind::YieldFromKeyword:
-
- // object-creation-expression
- case TokenKind::NewKeyword:
- case TokenKind::CloneKeyword:
- return true;
-
- // unary-op-expression
- case TokenKind::PlusToken:
- case TokenKind::MinusToken:
- case TokenKind::ExclamationToken:
- case TokenKind::TildeToken:
-
- // error-control-expression
- case TokenKind::AtSymbolToken:
-
- // prefix-increment-expression
- case TokenKind::PlusPlusToken:
- // prefix-decrement-expression
- case TokenKind::MinusMinusToken:
- return true;
-
- // variable-name
- case TokenKind::VariableName:
- case TokenKind::DollarToken:
- return true;
-
- // qualified-name
- case TokenKind::Name:
- case TokenKind::BackslashToken:
- return true;
- case TokenKind::NamespaceKeyword:
- // TODO currently only supports qualified-names, but eventually parse namespace declarations
- return $this->isNamespaceKeywordStartOfExpression($token);
-
- // literal
- case TokenKind::DecimalLiteralToken: // TODO merge dec, oct, hex, bin, float -> NumericLiteral
- case TokenKind::OctalLiteralToken:
- case TokenKind::HexadecimalLiteralToken:
- case TokenKind::BinaryLiteralToken:
- case TokenKind::FloatingLiteralToken:
- case TokenKind::InvalidOctalLiteralToken:
- case TokenKind::InvalidHexadecimalLiteral:
- case TokenKind::InvalidBinaryLiteral:
- case TokenKind::IntegerLiteralToken:
-
- case TokenKind::StringLiteralToken:
-
- case TokenKind::SingleQuoteToken:
- case TokenKind::DoubleQuoteToken:
- case TokenKind::HeredocStart:
- case TokenKind::BacktickToken:
-
- // array-creation-expression
- case TokenKind::ArrayKeyword:
- case TokenKind::OpenBracketToken:
-
- // intrinsic-construct
- case TokenKind::EchoKeyword:
- case TokenKind::ListKeyword:
- case TokenKind::UnsetKeyword:
-
- // intrinsic-operator
- case TokenKind::EmptyKeyword:
- case TokenKind::EvalKeyword:
- case TokenKind::ExitKeyword:
- case TokenKind::DieKeyword:
- case TokenKind::IsSetKeyword:
- case TokenKind::PrintKeyword:
-
- // ( expression )
- case TokenKind::OpenParenToken:
- case TokenKind::ArrayCastToken:
- case TokenKind::BoolCastToken:
- case TokenKind::DoubleCastToken:
- case TokenKind::IntCastToken:
- case TokenKind::ObjectCastToken:
- case TokenKind::StringCastToken:
- case TokenKind::UnsetCastToken:
- case TokenKind::MatchKeyword:
-
- // anonymous-function-creation-expression
- case TokenKind::StaticKeyword:
- case TokenKind::FunctionKeyword:
- case TokenKind::FnKeyword:
- case TokenKind::AttributeToken:
- return true;
- }
- return \in_array($token->kind, $this->reservedWordTokens, true);
- };
- }
-
- /**
- * Handles the fact that $token may either be getCurrentToken or the token immediately before it in isExpressionStartFn().
- * An expression can be namespace\CONST, namespace\fn(), or namespace\ClassName
- */
- private function isNamespaceKeywordStartOfExpression(Token $token) : bool {
- $nextToken = $this->getCurrentToken();
- if ($nextToken->kind === TokenKind::BackslashToken) {
- return true;
- }
- if ($nextToken !== $token) {
- return false;
- }
- $oldPosition = $this->lexer->getCurrentPosition();
- $nextToken = $this->lexer->scanNextToken();
- $this->lexer->setCurrentPosition($oldPosition);
- return $nextToken->kind === TokenKind::BackslashToken;
- }
-
- /**
- * @param Node $parentNode
- * @return Token|MissingToken|Node
- */
- private function parsePrimaryExpression($parentNode) {
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- // variable-name
- case TokenKind::VariableName: // TODO special case $this
- case TokenKind::DollarToken:
- return $this->parseSimpleVariable($parentNode);
-
- // qualified-name
- case TokenKind::Name: // TODO Qualified name
- case TokenKind::BackslashToken:
- case TokenKind::NamespaceKeyword:
- return $this->parseQualifiedName($parentNode);
-
- case TokenKind::DecimalLiteralToken: // TODO merge dec, oct, hex, bin, float -> NumericLiteral
- case TokenKind::OctalLiteralToken:
- case TokenKind::HexadecimalLiteralToken:
- case TokenKind::BinaryLiteralToken:
- case TokenKind::FloatingLiteralToken:
- case TokenKind::InvalidOctalLiteralToken:
- case TokenKind::InvalidHexadecimalLiteral:
- case TokenKind::InvalidBinaryLiteral:
- case TokenKind::IntegerLiteralToken:
- return $this->parseNumericLiteralExpression($parentNode);
-
- case TokenKind::StringLiteralToken:
- return $this->parseStringLiteralExpression($parentNode);
-
- case TokenKind::DoubleQuoteToken:
- case TokenKind::SingleQuoteToken:
- case TokenKind::HeredocStart:
- case TokenKind::BacktickToken:
- return $this->parseStringLiteralExpression2($parentNode);
-
- // TODO constant-expression
-
- // array-creation-expression
- case TokenKind::ArrayKeyword:
- case TokenKind::OpenBracketToken:
- return $this->parseArrayCreationExpression($parentNode);
-
- // intrinsic-construct
- case TokenKind::ListKeyword:
- return $this->parseListIntrinsicExpression($parentNode);
-
- // intrinsic-operator
- case TokenKind::EmptyKeyword:
- return $this->parseEmptyIntrinsicExpression($parentNode);
- case TokenKind::EvalKeyword:
- return $this->parseEvalIntrinsicExpression($parentNode);
-
- case TokenKind::ExitKeyword:
- case TokenKind::DieKeyword:
- return $this->parseExitIntrinsicExpression($parentNode);
-
- case TokenKind::IsSetKeyword:
- return $this->parseIssetIntrinsicExpression($parentNode);
-
- case TokenKind::PrintKeyword:
- return $this->parsePrintIntrinsicExpression($parentNode);
-
- // ( expression )
- case TokenKind::OpenParenToken:
- return $this->parseParenthesizedExpression($parentNode);
-
- // anonymous-function-creation-expression
- case TokenKind::AttributeToken:
- return $this->parseAttributeExpression($parentNode);
-
- case TokenKind::StaticKeyword:
- // handle `static::`, `static(`, `new static;`, `instanceof static`
- if (!$this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
- // TODO: Should this check the parent type to reject `$x = static;`, `$x = static();`, etc.
- return $this->parseStaticQualifiedName($parentNode);
- }
- // Could be `static function` anonymous function creation expression, so flow through
- case TokenKind::FunctionKeyword:
- case TokenKind::FnKeyword:
- return $this->parseAnonymousFunctionCreationExpression($parentNode);
-
- case TokenKind::TrueReservedWord:
- case TokenKind::FalseReservedWord:
- case TokenKind::NullReservedWord:
- // handle `true::`, `true(`, `true\`
- if ($this->lookahead([TokenKind::BackslashToken, TokenKind::ColonColonToken, TokenKind::OpenParenToken])) {
- return $this->parseQualifiedName($parentNode);
- }
- return $this->parseReservedWordExpression($parentNode);
- case TokenKind::MatchKeyword:
- return $this->parseMatchExpression($parentNode);
- }
- if (\in_array($token->kind, TokenStringMaps::RESERVED_WORDS)) {
- return $this->parseQualifiedName($parentNode);
- }
-
- return new MissingToken(TokenKind::Expression, $token->fullStart);
- }
-
- private function parseEmptyStatement($parentNode) {
- $emptyStatement = new EmptyStatement();
- $emptyStatement->parent = $parentNode;
- $emptyStatement->semicolon = $this->eat1(TokenKind::SemicolonToken);
- return $emptyStatement;
- }
-
- private function parseStringLiteralExpression($parentNode) {
- // TODO validate input token
- $expression = new StringLiteral();
- $expression->parent = $parentNode;
- $expression->children = $this->getCurrentToken(); // TODO - merge string types
- $this->advanceToken();
- return $expression;
- }
-
- private function parseStringLiteralExpression2($parentNode) {
- // TODO validate input token
- $expression = new StringLiteral();
- $expression->parent = $parentNode;
- $expression->startQuote = $this->eat(TokenKind::SingleQuoteToken, TokenKind::DoubleQuoteToken, TokenKind::HeredocStart, TokenKind::BacktickToken);
- $expression->children = [];
-
- while (true) {
- switch ($this->getCurrentToken()->kind) {
- case TokenKind::DollarOpenBraceToken:
- case TokenKind::OpenBraceDollarToken:
- $expression->children[] = $this->eat(TokenKind::DollarOpenBraceToken, TokenKind::OpenBraceDollarToken);
- // TODO: Reject ${var->prop} and ${(var->prop)} without rejecting ${var+otherVar}
- // Currently, this fails to reject ${var->prop} (because `var` has TokenKind::Name instead of StringVarname)
- if ($this->getCurrentToken()->kind === TokenKind::StringVarname) {
- $expression->children[] = $this->parseComplexDollarTemplateStringExpression($expression);
- } else {
- $expression->children[] = $this->parseExpression($expression);
- }
- $expression->children[] = $this->eat1(TokenKind::CloseBraceToken);
- break;
- case $startQuoteKind = $expression->startQuote->kind:
- case TokenKind::EndOfFileToken:
- case TokenKind::HeredocEnd:
- $expression->endQuote = $this->eat($startQuoteKind, TokenKind::HeredocEnd);
- return $expression;
- case TokenKind::VariableName:
- $expression->children[] = $this->parseTemplateStringExpression($expression);
- break;
- default:
- $expression->children[] = $this->getCurrentToken();
- $this->advanceToken();
- break;
- }
- }
- }
-
- /**
- * This is used to parse the contents of `"${...}"` expressions.
- *
- * Supported: x, x[0], x[$y]
- * Not supported: $x->p1, x[0][1], etc.
- * @see parseTemplateStringExpression
- *
- * Precondition: getCurrentToken()->kind === TokenKind::StringVarname
- */
- private function parseComplexDollarTemplateStringExpression($parentNode) {
- $var = $this->parseSimpleVariable($parentNode);
- $token = $this->getCurrentToken();
- if ($token->kind === TokenKind::OpenBracketToken) {
- return $this->parseTemplateStringSubscriptExpression($var);
- }
- return $var;
- }
-
- /**
- * Double-quoted and heredoc strings support a basic set of expression types, described in http://php.net/manual/en/language.types.string.php#language.types.string.parsing
- * Supported: $x, $x->p, $x[0], $x[$y]
- * Not supported: $x->p1->p2, $x[0][1], etc.
- * Since there is a relatively small finite set of allowed forms, I implement it here rather than trying to reuse the general expression parsing code.
- */
- private function parseTemplateStringExpression($parentNode) {
- $token = $this->getCurrentToken();
- if ($token->kind === TokenKind::VariableName) {
- $var = $this->parseSimpleVariable($parentNode);
- $token = $this->getCurrentToken();
- if ($token->kind === TokenKind::OpenBracketToken) {
- return $this->parseTemplateStringSubscriptExpression($var);
- } else if ($token->kind === TokenKind::ArrowToken || $token->kind === TokenKind::QuestionArrowToken) {
- return $this->parseTemplateStringMemberAccessExpression($var);
- } else {
- return $var;
- }
- }
-
- return null;
- }
-
- private function parseTemplateStringSubscriptExpression($postfixExpression) : SubscriptExpression {
- $subscriptExpression = new SubscriptExpression();
- $subscriptExpression->parent = $postfixExpression->parent;
- $postfixExpression->parent = $subscriptExpression;
-
- $subscriptExpression->postfixExpression = $postfixExpression;
- $subscriptExpression->openBracketOrBrace = $this->eat1(TokenKind::OpenBracketToken); // Only [] syntax is supported, not {}
- $token = $this->getCurrentToken();
- if ($token->kind === TokenKind::VariableName) {
- $subscriptExpression->accessExpression = $this->parseSimpleVariable($subscriptExpression);
- } elseif ($token->kind === TokenKind::IntegerLiteralToken) {
- $subscriptExpression->accessExpression = $this->parseNumericLiteralExpression($subscriptExpression);
- } elseif ($token->kind === TokenKind::StringLiteralToken) {
- // TODO: investigate if this should add other uncommon types of tokens for strings/numbers mentioned in parsePrimaryExpression()
- $subscriptExpression->accessExpression = $this->parseStringLiteralExpression($subscriptExpression);
- } elseif ($token->kind === TokenKind::Name) {
- $subscriptExpression->accessExpression = $this->parseTemplateStringSubscriptStringLiteral($subscriptExpression);
- } else {
- $subscriptExpression->accessExpression = new MissingToken(TokenKind::Expression, $token->fullStart);
- }
-
- $subscriptExpression->closeBracketOrBrace = $this->eat1(TokenKind::CloseBracketToken);
-
- return $subscriptExpression;
- }
-
- private function parseTemplateStringSubscriptStringLiteral($parentNode) : StringLiteral {
- $expression = new StringLiteral();
- $expression->parent = $parentNode;
- $expression->children = $this->eat1(TokenKind::Name);
- return $expression;
- }
-
- private function parseTemplateStringMemberAccessExpression($expression) : MemberAccessExpression {
- $memberAccessExpression = new MemberAccessExpression();
- $memberAccessExpression->parent = $expression->parent;
- $expression->parent = $memberAccessExpression;
-
- $memberAccessExpression->dereferencableExpression = $expression;
- $memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
- $memberAccessExpression->memberName = $this->eat1(TokenKind::Name);
-
- return $memberAccessExpression;
- }
-
- private function parseNumericLiteralExpression($parentNode) {
- $numericLiteral = new NumericLiteral();
- $numericLiteral->parent = $parentNode;
- $numericLiteral->children = $this->getCurrentToken();
- $this->advanceToken();
- return $numericLiteral;
- }
-
- private function parseReservedWordExpression($parentNode) {
- $reservedWord = new ReservedWord();
- $reservedWord->parent = $parentNode;
- $reservedWord->children = $this->getCurrentToken();
- $this->advanceToken();
- return $reservedWord;
- }
-
- private function isModifier($token): bool {
- switch ($token->kind) {
- // class-modifier
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
-
- // visibility-modifier
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
-
- // static-modifier
- case TokenKind::StaticKeyword:
-
- // readonly-modifier
- case TokenKind::ReadonlyKeyword:
-
- // var
- case TokenKind::VarKeyword:
- return true;
- }
- return false;
- }
-
- private function isParameterModifier($token): bool {
- switch ($token->kind) {
- // visibility-modifier
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
-
- // readonly-modifier
- case TokenKind::ReadonlyKeyword:
-
- return true;
- }
- return false;
- }
-
- /** @return Token[] */
- private function parseParameterModifiers(): array {
- $modifiers = [];
- $token = $this->getCurrentToken();
- while ($this->isParameterModifier($token)) {
- $modifiers[] = $token;
- $this->advanceToken();
- $token = $this->getCurrentToken();
- }
- return $modifiers;
- }
-
- /** @return Token[] */
- private function parseModifiers(): array {
- $modifiers = [];
- $token = $this->getCurrentToken();
- while ($this->isModifier($token)) {
- $modifiers[] = $token;
- $this->advanceToken();
- $token = $this->getCurrentToken();
- }
- return $modifiers;
- }
-
- private function isParameterStartFn() {
- return function ($token) {
- switch ($token->kind) {
- case TokenKind::DotDotDotToken:
-
- // qualified-name
- case TokenKind::Name: // http://php.net/manual/en/language.namespaces.rules.php
- case TokenKind::BackslashToken:
- case TokenKind::NamespaceKeyword:
-
- case TokenKind::AmpersandToken:
-
- case TokenKind::VariableName:
-
- // nullable-type
- case TokenKind::QuestionToken:
-
- // parameter promotion
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
- case TokenKind::AttributeToken:
- return true;
- }
-
- // scalar-type
- return \in_array($token->kind, $this->parameterTypeDeclarationTokens, true);
- };
- }
-
- /**
- * @param string $className (name of subclass of DelimitedList)
- * @param int $delimiter
- * @param callable $isElementStartFn
- * @param callable $parseElementFn
- * @param Node $parentNode
- * @param bool $allowEmptyElements
- * @return DelimitedList|null instance of $className
- */
- private function parseDelimitedList($className, $delimiter, $isElementStartFn, $parseElementFn, $parentNode, $allowEmptyElements = false) {
- // TODO consider allowing empty delimiter to be more tolerant
- $node = new $className();
- $token = $this->getCurrentToken();
- do {
- if ($isElementStartFn($token)) {
- $node->addElement($parseElementFn($node));
- } elseif (!$allowEmptyElements || ($allowEmptyElements && !$this->checkToken($delimiter))) {
- break;
- }
-
- $delimiterToken = $this->eatOptional($delimiter);
- if ($delimiterToken !== null) {
- $node->addElement($delimiterToken);
- }
- $token = $this->getCurrentToken();
- // TODO ERROR CASE - no delimiter, but a param follows
- } while ($delimiterToken !== null);
-
-
- $node->parent = $parentNode;
- if ($node->children === null) {
- return null;
- }
- return $node;
- }
-
- /**
- * @internal
- */
- const QUALIFIED_NAME_START_TOKENS = [
- TokenKind::BackslashToken,
- TokenKind::NamespaceKeyword,
- TokenKind::Name,
- ];
-
- private function isQualifiedNameStart($token) {
- return \in_array($token->kind, self::QUALIFIED_NAME_START_TOKENS, true);
- }
-
- private function isQualifiedNameStartFn() {
- return function ($token) {
- return \in_array($token->kind, self::QUALIFIED_NAME_START_TOKENS, true);
- };
- }
-
- private function isQualifiedNameStartForCatchFn() {
- return function ($token) {
- // Unfortunately, catch(int $x) is *syntactically valid* php which `php --syntax-check` would accept.
- // (tolerant-php-parser is concerned with syntax, not semantics)
- return \in_array($token->kind, self::QUALIFIED_NAME_START_TOKENS, true) ||
- \in_array($token->kind, $this->reservedWordTokens, true);
- };
- }
-
- /**
- * @return QualifiedName
- */
- private function parseStaticQualifiedName($parentNode) {
- $node = new QualifiedName();
- $token = $this->eat(TokenKind::StaticKeyword);
- $token->kind = TokenKind::Name;
- $node->parent = $parentNode;
- $node->nameParts = [$token];
- return $node;
- }
-
- /**
- * @return QualifiedName|null - returns null for invalid qualified names such as `static\` (use parseStaticQualifiedName for that)
- */
- private function parseQualifiedName($parentNode) {
- return ($this->parseQualifiedNameFn())($parentNode);
- }
-
- private function parseQualifiedNameFn() {
- return function ($parentNode) {
- $node = new QualifiedName();
- $node->parent = $parentNode;
- $node->relativeSpecifier = $this->parseRelativeSpecifier($node);
- if (!isset($node->relativeSpecifier)) {
- $node->globalSpecifier = $this->eatOptional1(TokenKind::BackslashToken);
- }
-
- $nameParts =
- $this->parseDelimitedList(
- DelimitedList\QualifiedNameParts::class,
- TokenKind::BackslashToken,
- function ($token) {
- // a\static() <- INVALID (but not checked for right now)
- // new a\static() <- INVALID
- // new static() <- VALID
- // a\static\b <- INVALID
- // a\function <- INVALID
- // a\true\b <-VALID
- // a\b\true <-VALID
- // a\static::b <-VALID
- // TODO more tests
- return $this->lookahead(TokenKind::BackslashToken)
- ? in_array($token->kind, $this->nameOrReservedWordTokens)
- : in_array($token->kind, $this->nameOrStaticOrReservedWordTokens);
- },
- function ($parentNode) {
- $name = $this->lookahead(TokenKind::BackslashToken)
- ? $this->eat($this->nameOrReservedWordTokens)
- : $this->eat($this->nameOrStaticOrReservedWordTokens); // TODO support keyword name
- $name->kind = TokenKind::Name; // bool/true/null/static should not be treated as keywords in this case
- return $name;
- }, $node);
- if ($nameParts === null && $node->globalSpecifier === null && $node->relativeSpecifier === null) {
- return null;
- }
-
- $node->nameParts = $nameParts ? $nameParts->children : [];
-
- return $node;
- };
- }
-
- private function parseRelativeSpecifier($parentNode) {
- $node = new RelativeSpecifier();
- $node->parent = $parentNode;
- $node->namespaceKeyword = $this->eatOptional1(TokenKind::NamespaceKeyword);
- if ($node->namespaceKeyword !== null) {
- $node->backslash = $this->eat1(TokenKind::BackslashToken);
- }
- if (isset($node->backslash)) {
- return $node;
- }
- return null;
- }
-
- /**
- * @param MethodDeclaration|FunctionDeclaration|AnonymousFunctionCreationExpression $functionDeclaration
- */
- private function parseFunctionType(Node $functionDeclaration, $canBeAbstract = false, $isAnonymous = false) {
-
- $functionDeclaration->functionKeyword = $this->eat1(TokenKind::FunctionKeyword);
- $functionDeclaration->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
- $functionDeclaration->name = $isAnonymous
- ? $this->eatOptional($this->nameOrKeywordOrReservedWordTokens)
- : $this->eat($this->nameOrKeywordOrReservedWordTokens);
-
- if (isset($functionDeclaration->name)) {
- $functionDeclaration->name->kind = TokenKind::Name;
- }
-
- if ($isAnonymous && isset($functionDeclaration->name)) {
- // Anonymous functions should not have names
- $functionDeclaration->name = new SkippedToken($functionDeclaration->name); // TODO instead handle this during post-walk
- }
-
- $functionDeclaration->openParen = $this->eat1(TokenKind::OpenParenToken);
- $functionDeclaration->parameters = $this->parseDelimitedList(
- DelimitedList\ParameterDeclarationList::class,
- TokenKind::CommaToken,
- $this->isParameterStartFn(),
- $this->parseParameterFn(),
- $functionDeclaration);
- $functionDeclaration->closeParen = $this->eat1(TokenKind::CloseParenToken);
- if ($isAnonymous) {
- $functionDeclaration->anonymousFunctionUseClause = $this->parseAnonymousFunctionUseClause($functionDeclaration);
- }
-
- if ($this->checkToken(TokenKind::ColonToken)) {
- $functionDeclaration->colonToken = $this->eat1(TokenKind::ColonToken);
- $functionDeclaration->questionToken = $this->eatOptional1(TokenKind::QuestionToken);
- $this->parseAndSetReturnTypeDeclarationList($functionDeclaration);
- }
-
- if ($canBeAbstract) {
- $functionDeclaration->compoundStatementOrSemicolon = $this->eatOptional1(TokenKind::SemicolonToken);
- }
-
- if (!isset($functionDeclaration->compoundStatementOrSemicolon)) {
- $functionDeclaration->compoundStatementOrSemicolon = $this->parseCompoundStatement($functionDeclaration);
- }
- }
-
- private function parseNamedLabelStatement($parentNode) {
- $namedLabelStatement = new NamedLabelStatement();
- $namedLabelStatement->parent = $parentNode;
- $namedLabelStatement->name = $this->eat1(TokenKind::Name);
- $namedLabelStatement->colon = $this->eat1(TokenKind::ColonToken);
- // A named label is a statement on its own. E.g. `while (false) label: echo "test";`
- // is parsed as `while (false) { label: } echo "test";
- return $namedLabelStatement;
- }
-
- /**
- * @param int|int[] ...$expectedKinds an array of one or more kinds/sets of allowed kinds in each position
- */
- private function lookahead(...$expectedKinds) : bool {
- $startPos = $this->lexer->getCurrentPosition();
- $startToken = $this->token;
- $succeeded = true;
- foreach ($expectedKinds as $kind) {
- $token = $this->lexer->scanNextToken();
- $currentPosition = $this->lexer->getCurrentPosition();
- $endOfFilePosition = $this->lexer->getEndOfFilePosition();
- if (\is_array($kind)) {
- $succeeded = false;
- foreach ($kind as $kindOption) {
- if ($currentPosition <= $endOfFilePosition && $token->kind === $kindOption) {
- $succeeded = true;
- break;
- }
- }
- } else {
- if ($currentPosition > $endOfFilePosition || $token->kind !== $kind) {
- $succeeded = false;
- break;
- }
- }
- }
- $this->lexer->setCurrentPosition($startPos);
- $this->token = $startToken;
- return $succeeded;
- }
-
- private function checkToken($expectedKind) : bool {
- return $this->getCurrentToken()->kind === $expectedKind;
- }
-
- private function parseIfStatement($parentNode) {
- $ifStatement = new IfStatementNode();
- $ifStatement->parent = $parentNode;
- $ifStatement->ifKeyword = $this->eat1(TokenKind::IfKeyword);
- $ifStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $ifStatement->expression = $this->parseExpression($ifStatement);
- $ifStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $curTokenKind = $this->getCurrentToken()->kind;
- if ($curTokenKind === TokenKind::ColonToken) {
- $ifStatement->colon = $this->eat1(TokenKind::ColonToken);
- $ifStatement->statements = $this->parseList($ifStatement, ParseContext::IfClause2Elements);
- } else if ($curTokenKind !== TokenKind::ScriptSectionEndTag) {
- // Fix #246 : properly parse `if (false) ?\>echoed text\statements = $this->parseStatement($ifStatement);
- }
- $ifStatement->elseIfClauses = []; // TODO - should be some standard for empty arrays vs. null?
- while ($this->checkToken(TokenKind::ElseIfKeyword)) {
- $ifStatement->elseIfClauses[] = $this->parseElseIfClause($ifStatement);
- }
-
- if ($this->checkToken(TokenKind::ElseKeyword)) {
- $ifStatement->elseClause = $this->parseElseClause($ifStatement);
- }
-
- $ifStatement->endifKeyword = $this->eatOptional1(TokenKind::EndIfKeyword);
- if ($ifStatement->endifKeyword) {
- $ifStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- }
-
- return $ifStatement;
- }
-
- private function parseElseIfClause($parentNode) {
- $elseIfClause = new ElseIfClauseNode();
- $elseIfClause->parent = $parentNode;
- $elseIfClause->elseIfKeyword = $this->eat1(TokenKind::ElseIfKeyword);
- $elseIfClause->openParen = $this->eat1(TokenKind::OpenParenToken);
- $elseIfClause->expression = $this->parseExpression($elseIfClause);
- $elseIfClause->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $curTokenKind = $this->getCurrentToken()->kind;
- if ($curTokenKind === TokenKind::ColonToken) {
- $elseIfClause->colon = $this->eat1(TokenKind::ColonToken);
- $elseIfClause->statements = $this->parseList($elseIfClause, ParseContext::IfClause2Elements);
- } elseif ($curTokenKind !== TokenKind::ScriptSectionEndTag) {
- $elseIfClause->statements = $this->parseStatement($elseIfClause);
- }
- return $elseIfClause;
- }
-
- private function parseElseClause($parentNode) {
- $elseClause = new ElseClauseNode();
- $elseClause->parent = $parentNode;
- $elseClause->elseKeyword = $this->eat1(TokenKind::ElseKeyword);
- $curTokenKind = $this->getCurrentToken()->kind;
- if ($curTokenKind === TokenKind::ColonToken) {
- $elseClause->colon = $this->eat1(TokenKind::ColonToken);
- $elseClause->statements = $this->parseList($elseClause, ParseContext::IfClause2Elements);
- } elseif ($curTokenKind !== TokenKind::ScriptSectionEndTag) {
- $elseClause->statements = $this->parseStatement($elseClause);
- }
- return $elseClause;
- }
-
- private function parseSwitchStatement($parentNode) {
- $switchStatement = new SwitchStatementNode();
- $switchStatement->parent = $parentNode;
- $switchStatement->switchKeyword = $this->eat1(TokenKind::SwitchKeyword);
- $switchStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $switchStatement->expression = $this->parseExpression($switchStatement);
- $switchStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $switchStatement->openBrace = $this->eatOptional1(TokenKind::OpenBraceToken);
- $switchStatement->colon = $this->eatOptional1(TokenKind::ColonToken);
- $switchStatement->caseStatements = $this->parseList($switchStatement, ParseContext::SwitchStatementElements);
- if ($switchStatement->colon !== null) {
- $switchStatement->endswitch = $this->eat1(TokenKind::EndSwitchKeyword);
- $switchStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- } else {
- $switchStatement->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- }
-
- return $switchStatement;
- }
-
- private function parseCaseOrDefaultStatement() {
- return function ($parentNode) {
- $caseStatement = new CaseStatementNode();
- $caseStatement->parent = $parentNode;
- // TODO add error checking
- $caseStatement->caseKeyword = $this->eat(TokenKind::CaseKeyword, TokenKind::DefaultKeyword);
- if ($caseStatement->caseKeyword->kind === TokenKind::CaseKeyword) {
- $caseStatement->expression = $this->parseExpression($caseStatement);
- }
- $caseStatement->defaultLabelTerminator = $this->eat(TokenKind::ColonToken, TokenKind::SemicolonToken);
- $caseStatement->statementList = $this->parseList($caseStatement, ParseContext::CaseStatementElements);
- return $caseStatement;
- };
- }
-
- private function parseWhileStatement($parentNode) {
- $whileStatement = new WhileStatement();
- $whileStatement->parent = $parentNode;
- $whileStatement->whileToken = $this->eat1(TokenKind::WhileKeyword);
- $whileStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $whileStatement->expression = $this->parseExpression($whileStatement);
- $whileStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $whileStatement->colon = $this->eatOptional1(TokenKind::ColonToken);
- if ($whileStatement->colon !== null) {
- $whileStatement->statements = $this->parseList($whileStatement, ParseContext::WhileStatementElements);
- $whileStatement->endWhile = $this->eat1(TokenKind::EndWhileKeyword);
- $whileStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- } elseif (!$this->checkToken(TokenKind::ScriptSectionEndTag)) {
- $whileStatement->statements = $this->parseStatement($whileStatement);
- }
- return $whileStatement;
- }
-
- /**
- * @param Node $parentNode
- * @param bool $force
- * @return Node|MissingToken|array - The expression, or a missing token, or (if $force) an array containing a missed and skipped token
- */
- private function parseExpression($parentNode, $force = false) {
- $token = $this->getCurrentToken();
- if ($token->kind === TokenKind::EndOfFileToken) {
- return new MissingToken(TokenKind::Expression, $token->fullStart);
- }
-
- // Equivalent to (parseExpressionFn())($parentNode)
- $expression = $this->parseBinaryExpressionOrHigher(0, $parentNode);
- if ($force && $expression instanceof MissingToken) {
- $expression = [$expression, new SkippedToken($token)];
- $this->advanceToken();
- }
-
- return $expression;
- }
-
- private function parseExpressionFn() {
- return function ($parentNode) {
- return $this->parseBinaryExpressionOrHigher(0, $parentNode);
- };
- }
-
- /**
- * @param Node $parentNode
- * @return Expression
- */
- private function parseUnaryExpressionOrHigher($parentNode) {
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- // unary-op-expression
- case TokenKind::PlusToken:
- case TokenKind::MinusToken:
- case TokenKind::ExclamationToken:
- case TokenKind::TildeToken:
- return $this->parseUnaryOpExpression($parentNode);
-
- // error-control-expression
- case TokenKind::AtSymbolToken:
- return $this->parseErrorControlExpression($parentNode);
-
- // prefix-increment-expression
- case TokenKind::PlusPlusToken:
- // prefix-decrement-expression
- case TokenKind::MinusMinusToken:
- return $this->parsePrefixUpdateExpression($parentNode);
-
- case TokenKind::ArrayCastToken:
- case TokenKind::BoolCastToken:
- case TokenKind::DoubleCastToken:
- case TokenKind::IntCastToken:
- case TokenKind::ObjectCastToken:
- case TokenKind::StringCastToken:
- case TokenKind::UnsetCastToken:
- return $this->parseCastExpression($parentNode);
-
- case TokenKind::OpenParenToken:
- // TODO remove duplication
- if ($this->lookahead(
- [TokenKind::ArrayKeyword,
- TokenKind::BinaryReservedWord,
- TokenKind::BoolReservedWord,
- TokenKind::BooleanReservedWord,
- TokenKind::DoubleReservedWord,
- TokenKind::IntReservedWord,
- TokenKind::IntegerReservedWord,
- TokenKind::FloatReservedWord,
- TokenKind::ObjectReservedWord,
- TokenKind::RealReservedWord,
- TokenKind::StringReservedWord,
- TokenKind::UnsetKeyword], TokenKind::CloseParenToken)) {
- return $this->parseCastExpressionGranular($parentNode);
- }
- break;
-
-/*
-
- case TokenKind::BacktickToken:
- return $this->parseShellCommandExpression($parentNode);
-
- case TokenKind::OpenParenToken:
- // TODO
-// return $this->parseCastExpressionGranular($parentNode);
- break;*/
-
- // object-creation-expression (postfix-expression)
- case TokenKind::NewKeyword:
- return $this->parseObjectCreationExpression($parentNode);
-
- // clone-expression (postfix-expression)
- case TokenKind::CloneKeyword:
- return $this->parseCloneExpression($parentNode);
-
- case TokenKind::YieldKeyword:
- case TokenKind::YieldFromKeyword:
- return $this->parseYieldExpression($parentNode);
-
- // include-expression
- // include-once-expression
- // require-expression
- // require-once-expression
- case TokenKind::IncludeKeyword:
- case TokenKind::IncludeOnceKeyword:
- case TokenKind::RequireKeyword:
- case TokenKind::RequireOnceKeyword:
- return $this->parseScriptInclusionExpression($parentNode);
- case TokenKind::ThrowKeyword: // throw-statement will become an expression in php 8.0
- return $this->parseThrowExpression($parentNode);
- }
-
- $expression = $this->parsePrimaryExpression($parentNode);
- return $this->parsePostfixExpressionRest($expression);
- }
-
- /**
- * @param int $precedence
- * @param Node $parentNode
- * @return Expression
- */
- private function parseBinaryExpressionOrHigher($precedence, $parentNode) {
- $leftOperand = $this->parseUnaryExpressionOrHigher($parentNode);
-
- [$prevNewPrecedence, $prevAssociativity] = self::UNKNOWN_PRECEDENCE_AND_ASSOCIATIVITY;
-
- while (true) {
- $token = $this->getCurrentToken();
-
- [$newPrecedence, $associativity] = $this->getBinaryOperatorPrecedenceAndAssociativity($token);
-
- // Expressions using operators w/o associativity (equality, relational, instanceof)
- // cannot reference identical expression types within one of their operands.
- //
- // Example:
- // $a < $b < $c // CASE 1: INVALID
- // $a < $b === $c < $d // CASE 2: VALID
- //
- // In CASE 1, it is expected that we stop parsing the expression after the $b token.
- if ($prevAssociativity === Associativity::None && $prevNewPrecedence === $newPrecedence) {
- break;
- }
-
- // Precedence and associativity properties determine whether we recurse, and continue
- // building up the current operand, or whether we pop out.
- //
- // Example:
- // $a + $b + $c // CASE 1: additive-expression (left-associative)
- // $a = $b = $c // CASE 2: equality-expression (right-associative)
- //
- // CASE 1:
- // The additive-expression is left-associative, which means we expect the grouping to be:
- // ($a + $b) + $c
- //
- // Because both + operators have the same precedence, and the + operator is left associative,
- // we expect the second + operator NOT to be consumed because $newPrecedence > $precedence => FALSE
- //
- // CASE 2:
- // The equality-expression is right-associative, which means we expect the grouping to be:
- // $a = ($b = $c)
- //
- // Because both = operators have the same precedence, and the = operator is right-associative,
- // we expect the second = operator to be consumed because $newPrecedence >= $precedence => TRUE
- $shouldConsumeCurrentOperator =
- $associativity === Associativity::Right ?
- $newPrecedence >= $precedence:
- $newPrecedence > $precedence;
-
- if (!$shouldConsumeCurrentOperator) {
- break;
- }
-
- // Unlike every other binary expression, exponentiation operators take precedence over unary operators.
- //
- // Example:
- // -3**2 => -9
- //
- // In these cases, we strip the UnaryExpression operator, and reassign $leftOperand to
- // $unaryExpression->operand.
- //
- // After we finish building the BinaryExpression, we rebuild the UnaryExpression so that it includes
- // the original operator, and the newly constructed exponentiation-expression as the operand.
- $shouldOperatorTakePrecedenceOverUnary = false;
- switch ($token->kind) {
- case TokenKind::AsteriskAsteriskToken:
- $shouldOperatorTakePrecedenceOverUnary = $leftOperand instanceof UnaryExpression;
- break;
- case TokenKind::EqualsToken:
- case TokenKind::AsteriskAsteriskEqualsToken:
- case TokenKind::AsteriskEqualsToken:
- case TokenKind::SlashEqualsToken:
- case TokenKind::PercentEqualsToken:
- case TokenKind::PlusEqualsToken:
- case TokenKind::MinusEqualsToken:
- case TokenKind::DotEqualsToken:
- case TokenKind::LessThanLessThanEqualsToken:
- case TokenKind::GreaterThanGreaterThanEqualsToken:
- case TokenKind::AmpersandEqualsToken:
- case TokenKind::CaretEqualsToken:
- case TokenKind::BarEqualsToken:
- case TokenKind::QuestionQuestionEqualsToken:
- // Workarounds for https://github.com/Microsoft/tolerant-php-parser/issues/19#issue-201714377
- // Parse `!$a = $b` as `!($a = $b)` - PHP constrains the Left Hand Side of an assignment to a variable. A unary operator (`@`, `!`, etc.) is not a variable.
- // Instanceof has similar constraints for the LHS.
- // So does `!$a += $b`
- // TODO: Any other operators?
- if ($leftOperand instanceof UnaryOpExpression) {
- $shouldOperatorTakePrecedenceOverUnary = true;
- }
- break;
- case TokenKind::InstanceOfKeyword:
- // Unlike assignment, the instanceof operator doesn't have restrictions on what can go in the left hand side.
- // `!` is the only unary operator with lower precedence than instanceof.
- if ($leftOperand instanceof UnaryOpExpression) {
- if ($leftOperand->operator->kind === TokenKind::ExclamationToken) {
- $shouldOperatorTakePrecedenceOverUnary = true;
- }
- }
- break;
- case TokenKind::QuestionToken:
- if ($parentNode instanceof TernaryExpression) {
- // Workaround to parse "a ? b : c ? d : e" as "(a ? b : c) ? d : e"
- break 2;
- }
- break;
- }
-
- if ($shouldOperatorTakePrecedenceOverUnary) {
- $unaryExpression = $leftOperand;
- $leftOperand = $unaryExpression->operand;
- }
-
- $this->advanceToken();
-
- if ($token->kind === TokenKind::EqualsToken) {
- $byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
- }
-
- $leftOperand = $token->kind === TokenKind::QuestionToken ?
- $this->parseTernaryExpression($leftOperand, $token, $parentNode) :
- $this->makeBinaryExpression(
- $leftOperand,
- $token,
- $byRefToken ?? null,
- $this->parseBinaryExpressionOrHigher($newPrecedence, null),
- $parentNode);
-
- // Rebuild the unary expression if we deconstructed it earlier.
- if ($shouldOperatorTakePrecedenceOverUnary) {
- $leftOperand->parent = $unaryExpression;
- $unaryExpression->operand = $leftOperand;
- $leftOperand = $unaryExpression;
- }
-
- // Hold onto these values, so we know whether we've hit duplicate non-associative operators,
- // and need to terminate early.
- $prevNewPrecedence = $newPrecedence;
- $prevAssociativity = $associativity;
- }
- return $leftOperand;
- }
-
- const OPERATOR_PRECEDENCE_AND_ASSOCIATIVITY =
- [
- // logical-inc-OR-expression-2 (L)
- TokenKind::OrKeyword => [6, Associativity::Left],
-
- // logical-exc-OR-expression-2 (L)
- TokenKind::XorKeyword=> [7, Associativity::Left],
-
- // logical-AND-expression-2 (L)
- TokenKind::AndKeyword=> [8, Associativity::Left],
-
- // simple-assignment-expression (R)
- // TODO byref-assignment-expression
- TokenKind::EqualsToken => [9, Associativity::Right],
-
- // compound-assignment-expression (R)
- TokenKind::AsteriskAsteriskEqualsToken => [9, Associativity::Right],
- TokenKind::AsteriskEqualsToken => [9, Associativity::Right],
- TokenKind::SlashEqualsToken => [9, Associativity::Right],
- TokenKind::PercentEqualsToken => [9, Associativity::Right],
- TokenKind::PlusEqualsToken => [9, Associativity::Right],
- TokenKind::MinusEqualsToken => [9, Associativity::Right],
- TokenKind::DotEqualsToken => [9, Associativity::Right],
- TokenKind::LessThanLessThanEqualsToken => [9, Associativity::Right],
- TokenKind::GreaterThanGreaterThanEqualsToken => [9, Associativity::Right],
- TokenKind::AmpersandEqualsToken => [9, Associativity::Right],
- TokenKind::CaretEqualsToken => [9, Associativity::Right],
- TokenKind::BarEqualsToken => [9, Associativity::Right],
- TokenKind::QuestionQuestionEqualsToken => [9, Associativity::Right],
-
- // TODO conditional-expression (L)
- TokenKind::QuestionToken => [10, Associativity::Left],
-// TokenKind::ColonToken => [9, Associativity::Left],
-
- // TODO coalesce-expression (R)
- TokenKind::QuestionQuestionToken => [9, Associativity::Right],
-
- //logical-inc-OR-expression-1 (L)
- TokenKind::BarBarToken => [12, Associativity::Left],
-
- // logical-AND-expression-1 (L)
- TokenKind::AmpersandAmpersandToken => [13, Associativity::Left],
-
- // bitwise-inc-OR-expression (L)
- TokenKind::BarToken => [14, Associativity::Left],
-
- // bitwise-exc-OR-expression (L)
- TokenKind::CaretToken => [15, Associativity::Left],
-
- // bitwise-AND-expression (L)
- TokenKind::AmpersandToken => [16, Associativity::Left],
-
- // equality-expression (X)
- TokenKind::EqualsEqualsToken => [17, Associativity::None],
- TokenKind::ExclamationEqualsToken => [17, Associativity::None],
- TokenKind::LessThanGreaterThanToken => [17, Associativity::None],
- TokenKind::EqualsEqualsEqualsToken => [17, Associativity::None],
- TokenKind::ExclamationEqualsEqualsToken => [17, Associativity::None],
- TokenKind::LessThanEqualsGreaterThanToken => [17, Associativity::None],
-
- // relational-expression (X)
- TokenKind::LessThanToken => [18, Associativity::None],
- TokenKind::GreaterThanToken => [18, Associativity::None],
- TokenKind::LessThanEqualsToken => [18, Associativity::None],
- TokenKind::GreaterThanEqualsToken => [18, Associativity::None],
-
- // shift-expression (L)
- TokenKind::LessThanLessThanToken => [19, Associativity::Left],
- TokenKind::GreaterThanGreaterThanToken => [19, Associativity::Left],
-
- // additive-expression (L)
- TokenKind::PlusToken => [20, Associativity::Left],
- TokenKind::MinusToken => [20, Associativity::Left],
- TokenKind::DotToken =>[20, Associativity::Left],
-
- // multiplicative-expression (L)
- TokenKind::AsteriskToken => [21, Associativity::Left],
- TokenKind::SlashToken => [21, Associativity::Left],
- TokenKind::PercentToken => [21, Associativity::Left],
-
- // instanceof-expression (X)
- TokenKind::InstanceOfKeyword => [22, Associativity::None],
-
- // exponentiation-expression (R)
- TokenKind::AsteriskAsteriskToken => [23, Associativity::Right]
- ];
-
- const UNKNOWN_PRECEDENCE_AND_ASSOCIATIVITY = [-1, -1];
-
- private function getBinaryOperatorPrecedenceAndAssociativity($token) {
- return self::OPERATOR_PRECEDENCE_AND_ASSOCIATIVITY[$token->kind] ?? self::UNKNOWN_PRECEDENCE_AND_ASSOCIATIVITY;
- }
-
- /**
- * @internal Do not use outside this class, this may be changed or removed.
- */
- const KNOWN_ASSIGNMENT_TOKEN_SET = [
- TokenKind::AsteriskAsteriskEqualsToken => true,
- TokenKind::AsteriskEqualsToken => true,
- TokenKind::SlashEqualsToken => true,
- TokenKind::PercentEqualsToken => true,
- TokenKind::PlusEqualsToken => true,
- TokenKind::MinusEqualsToken => true,
- TokenKind::DotEqualsToken => true,
- TokenKind::LessThanLessThanEqualsToken => true,
- TokenKind::GreaterThanGreaterThanEqualsToken => true,
- TokenKind::AmpersandEqualsToken => true,
- TokenKind::CaretEqualsToken => true,
- TokenKind::BarEqualsToken => true,
- TokenKind::QuestionQuestionEqualsToken => true,
- // InstanceOf has other remaining issues, but this heuristic is an improvement for many common cases such as `$x && $y = $z`
- ];
-
- private function makeBinaryExpression($leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode) {
- $assignmentExpression = $operatorToken->kind === TokenKind::EqualsToken;
- if ($assignmentExpression || \array_key_exists($operatorToken->kind, self::KNOWN_ASSIGNMENT_TOKEN_SET)) {
- if ($leftOperand instanceof BinaryExpression) {
- if (!\array_key_exists($leftOperand->operator->kind, self::KNOWN_ASSIGNMENT_TOKEN_SET)) {
- // Handle cases without parenthesis, such as $x ** $y === $z, as $x ** ($y === $z)
- return $this->shiftBinaryOperands($leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode);
- }
- } elseif ($leftOperand instanceof UnaryOpExpression || $leftOperand instanceof ErrorControlExpression) {
- return $this->shiftUnaryOperands($leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode);
- }
- }
- $binaryExpression = $assignmentExpression ? new AssignmentExpression() : new BinaryExpression();
- $binaryExpression->parent = $parentNode;
- $leftOperand->parent = $binaryExpression;
- $rightOperand->parent = $binaryExpression;
- $binaryExpression->leftOperand = $leftOperand;
- $binaryExpression->operator = $operatorToken;
- if ($binaryExpression instanceof AssignmentExpression && isset($byRefToken)) {
- $binaryExpression->byRef = $byRefToken;
- }
- $binaryExpression->rightOperand = $rightOperand;
- return $binaryExpression;
- }
-
- /**
- * @param ErrorControlExpression|UnaryOpExpression $leftOperand
- */
- private function shiftUnaryOperands(UnaryExpression $leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode) {
- $outerUnaryOpExpression = clone($leftOperand);
- $inner = $this->makeBinaryExpression(
- $leftOperand->operand,
- $operatorToken,
- $byRefToken,
- $rightOperand,
- $outerUnaryOpExpression
- );
- // Either ErrorControlExpression or a UnaryOpExpression
- $outerUnaryOpExpression->parent = $parentNode;
- // TODO should this binaryExpression be wrapped in a UnaryExpression?
- $outerUnaryOpExpression->operand = $inner;
-
- return $outerUnaryOpExpression;
- }
-
- private function shiftBinaryOperands(BinaryExpression $leftOperand, $operatorToken, $byRefToken, $rightOperand, $parentNode) {
- $inner = $this->makeBinaryExpression(
- $leftOperand->rightOperand,
- $operatorToken,
- $byRefToken,
- $rightOperand,
- $parentNode
- );
- $outer = $this->makeBinaryExpression(
- $leftOperand->leftOperand,
- $leftOperand->operator,
- null,
- $inner,
- $parentNode
- );
- $inner->parent = $outer;
- return $outer;
- }
-
- private function parseDoStatement($parentNode) {
- $doStatement = new DoStatement();
- $doStatement->parent = $parentNode;
- $doStatement->do = $this->eat1(TokenKind::DoKeyword);
- $doStatement->statement = $this->parseStatement($doStatement);
- $doStatement->whileToken = $this->eat1(TokenKind::WhileKeyword);
- $doStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $doStatement->expression = $this->parseExpression($doStatement);
- $doStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $doStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- return $doStatement;
- }
-
- private function parseForStatement($parentNode) {
- $forStatement = new ForStatement();
- $forStatement->parent = $parentNode;
- $forStatement->for = $this->eat1(TokenKind::ForKeyword);
- $forStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $forStatement->forInitializer = $this->parseExpressionList($forStatement); // TODO spec is redundant
- $forStatement->exprGroupSemicolon1 = $this->eat1(TokenKind::SemicolonToken);
- $forStatement->forControl = $this->parseExpressionList($forStatement);
- $forStatement->exprGroupSemicolon2 = $this->eat1(TokenKind::SemicolonToken);
- $forStatement->forEndOfLoop = $this->parseExpressionList($forStatement);
- $forStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $forStatement->colon = $this->eatOptional1(TokenKind::ColonToken);
- if ($forStatement->colon !== null) {
- $forStatement->statements = $this->parseList($forStatement, ParseContext::ForStatementElements);
- $forStatement->endFor = $this->eat1(TokenKind::EndForKeyword);
- $forStatement->endForSemicolon = $this->eatSemicolonOrAbortStatement();
- } elseif (!$this->checkToken(TokenKind::ScriptSectionEndTag)) {
- $forStatement->statements = $this->parseStatement($forStatement);
- }
- return $forStatement;
- }
-
- private function parseForeachStatement($parentNode) {
- $foreachStatement = new ForeachStatement();
- $foreachStatement->parent = $parentNode;
- $foreachStatement->foreach = $this->eat1(TokenKind::ForeachKeyword);
- $foreachStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $foreachStatement->forEachCollectionName = $this->parseExpression($foreachStatement);
- $foreachStatement->asKeyword = $this->eat1(TokenKind::AsKeyword);
- $foreachStatement->foreachKey = $this->tryParseForeachKey($foreachStatement);
- $foreachStatement->foreachValue = $this->parseForeachValue($foreachStatement);
- $foreachStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $foreachStatement->colon = $this->eatOptional1(TokenKind::ColonToken);
- if ($foreachStatement->colon !== null) {
- $foreachStatement->statements = $this->parseList($foreachStatement, ParseContext::ForeachStatementElements);
- $foreachStatement->endForeach = $this->eat1(TokenKind::EndForEachKeyword);
- $foreachStatement->endForeachSemicolon = $this->eatSemicolonOrAbortStatement();
- } elseif (!$this->checkToken(TokenKind::ScriptSectionEndTag)) {
- $foreachStatement->statements = $this->parseStatement($foreachStatement);
- }
- return $foreachStatement;
- }
-
- private function tryParseForeachKey($parentNode) {
- if (!$this->isExpressionStart($this->getCurrentToken())) {
- return null;
- }
-
- $startPos = $this->lexer->getCurrentPosition();
- $startToken = $this->getCurrentToken();
- $foreachKey = new ForeachKey();
- $foreachKey->parent = $parentNode;
- $foreachKey->expression = $this->parseExpression($foreachKey);
-
- if (!$this->checkToken(TokenKind::DoubleArrowToken)) {
- $this->lexer->setCurrentPosition($startPos);
- $this->token = $startToken;
- return null;
- }
-
- $foreachKey->arrow = $this->eat1(TokenKind::DoubleArrowToken);
- return $foreachKey;
- }
-
- private function parseForeachValue($parentNode) {
- $foreachValue = new ForeachValue();
- $foreachValue->parent = $parentNode;
- $foreachValue->ampersand = $this->eatOptional1(TokenKind::AmpersandToken);
- $foreachValue->expression = $this->parseExpression($foreachValue);
- return $foreachValue;
- }
-
- private function parseGotoStatement($parentNode) {
- $gotoStatement = new GotoStatement();
- $gotoStatement->parent = $parentNode;
- $gotoStatement->goto = $this->eat1(TokenKind::GotoKeyword);
- $gotoStatement->name = $this->eat1(TokenKind::Name);
- $gotoStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- return $gotoStatement;
- }
-
- private function parseBreakOrContinueStatement($parentNode) {
- // TODO should be error checking if on top level
- $continueStatement = new BreakOrContinueStatement();
- $continueStatement->parent = $parentNode;
- $continueStatement->breakOrContinueKeyword = $this->eat(TokenKind::ContinueKeyword, TokenKind::BreakKeyword);
-
- if ($this->isExpressionStart($this->getCurrentToken())) {
- $continueStatement->breakoutLevel = $this->parseExpression($continueStatement);
- }
-
- $continueStatement->semicolon = $this->eatSemicolonOrAbortStatement();
-
- return $continueStatement;
- }
-
- private function parseReturnStatement($parentNode) {
- $returnStatement = new ReturnStatement();
- $returnStatement->parent = $parentNode;
- $returnStatement->returnKeyword = $this->eat1(TokenKind::ReturnKeyword);
- if ($this->isExpressionStart($this->getCurrentToken())) {
- $returnStatement->expression = $this->parseExpression($returnStatement);
- }
- $returnStatement->semicolon = $this->eatSemicolonOrAbortStatement();
-
- return $returnStatement;
- }
-
- /** @return ThrowExpression */
- private function parseThrowExpression($parentNode) {
- $throwExpression = new ThrowExpression();
- $throwExpression->parent = $parentNode;
- $throwExpression->throwKeyword = $this->eat1(TokenKind::ThrowKeyword);
- // TODO error for failures to parse expressions when not optional
- $throwExpression->expression = $this->parseExpression($throwExpression);
-
- return $throwExpression;
- }
-
- private function parseTryStatement($parentNode) {
- $tryStatement = new TryStatement();
- $tryStatement->parent = $parentNode;
- $tryStatement->tryKeyword = $this->eat1(TokenKind::TryKeyword);
- $tryStatement->compoundStatement = $this->parseCompoundStatement($tryStatement); // TODO verifiy this is only compound
-
- $tryStatement->catchClauses = []; // TODO - should be some standard for empty arrays vs. null?
- while ($this->checkToken(TokenKind::CatchKeyword)) {
- $tryStatement->catchClauses[] = $this->parseCatchClause($tryStatement);
- }
-
- if ($this->checkToken(TokenKind::FinallyKeyword)) {
- $tryStatement->finallyClause = $this->parseFinallyClause($tryStatement);
- }
-
- return $tryStatement;
- }
-
- private function parseCatchClause($parentNode) {
- $catchClause = new CatchClause();
- $catchClause->parent = $parentNode;
- $catchClause->catch = $this->eat1(TokenKind::CatchKeyword);
- $catchClause->openParen = $this->eat1(TokenKind::OpenParenToken);
- $catchClause->qualifiedNameList = $this->parseQualifiedNameCatchList($catchClause) ?? new MissingToken(TokenKind::QualifiedName, $this->token->fullStart); // TODO generate missing token or error if null
- $catchClause->variableName = $this->eatOptional1(TokenKind::VariableName);
- $catchClause->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $catchClause->compoundStatement = $this->parseCompoundStatement($catchClause);
-
- return $catchClause;
- }
-
- private function parseFinallyClause($parentNode) {
- $finallyClause = new FinallyClause();
- $finallyClause->parent = $parentNode;
- $finallyClause->finallyToken = $this->eat1(TokenKind::FinallyKeyword);
- $finallyClause->compoundStatement = $this->parseCompoundStatement($finallyClause);
-
- return $finallyClause;
- }
-
- private function parseDeclareStatement($parentNode) {
- $declareStatement = new DeclareStatement();
- $declareStatement->parent = $parentNode;
- $declareStatement->declareKeyword = $this->eat1(TokenKind::DeclareKeyword);
- $declareStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $this->parseAndSetDeclareDirectiveList($declareStatement);
- $declareStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- if ($this->checkToken(TokenKind::SemicolonToken)) {
- $declareStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- } elseif ($this->checkToken(TokenKind::ColonToken)) {
- $declareStatement->colon = $this->eat1(TokenKind::ColonToken);
- $declareStatement->statements = $this->parseList($declareStatement, ParseContext::DeclareStatementElements);
- $declareStatement->enddeclareKeyword = $this->eat1(TokenKind::EndDeclareKeyword);
- $declareStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- } else {
- $declareStatement->statements = $this->parseStatement($declareStatement);
- }
-
- return $declareStatement;
- }
-
- /**
- * @param DeclareStatement $parentNode
- */
- private function parseAndSetDeclareDirectiveList($parentNode) {
- $declareDirectiveList = $this->parseDeclareDirectiveList($parentNode);
-
- $parentNode->declareDirectiveList = $declareDirectiveList ?? new MissingToken(TokenKind::Name, $this->token->fullStart);
- }
-
- /**
- * @param DeclareStatement $parentNode
- * @return DelimitedList\DeclareDirectiveList|null
- */
- private function parseDeclareDirectiveList($parentNode) {
- $declareDirectiveList = $this->parseDelimitedList(
- DelimitedList\DeclareDirectiveList::class,
- TokenKind::CommaToken,
- function ($token) {
- return $token->kind === TokenKind::Name;
- },
- $this->parseDeclareDirectiveFn(),
- $parentNode,
- false
- );
-
- return $declareDirectiveList;
- }
-
- private function parseDeclareDirectiveFn() {
- return function ($parentNode) {
- $declareDirective = new DeclareDirective();
- $declareDirective->parent = $parentNode;
- $declareDirective->name = $this->eat1(TokenKind::Name);
- $declareDirective->equals = $this->eat1(TokenKind::EqualsToken);
- $declareDirective->literal =
- $this->eat(
- TokenKind::FloatingLiteralToken,
- TokenKind::IntegerLiteralToken,
- TokenKind::DecimalLiteralToken,
- TokenKind::OctalLiteralToken,
- TokenKind::HexadecimalLiteralToken,
- TokenKind::BinaryLiteralToken,
- TokenKind::InvalidOctalLiteralToken,
- TokenKind::InvalidHexadecimalLiteral,
- TokenKind::InvalidBinaryLiteral,
- TokenKind::StringLiteralToken
- ); // TODO simplify
-
- return $declareDirective;
- };
- }
-
- private function parseSimpleVariable($parentNode) {
- return ($this->parseSimpleVariableFn())($parentNode);
- }
-
- private function parseSimpleVariableFn() {
- return function ($parentNode) {
- $token = $this->getCurrentToken();
- $variable = new Variable();
- $variable->parent = $parentNode;
-
- if ($token->kind === TokenKind::DollarToken) {
- $variable->dollar = $this->eat1(TokenKind::DollarToken);
- $token = $this->getCurrentToken();
-
- switch ($token->kind) {
- case TokenKind::OpenBraceToken:
- $variable->name = $this->parseBracedExpression($variable);
- break;
- case TokenKind::VariableName:
- case TokenKind::StringVarname:
- case TokenKind::DollarToken:
- $variable->name = $this->parseSimpleVariable($variable);
- break;
- default:
- $variable->name = new MissingToken(TokenKind::VariableName, $token->fullStart);
- break;
- }
- } elseif ($token->kind === TokenKind::VariableName || $token->kind === TokenKind::StringVarname) {
- // TODO consider splitting into dollar and name.
- // StringVarname is the variable name without $, used in a template string e.g. `"${foo}"`
- $variable->name = $this->eat(TokenKind::VariableName, TokenKind::StringVarname);
- } else {
- $variable->name = new MissingToken(TokenKind::VariableName, $token->fullStart);
- }
-
- return $variable;
- };
- }
-
- private function parseYieldExpression($parentNode) {
- $yieldExpression = new YieldExpression();
- $yieldExpression->parent = $parentNode;
- $yieldExpression->yieldOrYieldFromKeyword = $this->eat(
- TokenKind::YieldFromKeyword,
- TokenKind::YieldKeyword
- );
- if ($yieldExpression->yieldOrYieldFromKeyword->kind === TokenKind::YieldFromKeyword) {
- // Don't use parseArrayElement. E.g. `yield from &$varName` or `yield from $key => $varName` are both syntax errors
- $arrayElement = new ArrayElement();
- $arrayElement->parent = $yieldExpression;
- $arrayElement->elementValue = $this->parseExpression($arrayElement);
- $yieldExpression->arrayElement = $arrayElement;
- } else {
- // This is always an ArrayElement for backwards compatibilitiy.
- // TODO: Can this be changed to a non-ArrayElement in a future release?
- if ($this->isExpressionStart($this->getCurrentToken())) {
- // Both `yield expr;` and `yield;` are possible.
- $yieldExpression->arrayElement = $this->parseArrayElement($yieldExpression);
- } else {
- $yieldExpression->arrayElement = null;
- }
- }
-
- return $yieldExpression;
- }
-
- private function parseScriptInclusionExpression($parentNode) {
- $scriptInclusionExpression = new ScriptInclusionExpression();
- $scriptInclusionExpression->parent = $parentNode;
- $scriptInclusionExpression->requireOrIncludeKeyword =
- $this->eat(
- TokenKind::RequireKeyword, TokenKind::RequireOnceKeyword,
- TokenKind::IncludeKeyword, TokenKind::IncludeOnceKeyword
- );
- $scriptInclusionExpression->expression = $this->parseExpression($scriptInclusionExpression);
- return $scriptInclusionExpression;
- }
-
- /** @return EchoStatement */
- private function parseEchoStatement($parentNode) {
- $echoStatement = new EchoStatement();
- $echoStatement->parent = $parentNode;
- $echoStatement->echoKeyword = $this->eat1(TokenKind::EchoKeyword);
- $echoStatement->expressions =
- $this->parseExpressionList($echoStatement);
- $echoStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- return $echoStatement;
- }
-
- private function parseListIntrinsicExpression($parentNode) {
- $listExpression = new ListIntrinsicExpression();
- $listExpression->parent = $parentNode;
- $listExpression->listKeyword = $this->eat1(TokenKind::ListKeyword);
- $listExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- // TODO - parse loosely as ArrayElementList, and validate parse tree later
- $listExpression->listElements =
- $this->parseArrayElementList($listExpression, DelimitedList\ListExpressionList::class);
- $listExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $listExpression;
- }
-
- private function isArrayElementStart($token) {
- return ($this->isArrayElementStartFn())($token);
- }
-
- private function isArrayElementStartFn() {
- return function ($token) {
- return $token->kind === TokenKind::AmpersandToken || $token->kind === TokenKind::DotDotDotToken || $this->isExpressionStart($token);
- };
- }
-
- private function parseArrayElement($parentNode) {
- return ($this->parseArrayElementFn())($parentNode);
- }
-
- private function parseArrayElementFn() {
- return function ($parentNode) {
- $arrayElement = new ArrayElement();
- $arrayElement->parent = $parentNode;
-
- if ($this->checkToken(TokenKind::AmpersandToken)) {
- $arrayElement->byRef = $this->eat1(TokenKind::AmpersandToken);
- $arrayElement->elementValue = $this->parseExpression($arrayElement);
- } elseif ($this->checkToken(TokenKind::DotDotDotToken)) {
- $arrayElement->dotDotDot = $this->eat1(TokenKind::DotDotDotToken);
- $arrayElement->elementValue = $this->parseExpression($arrayElement);
- } else {
- $expression = $this->parseExpression($arrayElement);
- if ($this->checkToken(TokenKind::DoubleArrowToken)) {
- $arrayElement->elementKey = $expression;
- $arrayElement->arrowToken = $this->eat1(TokenKind::DoubleArrowToken);
- $arrayElement->byRef = $this->eatOptional1(TokenKind::AmpersandToken); // TODO not okay for list expressions
- $arrayElement->elementValue = $this->parseExpression($arrayElement);
- } else {
- $arrayElement->elementValue = $expression;
- }
- }
-
- return $arrayElement;
- };
- }
-
- private function parseExpressionList($parentExpression) {
- return $this->parseDelimitedList(
- DelimitedList\ExpressionList::class,
- TokenKind::CommaToken,
- $this->isExpressionStartFn(),
- $this->parseExpressionFn(),
- $parentExpression
- );
- }
-
- private function parseUnsetStatement($parentNode) {
- $unsetStatement = new UnsetStatement();
- $unsetStatement->parent = $parentNode;
-
- $unsetStatement->unsetKeyword = $this->eat1(TokenKind::UnsetKeyword);
- $unsetStatement->openParen = $this->eat1(TokenKind::OpenParenToken);
- $unsetStatement->expressions = $this->parseExpressionList($unsetStatement);
- $unsetStatement->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $unsetStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- return $unsetStatement;
- }
-
- private function parseArrayCreationExpression($parentNode) {
- $arrayExpression = new ArrayCreationExpression();
- $arrayExpression->parent = $parentNode;
-
- $arrayExpression->arrayKeyword = $this->eatOptional1(TokenKind::ArrayKeyword);
-
- $arrayExpression->openParenOrBracket = $arrayExpression->arrayKeyword !== null
- ? $this->eat1(TokenKind::OpenParenToken)
- : $this->eat1(TokenKind::OpenBracketToken);
-
- $arrayExpression->arrayElements = $this->parseArrayElementList($arrayExpression, DelimitedList\ArrayElementList::class);
-
- $arrayExpression->closeParenOrBracket = $arrayExpression->arrayKeyword !== null
- ? $this->eat1(TokenKind::CloseParenToken)
- : $this->eat1(TokenKind::CloseBracketToken);
-
- return $arrayExpression;
- }
-
- private function parseArrayElementList($listExpression, $className) {
- return $this->parseDelimitedList(
- $className,
- TokenKind::CommaToken,
- $this->isArrayElementStartFn(),
- $this->parseArrayElementFn(),
- $listExpression,
- true
- );
- }
-
- private function parseEmptyIntrinsicExpression($parentNode) {
- $emptyExpression = new EmptyIntrinsicExpression();
- $emptyExpression->parent = $parentNode;
-
- $emptyExpression->emptyKeyword = $this->eat1(TokenKind::EmptyKeyword);
- $emptyExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $emptyExpression->expression = $this->parseExpression($emptyExpression);
- $emptyExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $emptyExpression;
- }
-
- private function parseEvalIntrinsicExpression($parentNode) {
- $evalExpression = new EvalIntrinsicExpression();
- $evalExpression->parent = $parentNode;
-
- $evalExpression->evalKeyword = $this->eat1(TokenKind::EvalKeyword);
- $evalExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $evalExpression->expression = $this->parseExpression($evalExpression);
- $evalExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $evalExpression;
- }
-
- private function parseParenthesizedExpression($parentNode) {
- $parenthesizedExpression = new ParenthesizedExpression();
- $parenthesizedExpression->parent = $parentNode;
-
- $parenthesizedExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $parenthesizedExpression->expression = $this->parseExpression($parenthesizedExpression);
- $parenthesizedExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $parenthesizedExpression;
- }
-
- private function parseExitIntrinsicExpression($parentNode) {
- $exitExpression = new ExitIntrinsicExpression();
- $exitExpression->parent = $parentNode;
-
- $exitExpression->exitOrDieKeyword = $this->eat(TokenKind::ExitKeyword, TokenKind::DieKeyword);
- $exitExpression->openParen = $this->eatOptional1(TokenKind::OpenParenToken);
- if ($exitExpression->openParen !== null) {
- if ($this->isExpressionStart($this->getCurrentToken())) {
- $exitExpression->expression = $this->parseExpression($exitExpression);
- }
- $exitExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
- }
-
- return $exitExpression;
- }
-
- private function parsePrintIntrinsicExpression($parentNode) {
- $printExpression = new PrintIntrinsicExpression();
- $printExpression->parent = $parentNode;
-
- $printExpression->printKeyword = $this->eat1(TokenKind::PrintKeyword);
- $printExpression->expression = $this->parseExpression($printExpression);
-
- return $printExpression;
- }
-
- private function parseIssetIntrinsicExpression($parentNode) {
- $issetExpression = new IssetIntrinsicExpression();
- $issetExpression->parent = $parentNode;
-
- $issetExpression->issetKeyword = $this->eat1(TokenKind::IsSetKeyword);
- $issetExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $issetExpression->expressions = $this->parseExpressionList($issetExpression);
- $issetExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $issetExpression;
- }
-
- private function parseUnaryOpExpression($parentNode) {
- $unaryOpExpression = new UnaryOpExpression();
- $unaryOpExpression->parent = $parentNode;
- $unaryOpExpression->operator =
- $this->eat(TokenKind::PlusToken, TokenKind::MinusToken, TokenKind::ExclamationToken, TokenKind::TildeToken);
- $unaryOpExpression->operand = $this->parseUnaryExpressionOrHigher($unaryOpExpression);
-
- return $unaryOpExpression;
- }
-
- private function parseErrorControlExpression($parentNode) {
- $errorControlExpression = new ErrorControlExpression();
- $errorControlExpression->parent = $parentNode;
-
- $errorControlExpression->operator = $this->eat1(TokenKind::AtSymbolToken);
- $errorControlExpression->operand = $this->parseUnaryExpressionOrHigher($errorControlExpression);
-
- return $errorControlExpression;
- }
-
- private function parsePrefixUpdateExpression($parentNode) {
- $prefixUpdateExpression = new PrefixUpdateExpression();
- $prefixUpdateExpression->parent = $parentNode;
-
- $prefixUpdateExpression->incrementOrDecrementOperator = $this->eat(TokenKind::PlusPlusToken, TokenKind::MinusMinusToken);
-
- $prefixUpdateExpression->operand = $this->parsePrimaryExpression($prefixUpdateExpression);
-
- if (!($prefixUpdateExpression->operand instanceof MissingToken)) {
- $prefixUpdateExpression->operand = $this->parsePostfixExpressionRest($prefixUpdateExpression->operand, false);
- }
-
- // TODO also check operand expression validity
- return $prefixUpdateExpression;
- }
-
- private function parsePostfixExpressionRest($expression, $allowUpdateExpression = true) {
- $tokenKind = $this->getCurrentToken()->kind;
-
- // `--$a++` is invalid
- if ($allowUpdateExpression &&
- ($tokenKind === TokenKind::PlusPlusToken ||
- $tokenKind === TokenKind::MinusMinusToken)) {
- return $this->parseParsePostfixUpdateExpression($expression);
- }
-
- // TODO write tons of tests
- if (!($expression instanceof Variable ||
- $expression instanceof ParenthesizedExpression ||
- $expression instanceof QualifiedName ||
- $expression instanceof CallExpression ||
- $expression instanceof MemberAccessExpression ||
- $expression instanceof SubscriptExpression ||
- $expression instanceof ScopedPropertyAccessExpression ||
- $expression instanceof StringLiteral ||
- $expression instanceof ArrayCreationExpression
- )) {
- return $expression;
- }
- if ($tokenKind === TokenKind::ColonColonToken) {
- $expression = $this->parseScopedPropertyAccessExpression($expression, null);
- return $this->parsePostfixExpressionRest($expression);
- }
-
- $tokenKind = $this->getCurrentToken()->kind;
-
- if ($tokenKind === TokenKind::OpenBraceToken ||
- $tokenKind === TokenKind::OpenBracketToken) {
- $expression = $this->parseSubscriptExpression($expression);
- return $this->parsePostfixExpressionRest($expression);
- }
-
- if ($expression instanceof ArrayCreationExpression) {
- // Remaining postfix expressions are invalid, so abort
- return $expression;
- }
-
- if ($tokenKind === TokenKind::ArrowToken || $tokenKind === TokenKind::QuestionArrowToken) {
- $expression = $this->parseMemberAccessExpression($expression);
- return $this->parsePostfixExpressionRest($expression);
- }
-
- if ($tokenKind === TokenKind::OpenParenToken && !$this->isParsingUnparenthesizedObjectCreationExpression($expression)) {
- $expression = $this->parseCallExpressionRest($expression);
-
- if (!$this->checkToken(TokenKind::OpenParenToken)) {
- return $this->parsePostfixExpressionRest($expression);
- }
- if (
- $expression instanceof ParenthesizedExpression ||
- $expression instanceof CallExpression ||
- $expression instanceof SubscriptExpression) {
- // Continue parsing the remaining brackets for expressions
- // such as `(new Foo())()`, `foo()()`, `foo()['index']()`
- return $this->parsePostfixExpressionRest($expression);
- }
- return $expression;
- }
-
- // Reached the end of the postfix-expression, so return
- return $expression;
- }
-
- private function parseMemberName($parentNode) {
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- case TokenKind::Name:
- $this->advanceToken(); // TODO all names should be Nodes
- return $token;
- case TokenKind::VariableName:
- case TokenKind::DollarToken:
- return $this->parseSimpleVariable($parentNode); // TODO should be simple-variable
- case TokenKind::OpenBraceToken:
- return $this->parseBracedExpression($parentNode);
-
- default:
- if (\in_array($token->kind, $this->nameOrKeywordOrReservedWordTokens)) {
- $this->advanceToken();
- $token->kind = TokenKind::Name;
- return $token;
- }
- }
- return new MissingToken(TokenKind::MemberName, $token->fullStart);
- }
-
- private function isArgumentExpressionStartFn() {
- return function ($token) {
- return
- isset($this->argumentStartTokensSet[$token->kind]) || $this->isExpressionStart($token);
- };
- }
-
- private function parseArgumentExpressionFn() {
- return function ($parentNode) {
- $argumentExpression = new ArgumentExpression();
- $argumentExpression->parent = $parentNode;
-
- $nextToken = $this->lexer->getTokensArray()[$this->lexer->getCurrentPosition()] ?? null;
- if ($nextToken && $nextToken->kind === TokenKind::ColonToken) {
- $name = $this->token;
- $this->advanceToken();
- if ($name->kind === TokenKind::YieldFromKeyword || !\in_array($name->kind, $this->nameOrKeywordOrReservedWordTokens)) {
- $name = new SkippedToken($name);
- } else {
- $name->kind = TokenKind::Name;
- }
- $argumentExpression->name = $name;
- $argumentExpression->colonToken = $this->eat1(TokenKind::ColonToken);
- } else {
- $argumentExpression->dotDotDotToken = $this->eatOptional1(TokenKind::DotDotDotToken);
- }
- $argumentExpression->expression = $this->parseExpression($argumentExpression);
- return $argumentExpression;
- };
- }
-
- private function parseCallExpressionRest($expression) {
- $callExpression = new CallExpression();
- $callExpression->parent = $expression->parent;
- $expression->parent = $callExpression;
- $callExpression->callableExpression = $expression;
- $callExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $callExpression->argumentExpressionList =
- $this->parseArgumentExpressionList($callExpression);
- $callExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
- return $callExpression;
- }
-
- private function parseParsePostfixUpdateExpression($prefixExpression) {
- $postfixUpdateExpression = new PostfixUpdateExpression();
- $postfixUpdateExpression->operand = $prefixExpression;
- $postfixUpdateExpression->parent = $prefixExpression->parent;
- $prefixExpression->parent = $postfixUpdateExpression;
- $postfixUpdateExpression->incrementOrDecrementOperator =
- $this->eat(TokenKind::PlusPlusToken, TokenKind::MinusMinusToken);
- return $postfixUpdateExpression;
- }
-
- private function parseBracedExpression($parentNode) {
- $bracedExpression = new BracedExpression();
- $bracedExpression->parent = $parentNode;
-
- $bracedExpression->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $bracedExpression->expression = $this->parseExpression($bracedExpression);
- $bracedExpression->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
-
- return $bracedExpression;
- }
-
- private function parseSubscriptExpression($expression) : SubscriptExpression {
- $subscriptExpression = new SubscriptExpression();
- $subscriptExpression->parent = $expression->parent;
- $expression->parent = $subscriptExpression;
-
- $subscriptExpression->postfixExpression = $expression;
- $subscriptExpression->openBracketOrBrace = $this->eat(TokenKind::OpenBracketToken, TokenKind::OpenBraceToken);
- $subscriptExpression->accessExpression = $this->isExpressionStart($this->getCurrentToken())
- ? $this->parseExpression($subscriptExpression)
- : null; // TODO error if used in a getter
-
- if ($subscriptExpression->openBracketOrBrace->kind === TokenKind::OpenBraceToken) {
- $subscriptExpression->closeBracketOrBrace = $this->eat1(TokenKind::CloseBraceToken);
- } else {
- $subscriptExpression->closeBracketOrBrace = $this->eat1(TokenKind::CloseBracketToken);
- }
-
- return $subscriptExpression;
- }
-
- private function parseMemberAccessExpression($expression):MemberAccessExpression {
- $memberAccessExpression = new MemberAccessExpression();
- $memberAccessExpression->parent = $expression->parent;
- $expression->parent = $memberAccessExpression;
-
- $memberAccessExpression->dereferencableExpression = $expression;
- $memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
- $memberAccessExpression->memberName = $this->parseMemberName($memberAccessExpression);
-
- return $memberAccessExpression;
- }
-
- /**
- * @param Node|null $expression
- * @param Node|null $fallbackParentNode (Workaround for the invalid AST `use TraitName::foo as ::x`)
- */
- private function parseScopedPropertyAccessExpression($expression, $fallbackParentNode): ScopedPropertyAccessExpression {
- $scopedPropertyAccessExpression = new ScopedPropertyAccessExpression();
- $scopedPropertyAccessExpression->parent = $expression->parent ?? $fallbackParentNode;
- if ($expression instanceof Node) {
- $expression->parent = $scopedPropertyAccessExpression;
- $scopedPropertyAccessExpression->scopeResolutionQualifier = $expression; // TODO ensure always a Node
- }
-
- $scopedPropertyAccessExpression->doubleColon = $this->eat1(TokenKind::ColonColonToken);
- $scopedPropertyAccessExpression->memberName = $this->parseMemberName($scopedPropertyAccessExpression);
-
- return $scopedPropertyAccessExpression;
- }
-
- public function isParsingUnparenthesizedObjectCreationExpression($expression) {
- if (!$this->isParsingObjectCreationExpression) {
- return false;
- }
- if ($expression instanceof Token) {
- return true;
- }
- while ($expression->parent) {
- $expression = $expression->parent;
- if ($expression instanceof ObjectCreationExpression) {
- return true;
- } elseif ($expression instanceof ParenthesizedExpression) {
- return false;
- }
- }
- }
-
- private $isParsingObjectCreationExpression = false;
-
- private function parseObjectCreationExpression($parentNode) {
- $objectCreationExpression = new ObjectCreationExpression();
- $objectCreationExpression->parent = $parentNode;
- $objectCreationExpression->newKeword = $this->eat1(TokenKind::NewKeyword);
- // TODO - add tests for this scenario
- $oldIsParsingObjectCreationExpression = $this->isParsingObjectCreationExpression;
- $this->isParsingObjectCreationExpression = true;
-
- if ($this->getCurrentToken()->kind === TokenKind::AttributeToken) {
- // Attributes such as `new #[MyAttr] class` can only be used with anonymous class declarations.
- // But handle this like $objectCreationExpression->classMembers and leave it up to the applications to detect the invalid combination.
- $objectCreationExpression->attributes = $this->parseAttributeGroups($objectCreationExpression);
- }
- $objectCreationExpression->classTypeDesignator =
- $this->eatOptional1(TokenKind::ClassKeyword) ??
- $this->parseExpression($objectCreationExpression);
-
- $this->isParsingObjectCreationExpression = $oldIsParsingObjectCreationExpression;
-
- $objectCreationExpression->openParen = $this->eatOptional1(TokenKind::OpenParenToken);
- if ($objectCreationExpression->openParen !== null) {
- $objectCreationExpression->argumentExpressionList = $this->parseArgumentExpressionList($objectCreationExpression);
- $objectCreationExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
- }
-
- $objectCreationExpression->classBaseClause = $this->parseClassBaseClause($objectCreationExpression);
- $objectCreationExpression->classInterfaceClause = $this->parseClassInterfaceClause($objectCreationExpression);
-
- if ($this->getCurrentToken()->kind === TokenKind::OpenBraceToken) {
- $objectCreationExpression->classMembers = $this->parseClassMembers($objectCreationExpression);
- }
-
- return $objectCreationExpression;
- }
-
- /**
- * @return DelimitedList\ArgumentExpressionList|null
- */
- private function parseArgumentExpressionList($parentNode) {
- $list = $this->parseDelimitedList(
- DelimitedList\ArgumentExpressionList::class,
- TokenKind::CommaToken,
- $this->isArgumentExpressionStartFn(),
- $this->parseArgumentExpressionFn(),
- $parentNode
- );
- $children = $list->children ?? null;
- if (is_array($children) && \count($children) === 1) {
- $arg = $children[0];
- if ($arg instanceof ArgumentExpression) {
- if ($arg->dotDotDotToken && $arg->expression instanceof MissingToken && !$arg->colonToken && !$arg->name) {
- $arg->expression = null;
- }
- }
- }
- return $list;
- }
-
- /**
- * @param Node|Token $leftOperand (should only be a token for invalid ASTs)
- * @param Token $questionToken
- * @param Node $fallbackParentNode
- */
- private function parseTernaryExpression($leftOperand, $questionToken, $fallbackParentNode):TernaryExpression {
- $ternaryExpression = new TernaryExpression();
- if ($leftOperand instanceof Node) {
- $ternaryExpression->parent = $leftOperand->parent;
- $leftOperand->parent = $ternaryExpression;
- } else {
- $ternaryExpression->parent = $fallbackParentNode;
- }
- $ternaryExpression->condition = $leftOperand;
- $ternaryExpression->questionToken = $questionToken;
- $ternaryExpression->ifExpression = $this->isExpressionStart($this->getCurrentToken()) ? $this->parseExpression($ternaryExpression) : null;
- $ternaryExpression->colonToken = $this->eat1(TokenKind::ColonToken);
- $ternaryExpression->elseExpression = $this->parseBinaryExpressionOrHigher(9, $ternaryExpression);
- $leftOperand = $ternaryExpression;
- return $leftOperand;
- }
-
- private function parseClassInterfaceClause($parentNode) {
- $classInterfaceClause = new ClassInterfaceClause();
- $classInterfaceClause->parent = $parentNode;
- $classInterfaceClause->implementsKeyword = $this->eatOptional1(TokenKind::ImplementsKeyword);
-
- if ($classInterfaceClause->implementsKeyword === null) {
- return null;
- }
-
- $classInterfaceClause->interfaceNameList =
- $this->parseQualifiedNameList($classInterfaceClause);
- return $classInterfaceClause;
- }
-
- private function parseClassBaseClause($parentNode) {
- $classBaseClause = new ClassBaseClause();
- $classBaseClause->parent = $parentNode;
-
- $classBaseClause->extendsKeyword = $this->eatOptional1(TokenKind::ExtendsKeyword);
- if ($classBaseClause->extendsKeyword === null) {
- return null;
- }
- $classBaseClause->baseClass = $this->parseQualifiedName($classBaseClause);
-
- return $classBaseClause;
- }
-
- private function parseClassConstDeclaration($parentNode, $modifiers) {
- $classConstDeclaration = new ClassConstDeclaration();
- $classConstDeclaration->parent = $parentNode;
-
- $classConstDeclaration->modifiers = $modifiers;
- $classConstDeclaration->constKeyword = $this->eat1(TokenKind::ConstKeyword);
- $classConstDeclaration->constElements = $this->parseConstElements($classConstDeclaration);
- $classConstDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
-
- return $classConstDeclaration;
- }
-
- private function parseEnumCaseDeclaration($parentNode) {
- $classConstDeclaration = new EnumCaseDeclaration();
- $classConstDeclaration->parent = $parentNode;
- $classConstDeclaration->caseKeyword = $this->eat1(TokenKind::CaseKeyword);
- $classConstDeclaration->name = $this->eat($this->nameOrKeywordOrReservedWordTokens);
- $classConstDeclaration->equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
- if ($classConstDeclaration->equalsToken !== null) {
- // TODO add post-parse rule that checks for invalid assignments
- $classConstDeclaration->assignment = $this->parseExpression($classConstDeclaration);
- }
- $classConstDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
-
- return $classConstDeclaration;
- }
-
- /**
- * @param Node $parentNode
- * @param Token[] $modifiers
- * @param Token|null $questionToken
- */
- private function parseRemainingPropertyDeclarationOrMissingMemberDeclaration($parentNode, $modifiers, $questionToken = null)
- {
- $typeDeclarationList = $this->tryParseParameterTypeDeclarationList(null);
- if ($this->getCurrentToken()->kind !== TokenKind::VariableName) {
- return $this->makeMissingMemberDeclaration($parentNode, $modifiers, $questionToken, $typeDeclarationList);
- }
- return $this->parsePropertyDeclaration($parentNode, $modifiers, $questionToken, $typeDeclarationList);
- }
-
- /**
- * @param Node $parentNode
- * @param Token[] $modifiers
- * @param Token|null $questionToken
- * @param DelimitedList\QualifiedNameList|null $typeDeclarationList
- */
- private function parsePropertyDeclaration($parentNode, $modifiers, $questionToken = null, $typeDeclarationList = null) {
- $propertyDeclaration = new PropertyDeclaration();
- $propertyDeclaration->parent = $parentNode;
-
- $propertyDeclaration->modifiers = $modifiers;
- $propertyDeclaration->questionToken = $questionToken;
- if ($typeDeclarationList) {
- $propertyDeclaration->typeDeclarationList = $typeDeclarationList;
- $typeDeclarationList->parent = $propertyDeclaration;
- } elseif ($questionToken) {
- $propertyDeclaration->typeDeclarationList = new MissingToken(TokenKind::PropertyType, $this->token->fullStart);
- }
- $propertyDeclaration->propertyElements = $this->parseExpressionList($propertyDeclaration);
- $propertyDeclaration->semicolon = $this->eat1(TokenKind::SemicolonToken);
-
- return $propertyDeclaration;
- }
-
- /**
- * @param Node $parentNode
- * @return DelimitedList\QualifiedNameList
- */
- private function parseQualifiedNameList($parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\QualifiedNameList::class,
- TokenKind::CommaToken,
- $this->isQualifiedNameStartFn(),
- $this->parseQualifiedNameFn(),
- $parentNode);
- }
-
- private function parseQualifiedNameCatchList($parentNode) {
- $result = $this->parseDelimitedList(
- DelimitedList\QualifiedNameList::class,
- TokenKind::BarToken,
- $this->isQualifiedNameStartForCatchFn(),
- $this->parseQualifiedNameFn(),
- $parentNode);
-
- // Add a MissingToken so that this will Warn about `catch (T| $x) {}`
- // TODO: Make this a reusable abstraction?
- if ($result && (end($result->children)->kind ?? null) === TokenKind::BarToken) {
- $result->children[] = new MissingToken(TokenKind::Name, $this->token->fullStart);
- }
- return $result;
- }
-
- private function parseInterfaceDeclaration($parentNode) {
- $interfaceDeclaration = new InterfaceDeclaration(); // TODO verify not nested
- $interfaceDeclaration->parent = $parentNode;
- $interfaceDeclaration->interfaceKeyword = $this->eat1(TokenKind::InterfaceKeyword);
- $interfaceDeclaration->name = $this->eat1(TokenKind::Name);
- $interfaceDeclaration->interfaceBaseClause = $this->parseInterfaceBaseClause($interfaceDeclaration);
- $interfaceDeclaration->interfaceMembers = $this->parseInterfaceMembers($interfaceDeclaration);
- return $interfaceDeclaration;
- }
-
- private function parseInterfaceMembers($parentNode) : Node {
- $interfaceMembers = new InterfaceMembers();
- $interfaceMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $interfaceMembers->interfaceMemberDeclarations = $this->parseList($interfaceMembers, ParseContext::InterfaceMembers);
- $interfaceMembers->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- $interfaceMembers->parent = $parentNode;
- return $interfaceMembers;
- }
-
- private function isInterfaceMemberDeclarationStart(Token $token) {
- switch ($token->kind) {
- // visibility-modifier
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
-
- // static-modifier
- case TokenKind::StaticKeyword:
-
- // readonly-modifier
- case TokenKind::ReadonlyKeyword:
-
- // class-modifier
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
-
- case TokenKind::ConstKeyword:
-
- case TokenKind::FunctionKeyword:
-
- case TokenKind::AttributeToken:
- return true;
- }
- return false;
- }
-
- private function parseInterfaceElementFn() {
- return function ($parentNode) {
- $modifiers = $this->parseModifiers();
-
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- case TokenKind::ConstKeyword:
- return $this->parseClassConstDeclaration($parentNode, $modifiers);
-
- case TokenKind::FunctionKeyword:
- return $this->parseMethodDeclaration($parentNode, $modifiers);
-
- case TokenKind::AttributeToken:
- return $this->parseAttributeStatement($parentNode);
-
- default:
- $missingInterfaceMemberDeclaration = new MissingMemberDeclaration();
- $missingInterfaceMemberDeclaration->parent = $parentNode;
- $missingInterfaceMemberDeclaration->modifiers = $modifiers;
- return $missingInterfaceMemberDeclaration;
- }
- };
- }
-
- private function parseInterfaceBaseClause($parentNode) {
- $interfaceBaseClause = new InterfaceBaseClause();
- $interfaceBaseClause->parent = $parentNode;
-
- $interfaceBaseClause->extendsKeyword = $this->eatOptional1(TokenKind::ExtendsKeyword);
- if (isset($interfaceBaseClause->extendsKeyword)) {
- $interfaceBaseClause->interfaceNameList = $this->parseQualifiedNameList($interfaceBaseClause);
- } else {
- return null;
- }
-
- return $interfaceBaseClause;
- }
-
- private function parseNamespaceDefinition($parentNode) {
- $namespaceDefinition = new NamespaceDefinition();
- $namespaceDefinition->parent = $parentNode;
-
- $namespaceDefinition->namespaceKeyword = $this->eat1(TokenKind::NamespaceKeyword);
-
- if (!$this->checkToken(TokenKind::NamespaceKeyword)) {
- $namespaceDefinition->name = $this->parseQualifiedName($namespaceDefinition); // TODO only optional with compound statement block
- }
-
- $namespaceDefinition->compoundStatementOrSemicolon =
- $this->checkToken(TokenKind::OpenBraceToken) ?
- $this->parseCompoundStatement($namespaceDefinition) : $this->eatSemicolonOrAbortStatement();
-
- return $namespaceDefinition;
- }
-
- private function parseNamespaceUseDeclaration($parentNode) {
- $namespaceUseDeclaration = new NamespaceUseDeclaration();
- $namespaceUseDeclaration->parent = $parentNode;
- $namespaceUseDeclaration->useKeyword = $this->eat1(TokenKind::UseKeyword);
- $namespaceUseDeclaration->functionOrConst = $this->eatOptional(TokenKind::FunctionKeyword, TokenKind::ConstKeyword);
- $namespaceUseDeclaration->useClauses = $this->parseNamespaceUseClauseList($namespaceUseDeclaration);
- $namespaceUseDeclaration->semicolon = $this->eatSemicolonOrAbortStatement();
- return $namespaceUseDeclaration;
- }
-
- private function parseNamespaceUseClauseList($parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\NamespaceUseClauseList::class,
- TokenKind::CommaToken,
- function ($token) {
- return $this->isQualifiedNameStart($token) || $token->kind === TokenKind::FunctionKeyword || $token->kind === TokenKind::ConstKeyword;
- },
- function ($parentNode) {
- $namespaceUseClause = new NamespaceUseClause();
- $namespaceUseClause->parent = $parentNode;
- $namespaceUseClause->namespaceName = $this->parseQualifiedName($namespaceUseClause);
- if ($this->checkToken(TokenKind::AsKeyword)) {
- $namespaceUseClause->namespaceAliasingClause = $this->parseNamespaceAliasingClause($namespaceUseClause);
- }
- elseif ($this->checkToken(TokenKind::OpenBraceToken)) {
- $namespaceUseClause->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $namespaceUseClause->groupClauses = $this->parseNamespaceUseGroupClauseList($namespaceUseClause);
- $namespaceUseClause->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- }
-
- return $namespaceUseClause;
- },
- $parentNode
- );
- }
-
- private function parseNamespaceUseGroupClauseList($parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\NamespaceUseGroupClauseList::class,
- TokenKind::CommaToken,
- function ($token) {
- return $this->isQualifiedNameStart($token) || $token->kind === TokenKind::FunctionKeyword || $token->kind === TokenKind::ConstKeyword;
- },
- function ($parentNode) {
- $namespaceUseGroupClause = new NamespaceUseGroupClause();
- $namespaceUseGroupClause->parent = $parentNode;
-
- $namespaceUseGroupClause->functionOrConst = $this->eatOptional(TokenKind::FunctionKeyword, TokenKind::ConstKeyword);
- $namespaceUseGroupClause->namespaceName = $this->parseQualifiedName($namespaceUseGroupClause);
- if ($this->checkToken(TokenKind::AsKeyword)) {
- $namespaceUseGroupClause->namespaceAliasingClause = $this->parseNamespaceAliasingClause($namespaceUseGroupClause);
- }
-
- return $namespaceUseGroupClause;
- },
- $parentNode
- );
- }
-
- private function parseNamespaceAliasingClause($parentNode) {
- $namespaceAliasingClause = new NamespaceAliasingClause();
- $namespaceAliasingClause->parent = $parentNode;
- $namespaceAliasingClause->asKeyword = $this->eat1(TokenKind::AsKeyword);
- $namespaceAliasingClause->name = $this->eat1(TokenKind::Name);
- return $namespaceAliasingClause;
- }
-
- private function parseTraitDeclaration($parentNode) {
- $traitDeclaration = new TraitDeclaration();
- $traitDeclaration->parent = $parentNode;
-
- $traitDeclaration->traitKeyword = $this->eat1(TokenKind::TraitKeyword);
- $traitDeclaration->name = $this->eat1(TokenKind::Name);
-
- $traitDeclaration->traitMembers = $this->parseTraitMembers($traitDeclaration);
-
- return $traitDeclaration;
- }
-
- private function parseTraitMembers($parentNode) {
- $traitMembers = new TraitMembers();
- $traitMembers->parent = $parentNode;
-
- $traitMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
-
- $traitMembers->traitMemberDeclarations = $this->parseList($traitMembers, ParseContext::TraitMembers);
-
- $traitMembers->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
-
- return $traitMembers;
- }
-
- private function isTraitMemberDeclarationStart($token) {
- switch ($token->kind) {
- // property-declaration
- case TokenKind::VariableName:
-
- // modifiers
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
- case TokenKind::VarKeyword:
- case TokenKind::StaticKeyword:
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
- case TokenKind::ReadonlyKeyword:
-
- // method-declaration
- case TokenKind::FunctionKeyword:
-
- // trait-use-clauses
- case TokenKind::UseKeyword:
-
- // attributes
- case TokenKind::AttributeToken:
- return true;
- }
- return false;
- }
-
- private function parseTraitElementFn() {
- return function ($parentNode) {
- $modifiers = $this->parseModifiers();
-
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- case TokenKind::FunctionKeyword:
- return $this->parseMethodDeclaration($parentNode, $modifiers);
-
- case TokenKind::QuestionToken:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration(
- $parentNode,
- $modifiers,
- $this->eat1(TokenKind::QuestionToken)
- );
- case TokenKind::VariableName:
- return $this->parsePropertyDeclaration($parentNode, $modifiers);
-
- case TokenKind::UseKeyword:
- return $this->parseTraitUseClause($parentNode);
-
- case TokenKind::AttributeToken:
- return $this->parseAttributeStatement($parentNode);
-
- default:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration($parentNode, $modifiers);
- }
- };
- }
-
- private function parseEnumDeclaration($parentNode) {
- $enumDeclaration = new EnumDeclaration();
- $enumDeclaration->parent = $parentNode;
-
- $enumDeclaration->enumKeyword = $this->eat1(TokenKind::EnumKeyword);
- $enumDeclaration->name = $this->eat1(TokenKind::Name);
- $enumDeclaration->colonToken = $this->eatOptional1(TokenKind::ColonToken);
- if ($enumDeclaration->colonToken !== null) {
- $enumDeclaration->enumType = $this->tryParseParameterTypeDeclaration($enumDeclaration)
- ?: new MissingToken(TokenKind::EnumType, $this->token->fullStart);
- }
-
- $enumDeclaration->enumMembers = $this->parseEnumMembers($enumDeclaration);
-
- return $enumDeclaration;
- }
-
- private function parseEnumMembers($parentNode) {
- $enumMembers = new EnumMembers();
- $enumMembers->parent = $parentNode;
-
- $enumMembers->openBrace = $this->eat1(TokenKind::OpenBraceToken);
-
- $enumMembers->enumMemberDeclarations = $this->parseList($enumMembers, ParseContext::EnumMembers);
-
- $enumMembers->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
-
- return $enumMembers;
- }
-
- private function isEnumMemberDeclarationStart($token) {
- switch ($token->kind) {
- // modifiers
- case TokenKind::PublicKeyword:
- case TokenKind::ProtectedKeyword:
- case TokenKind::PrivateKeyword:
- case TokenKind::StaticKeyword:
- case TokenKind::AbstractKeyword:
- case TokenKind::FinalKeyword:
-
- // method-declaration
- case TokenKind::FunctionKeyword:
-
- // trait-use-clauses (enums can use traits)
- case TokenKind::UseKeyword:
-
- // cases and constants
- case TokenKind::CaseKeyword:
- case TokenKind::ConstKeyword:
-
- // attributes
- case TokenKind::AttributeToken:
- return true;
- }
- return false;
- }
-
- private function parseEnumElementFn() {
- return function ($parentNode) {
- $modifiers = $this->parseModifiers();
-
- $token = $this->getCurrentToken();
- switch ($token->kind) {
- // TODO: CaseKeyword
- case TokenKind::CaseKeyword:
- return $this->parseEnumCaseDeclaration($parentNode);
-
- case TokenKind::ConstKeyword:
- return $this->parseClassConstDeclaration($parentNode, $modifiers);
-
- case TokenKind::FunctionKeyword:
- return $this->parseMethodDeclaration($parentNode, $modifiers);
-
- case TokenKind::QuestionToken:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration(
- $parentNode,
- $modifiers,
- $this->eat1(TokenKind::QuestionToken)
- );
- case TokenKind::VariableName:
- return $this->parsePropertyDeclaration($parentNode, $modifiers);
-
- case TokenKind::UseKeyword:
- return $this->parseTraitUseClause($parentNode);
-
- case TokenKind::AttributeToken:
- return $this->parseAttributeStatement($parentNode);
-
- default:
- return $this->parseRemainingPropertyDeclarationOrMissingMemberDeclaration($parentNode, $modifiers);
- }
- };
- }
-
-
- /**
- * @param Node $parentNode
- * @param Token[] $modifiers
- * @param Token $questionToken
- * @param DelimitedList\QualifiedNameList|null $typeDeclarationList
- */
- private function makeMissingMemberDeclaration($parentNode, $modifiers, $questionToken = null, $typeDeclarationList = null) {
- $missingTraitMemberDeclaration = new MissingMemberDeclaration();
- $missingTraitMemberDeclaration->parent = $parentNode;
- $missingTraitMemberDeclaration->modifiers = $modifiers;
- $missingTraitMemberDeclaration->questionToken = $questionToken;
- if ($typeDeclarationList) {
- $missingTraitMemberDeclaration->typeDeclarationList = $typeDeclarationList;
- $missingTraitMemberDeclaration->typeDeclarationList->parent = $missingTraitMemberDeclaration;
- } elseif ($questionToken) {
- $missingTraitMemberDeclaration->typeDeclarationList = new MissingToken(TokenKind::PropertyType, $this->token->fullStart);
- }
- return $missingTraitMemberDeclaration;
- }
-
- private function parseTraitUseClause($parentNode) {
- $traitUseClause = new TraitUseClause();
- $traitUseClause->parent = $parentNode;
-
- $traitUseClause->useKeyword = $this->eat1(TokenKind::UseKeyword);
- $traitUseClause->traitNameList = $this->parseQualifiedNameList($traitUseClause);
-
- $traitUseClause->semicolonOrOpenBrace = $this->eat(TokenKind::OpenBraceToken, TokenKind::SemicolonToken);
- if ($traitUseClause->semicolonOrOpenBrace->kind === TokenKind::OpenBraceToken) {
- $traitUseClause->traitSelectAndAliasClauses = $this->parseTraitSelectAndAliasClauseList($traitUseClause);
- $traitUseClause->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- }
-
- return $traitUseClause;
- }
-
- private function parseTraitSelectAndAliasClauseList($parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\TraitSelectOrAliasClauseList::class,
- TokenKind::SemicolonToken,
- $this->isQualifiedNameStartFn(),
- $this->parseTraitSelectOrAliasClauseFn(),
- $parentNode
- );
- }
-
- private function parseTraitSelectOrAliasClauseFn() {
- return function ($parentNode) {
- $traitSelectAndAliasClause = new TraitSelectOrAliasClause();
- $traitSelectAndAliasClause->parent = $parentNode;
- $traitSelectAndAliasClause->name = // TODO update spec
- $this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
-
- $traitSelectAndAliasClause->asOrInsteadOfKeyword = $this->eat(TokenKind::AsKeyword, TokenKind::InsteadOfKeyword);
- $traitSelectAndAliasClause->modifiers = $this->parseModifiers(); // TODO accept all modifiers, verify later
-
- if ($traitSelectAndAliasClause->asOrInsteadOfKeyword->kind === TokenKind::InsteadOfKeyword) {
- $traitSelectAndAliasClause->targetNameList = $this->parseQualifiedNameList($traitSelectAndAliasClause);
- } else {
- $traitSelectAndAliasClause->targetNameList =
- $this->parseQualifiedNameOrScopedPropertyAccessExpression($traitSelectAndAliasClause);
- }
-
- // TODO errors for insteadof/as
- return $traitSelectAndAliasClause;
- };
- }
-
- private function parseQualifiedNameOrScopedPropertyAccessExpression($parentNode) {
- $qualifiedNameOrScopedProperty = $this->parseQualifiedName($parentNode);
- if ($this->getCurrentToken()->kind === TokenKind::ColonColonToken) {
- $qualifiedNameOrScopedProperty = $this->parseScopedPropertyAccessExpression($qualifiedNameOrScopedProperty, $parentNode);
- }
- return $qualifiedNameOrScopedProperty;
- }
-
- private function parseGlobalDeclaration($parentNode) {
- $globalDeclaration = new GlobalDeclaration();
- $globalDeclaration->parent = $parentNode;
-
- $globalDeclaration->globalKeyword = $this->eat1(TokenKind::GlobalKeyword);
- $globalDeclaration->variableNameList = $this->parseDelimitedList(
- DelimitedList\VariableNameList::class,
- TokenKind::CommaToken,
- $this->isVariableNameStartFn(),
- $this->parseSimpleVariableFn(),
- $globalDeclaration
- );
-
- $globalDeclaration->semicolon = $this->eatSemicolonOrAbortStatement();
-
- return $globalDeclaration;
- }
-
- private function parseFunctionStaticDeclaration($parentNode) {
- $functionStaticDeclaration = new FunctionStaticDeclaration();
- $functionStaticDeclaration->parent = $parentNode;
-
- $functionStaticDeclaration->staticKeyword = $this->eat1(TokenKind::StaticKeyword);
- $functionStaticDeclaration->staticVariableNameList = $this->parseDelimitedList(
- DelimitedList\StaticVariableNameList::class,
- TokenKind::CommaToken,
- function ($token) {
- return $token->kind === TokenKind::VariableName;
- },
- $this->parseStaticVariableDeclarationFn(),
- $functionStaticDeclaration
- );
- $functionStaticDeclaration->semicolon = $this->eatSemicolonOrAbortStatement();
-
- return $functionStaticDeclaration;
- }
-
- private function isVariableNameStartFn() {
- return function ($token) {
- return $token->kind === TokenKind::VariableName || $token->kind === TokenKind::DollarToken;
- };
- }
-
- private function parseStaticVariableDeclarationFn() {
- return function ($parentNode) {
- $staticVariableDeclaration = new StaticVariableDeclaration();
- $staticVariableDeclaration->parent = $parentNode;
- $staticVariableDeclaration->variableName = $this->eat1(TokenKind::VariableName);
- $staticVariableDeclaration->equalsToken = $this->eatOptional1(TokenKind::EqualsToken);
- if ($staticVariableDeclaration->equalsToken !== null) {
- // TODO add post-parse rule that checks for invalid assignments
- $staticVariableDeclaration->assignment = $this->parseExpression($staticVariableDeclaration);
- }
- return $staticVariableDeclaration;
- };
- }
-
- private function parseConstDeclaration($parentNode) {
- $constDeclaration = new ConstDeclaration();
- $constDeclaration->parent = $parentNode;
-
- $constDeclaration->constKeyword = $this->eat1(TokenKind::ConstKeyword);
- $constDeclaration->constElements = $this->parseConstElements($constDeclaration);
- $constDeclaration->semicolon = $this->eatSemicolonOrAbortStatement();
-
- return $constDeclaration;
- }
-
- private function parseConstElements($parentNode) {
- return $this->parseDelimitedList(
- DelimitedList\ConstElementList::class,
- TokenKind::CommaToken,
- function ($token) {
- return \in_array($token->kind, $this->nameOrKeywordOrReservedWordTokens);
- },
- $this->parseConstElementFn(),
- $parentNode
- );
- }
-
- private function parseConstElementFn() {
- return function ($parentNode) {
- $constElement = new ConstElement();
- $constElement->parent = $parentNode;
- $constElement->name = $this->getCurrentToken();
- $this->advanceToken();
- $constElement->name->kind = TokenKind::Name; // to support keyword names
- $constElement->equalsToken = $this->eat1(TokenKind::EqualsToken);
- // TODO add post-parse rule that checks for invalid assignments
- $constElement->assignment = $this->parseExpression($constElement);
- return $constElement;
- };
- }
-
- private function parseCastExpression($parentNode) {
- $castExpression = new CastExpression();
- $castExpression->parent = $parentNode;
- $castExpression->castType = $this->eat(
- TokenKind::ArrayCastToken,
- TokenKind::BoolCastToken,
- TokenKind::DoubleCastToken,
- TokenKind::IntCastToken,
- TokenKind::ObjectCastToken,
- TokenKind::StringCastToken,
- TokenKind::UnsetCastToken
- );
-
- $castExpression->operand = $this->parseUnaryExpressionOrHigher($castExpression);
-
- return $castExpression;
- }
-
- private function parseCastExpressionGranular($parentNode) {
- $castExpression = new CastExpression();
- $castExpression->parent = $parentNode;
-
- $castExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $castExpression->castType = $this->eat(
- TokenKind::ArrayKeyword,
- TokenKind::BinaryReservedWord,
- TokenKind::BoolReservedWord,
- TokenKind::BooleanReservedWord,
- TokenKind::DoubleReservedWord,
- TokenKind::IntReservedWord,
- TokenKind::IntegerReservedWord,
- TokenKind::FloatReservedWord,
- TokenKind::ObjectReservedWord,
- TokenKind::RealReservedWord,
- TokenKind::StringReservedWord,
- TokenKind::UnsetKeyword
- );
- $castExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $castExpression->operand = $this->parseUnaryExpressionOrHigher($castExpression);
-
- return $castExpression;
- }
-
- private function parseAnonymousFunctionCreationExpression($parentNode) {
- $staticModifier = $this->eatOptional1(TokenKind::StaticKeyword);
- if ($this->getCurrentToken()->kind === TokenKind::FnKeyword) {
- return $this->parseArrowFunctionCreationExpression($parentNode, $staticModifier);
- }
- $anonymousFunctionCreationExpression = new AnonymousFunctionCreationExpression();
- $anonymousFunctionCreationExpression->parent = $parentNode;
-
- $anonymousFunctionCreationExpression->staticModifier = $staticModifier;
- $this->parseFunctionType($anonymousFunctionCreationExpression, false, true);
-
- return $anonymousFunctionCreationExpression;
- }
-
- private function parseArrowFunctionCreationExpression($parentNode, $staticModifier) : ArrowFunctionCreationExpression {
- $arrowFunction = new ArrowFunctionCreationExpression();
- $arrowFunction->parent = $parentNode;
- $arrowFunction->staticModifier = $staticModifier;
-
- $arrowFunction->functionKeyword = $this->eat1(TokenKind::FnKeyword);
- $arrowFunction->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
- $arrowFunction->name = $this->eatOptional($this->nameOrKeywordOrReservedWordTokens);
-
- if (isset($arrowFunction->name)) {
- // Anonymous functions should not have names.
- // This is based on the code for AnonymousFunctionCreationExpression.
- $arrowFunction->name->kind = TokenKind::Name;
- $arrowFunction->name = new SkippedToken($arrowFunction->name); // TODO instead handle this during post-walk
- }
-
- $arrowFunction->openParen = $this->eat1(TokenKind::OpenParenToken);
- $arrowFunction->parameters = $this->parseDelimitedList(
- DelimitedList\ParameterDeclarationList::class,
- TokenKind::CommaToken,
- $this->isParameterStartFn(),
- $this->parseParameterFn(),
- $arrowFunction);
- $arrowFunction->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- if ($this->checkToken(TokenKind::ColonToken)) {
- $arrowFunction->colonToken = $this->eat1(TokenKind::ColonToken);
- $arrowFunction->questionToken = $this->eatOptional1(TokenKind::QuestionToken);
- $this->parseAndSetReturnTypeDeclarationList($arrowFunction);
- }
-
- $arrowFunction->arrowToken = $this->eat1(TokenKind::DoubleArrowToken);
- $arrowFunction->resultExpression = $this->parseExpression($arrowFunction);
-
- return $arrowFunction;
- }
-
- private function parseAnonymousFunctionUseClause($parentNode) {
- $anonymousFunctionUseClause = new AnonymousFunctionUseClause();
- $anonymousFunctionUseClause->parent = $parentNode;
-
- $anonymousFunctionUseClause->useKeyword = $this->eatOptional1(TokenKind::UseKeyword);
- if ($anonymousFunctionUseClause->useKeyword === null) {
- return null;
- }
- $anonymousFunctionUseClause->openParen = $this->eat1(TokenKind::OpenParenToken);
- $anonymousFunctionUseClause->useVariableNameList = $this->parseDelimitedList(
- DelimitedList\UseVariableNameList::class,
- TokenKind::CommaToken,
- function ($token) {
- return $token->kind === TokenKind::AmpersandToken || $token->kind === TokenKind::VariableName;
- },
- function ($parentNode) {
- $useVariableName = new UseVariableName();
- $useVariableName->parent = $parentNode;
- $useVariableName->byRef = $this->eatOptional1(TokenKind::AmpersandToken);
- $useVariableName->variableName = $this->eat1(TokenKind::VariableName);
- return $useVariableName;
- },
- $anonymousFunctionUseClause
- ) ?: (new MissingToken(TokenKind::VariableName, $this->token->fullStart));
- $anonymousFunctionUseClause->closeParen = $this->eat1(TokenKind::CloseParenToken);
-
- return $anonymousFunctionUseClause;
- }
-
- private function parseMatchExpression($parentNode) {
- $matchExpression = new MatchExpression();
- $matchExpression->parent = $parentNode;
- $matchExpression->matchToken = $this->eat1(TokenKind::MatchKeyword);
- $matchExpression->openParen = $this->eat1(TokenKind::OpenParenToken);
- $matchExpression->expression = $this->parseExpression($matchExpression);
- $matchExpression->closeParen = $this->eat1(TokenKind::CloseParenToken);
- $matchExpression->openBrace = $this->eat1(TokenKind::OpenBraceToken);
- $matchExpression->arms = $this->parseDelimitedList(
- DelimitedList\MatchExpressionArmList::class,
- TokenKind::CommaToken,
- $this->isMatchConditionStartFn(),
- $this->parseMatchArmFn(),
- $matchExpression);
- $matchExpression->closeBrace = $this->eat1(TokenKind::CloseBraceToken);
- return $matchExpression;
- }
-
- private function isMatchConditionStartFn() {
- return function ($token) {
- return $token->kind === TokenKind::DefaultKeyword ||
- $this->isExpressionStart($token);
- };
- }
-
- private function parseMatchArmFn() {
- return function ($parentNode) {
- $matchArm = new MatchArm();
- $matchArm->parent = $parentNode;
- $matchArmConditionList = $this->parseDelimitedList(
- DelimitedList\MatchArmConditionList::class,
- TokenKind::CommaToken,
- $this->isMatchConditionStartFn(),
- $this->parseMatchConditionFn(),
- $matchArm
- );
- $matchArmConditionList->parent = $matchArm;
- $matchArm->conditionList = $matchArmConditionList;
- $matchArm->arrowToken = $this->eat1(TokenKind::DoubleArrowToken);
- $matchArm->body = $this->parseExpression($matchArm);
- return $matchArm;
- };
- }
-
- private function parseMatchConditionFn() {
- return function ($parentNode) {
- if ($this->token->kind === TokenKind::DefaultKeyword) {
- return $this->eat1(TokenKind::DefaultKeyword);
- }
- return $this->parseExpression($parentNode);
- };
- }
-
- private function parseCloneExpression($parentNode) {
- $cloneExpression = new CloneExpression();
- $cloneExpression->parent = $parentNode;
-
- $cloneExpression->cloneKeyword = $this->eat1(TokenKind::CloneKeyword);
- $cloneExpression->expression = $this->parseUnaryExpressionOrHigher($cloneExpression);
-
- return $cloneExpression;
- }
-
- private function eatSemicolonOrAbortStatement() {
- if ($this->getCurrentToken()->kind !== TokenKind::ScriptSectionEndTag) {
- return $this->eat1(TokenKind::SemicolonToken);
- }
- return null;
- }
-
- private function parseInlineHtml($parentNode) {
- $inlineHtml = new InlineHtml();
- $inlineHtml->parent = $parentNode;
- $inlineHtml->scriptSectionEndTag = $this->eatOptional1(TokenKind::ScriptSectionEndTag);
- $inlineHtml->text = $this->eatOptional1(TokenKind::InlineHtml);
- $inlineHtml->scriptSectionStartTag = $this->eatOptional(TokenKind::ScriptSectionStartTag, TokenKind::ScriptSectionStartWithEchoTag);
-
- // This is the easiest way to represent `= "expr", "other" `
- if (($inlineHtml->scriptSectionStartTag->kind ?? null) === TokenKind::ScriptSectionStartWithEchoTag) {
- $echoStatement = new EchoStatement();
- $expressionList = $this->parseExpressionList($echoStatement) ?? (new MissingToken(TokenKind::Expression, $this->token->fullStart));
- $echoStatement->expressions = $expressionList;
-
- $echoStatement->semicolon = $this->eatSemicolonOrAbortStatement();
- $echoStatement->parent = $inlineHtml;
- // Deliberately leave echoKeyword as null instead of MissingToken
-
- $inlineHtml->echoStatement = $echoStatement;
- }
-
- return $inlineHtml;
- }
-}
-
-class Associativity {
- const None = 0;
- const Left = 1;
- const Right = 2;
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/PhpTokenizer.php b/vendor/microsoft/tolerant-php-parser/src/PhpTokenizer.php
deleted file mode 100644
index be8cd711..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/PhpTokenizer.php
+++ /dev/null
@@ -1,413 +0,0 @@
-tokensArray = $this->getTokensArrayFromContent($content);
- $this->endOfFilePos = \count($this->tokensArray) - 1;
- $this->pos = 0;
- }
-
- public function scanNextToken() : Token {
- return $this->pos >= $this->endOfFilePos
- ? $this->tokensArray[$this->endOfFilePos]
- : $this->tokensArray[$this->pos++];
- }
-
- public function getCurrentPosition() : int {
- return $this->pos;
- }
-
- public function setCurrentPosition(int $pos) {
- $this->pos = $pos;
- }
-
- public function getEndOfFilePosition() : int {
- return $this->endOfFilePos;
- }
-
- public function getTokensArray() : array {
- return $this->tokensArray;
- }
-
- /**
- * Return an array of Token object instances created from $content.
- *
- * This method is optimized heavily - this processes every single token being created.
- *
- * @param string $content the raw php code
- * @param ?int $parseContext can be SourceElements when extracting doc comments
- * @param int $initialPos
- * @param bool $treatCommentsAsTrivia
- * @return Token[]
- */
- public static function getTokensArrayFromContent(
- $content, $parseContext = null, $initialPos = 0, $treatCommentsAsTrivia = true
- ) : array {
- if ($parseContext !== null) {
- // If needed, add a prefix so that token_get_all will tokenize the remaining $contents
- $prefix = self::PARSE_CONTEXT_TO_PREFIX[$parseContext];
- $content = $prefix . $content;
- }
-
- $tokens = static::tokenGetAll($content, $parseContext);
-
- $arr = [];
- $fullStart = $start = $pos = $initialPos;
- if ($parseContext !== null) {
- // If needed, skip over the prefix we added for token_get_all and remove those tokens.
- // This was moved out of the below main loop as an optimization.
- // (the common case of parsing an entire file uses a null parseContext)
- foreach ($tokens as $i => $token) {
- unset($tokens[$i]);
- if (\is_array($token)) {
- $pos += \strlen($token[1]);
- } else {
- $pos += \strlen($token);
- }
- if (\strlen($prefix) < $pos) {
- $fullStart = $start = $pos = $initialPos;
- break;
- }
- }
- }
-
- // Convert tokens from token_get_all to Token instances,
- // skipping whitespace and (usually, when parseContext is null) comments.
- foreach ($tokens as $token) {
- if (\is_array($token)) {
- $tokenKind = $token[0];
- $strlen = \strlen($token[1]);
- } else {
- $pos += \strlen($token);
- $newTokenKind = self::TOKEN_MAP[$token] ?? TokenKind::Unknown;
- $arr[] = new Token($newTokenKind, $fullStart, $start, $pos - $fullStart);
- $start = $fullStart = $pos;
- continue;
- }
-
- $pos += $strlen;
-
- // Optimization note: In PHP < 7.2, the switch statement would check case by case,
- // so putting the most common cases first is slightly faster
- switch ($tokenKind) {
- case \T_WHITESPACE:
- $start += $strlen;
- break;
- case \T_STRING:
- $name = \strtolower($token[1]);
- $newTokenKind = TokenStringMaps::RESERVED_WORDS[$name] ?? TokenKind::Name;
- $arr[] = new Token($newTokenKind, $fullStart, $start, $pos - $fullStart);
- $start = $fullStart = $pos;
- break;
- case \T_OPEN_TAG:
- $arr[] = new Token(TokenKind::ScriptSectionStartTag, $fullStart, $start, $pos-$fullStart);
- $start = $fullStart = $pos;
- break;
- case \PHP_VERSION_ID >= 80000 ? \T_NAME_QUALIFIED : -1000:
- case \PHP_VERSION_ID >= 80000 ? \T_NAME_FULLY_QUALIFIED : -1001:
- // NOTE: This switch is called on every token of every file being parsed, so this traded performance for readability.
- //
- // PHP's Opcache is able to optimize switches that are exclusively known longs,
- // but not switches that mix strings and longs or have unknown longs.
- // Longs are only known if they're declared within the same *class* or an internal constant (tokenizer).
- //
- // For some reason, the SWITCH_LONG opcode was not generated when the expression was part of a class constant.
- // (seen with php -d opcache.opt_debug_level=0x20000)
- //
- // Use negative values because that's not expected to overlap with token kinds that token_get_all() will return.
- //
- // T_NAME_* was added in php 8.0 to forbid whitespace between parts of names.
- // Here, emulate the tokenization of php 7 by splitting it up into 1 or more tokens.
- foreach (\explode('\\', $token[1]) as $i => $name) {
- if ($i) {
- $arr[] = new Token(TokenKind::BackslashToken, $fullStart, $start, 1 + $start - $fullStart);
- $start++;
- $fullStart = $start;
- }
- if ($name === '') {
- continue;
- }
- // TODO: TokenStringMaps::RESERVED_WORDS[$name] ?? TokenKind::Name for compatibility?
- $len = \strlen($name);
- $arr[] = new Token(TokenKind::Name, $fullStart, $start, $len + $start - $fullStart);
- $start += $len;
- $fullStart = $start;
- }
- break;
- case \PHP_VERSION_ID >= 80000 ? \T_NAME_RELATIVE : -1002:
- // This is a namespace-relative name: namespace\...
- foreach (\explode('\\', $token[1]) as $i => $name) {
- $len = \strlen($name);
- if (!$i) {
- $arr[] = new Token(TokenKind::NamespaceKeyword, $fullStart, $start, $len + $start - $fullStart);
- $start += $len;
- $fullStart = $start;
- continue;
- }
- $arr[] = new Token(TokenKind::BackslashToken, $fullStart, $start, 1);
- $start++;
-
- // TODO: TokenStringMaps::RESERVED_WORDS[$name] ?? TokenKind::Name for compatibility?
- $arr[] = new Token(TokenKind::Name, $start, $start, $len);
-
- $start += $len;
- $fullStart = $start;
- }
- break;
- case \T_COMMENT:
- case \T_DOC_COMMENT:
- if ($treatCommentsAsTrivia) {
- $start += $strlen;
- break;
- }
- // fall through
- default:
- $newTokenKind = self::TOKEN_MAP[$tokenKind] ?? TokenKind::Unknown;
- $arr[] = new Token($newTokenKind, $fullStart, $start, $pos - $fullStart);
- $start = $fullStart = $pos;
- break;
- }
- }
-
- $arr[] = new Token(TokenKind::EndOfFileToken, $fullStart, $start, $pos - $fullStart);
- return $arr;
- }
-
- /**
- * @param string $content the raw php code
- * @param ?int $parseContext can be SourceElements when extracting doc comments.
- * Having this available may be useful for subclasses to decide whether or not to post-process results, cache results, etc.
- * @return array[]|string[] an array of tokens. When concatenated, these tokens must equal $content.
- *
- * This exists so that it can be overridden in subclasses, e.g. to cache the result of tokenizing entire files.
- * Applications using tolerant-php-parser may often end up needing to use the token stream for other reasons that are hard to do in the resulting AST,
- * such as iterating over T_COMMENTS, checking for inline html,
- * looking up all tokens (including skipped tokens) on a given line, etc.
- */
- protected static function tokenGetAll(string $content, $parseContext): array
- {
- return @\token_get_all($content);
- }
-
- const TOKEN_MAP = [
- T_CLASS_C => TokenKind::Name,
- T_DIR => TokenKind::Name,
- T_FILE => TokenKind::Name,
- T_FUNC_C => TokenKind::Name,
- T_HALT_COMPILER => TokenKind::Name,
- T_METHOD_C => TokenKind::Name,
- T_NS_C => TokenKind::Name,
- T_TRAIT_C => TokenKind::Name,
- T_LINE => TokenKind::Name,
-
- T_STRING => TokenKind::Name,
- T_VARIABLE => TokenKind::VariableName,
-
- T_ABSTRACT => TokenKind::AbstractKeyword,
- T_LOGICAL_AND => TokenKind::AndKeyword,
- T_ARRAY => TokenKind::ArrayKeyword,
- T_AS => TokenKind::AsKeyword,
- T_BREAK => TokenKind::BreakKeyword,
- T_CALLABLE => TokenKind::CallableKeyword,
- T_CASE => TokenKind::CaseKeyword,
- T_CATCH => TokenKind::CatchKeyword,
- T_CLASS => TokenKind::ClassKeyword,
- T_CLONE => TokenKind::CloneKeyword,
- T_CONST => TokenKind::ConstKeyword,
- T_CONTINUE => TokenKind::ContinueKeyword,
- T_DECLARE => TokenKind::DeclareKeyword,
- T_DEFAULT => TokenKind::DefaultKeyword,
- T_DO => TokenKind::DoKeyword,
- T_ECHO => TokenKind::EchoKeyword,
- T_ELSE => TokenKind::ElseKeyword,
- T_ELSEIF => TokenKind::ElseIfKeyword,
- T_EMPTY => TokenKind::EmptyKeyword,
- T_ENDDECLARE => TokenKind::EndDeclareKeyword,
- T_ENDFOR => TokenKind::EndForKeyword,
- T_ENDFOREACH => TokenKind::EndForEachKeyword,
- T_ENDIF => TokenKind::EndIfKeyword,
- T_ENDSWITCH => TokenKind::EndSwitchKeyword,
- T_ENDWHILE => TokenKind::EndWhileKeyword,
- T_ENUM => TokenKind::EnumKeyword,
- T_EVAL => TokenKind::EvalKeyword,
- T_EXIT => TokenKind::ExitKeyword,
- T_EXTENDS => TokenKind::ExtendsKeyword,
- T_FINAL => TokenKind::FinalKeyword,
- T_FINALLY => TokenKind::FinallyKeyword,
- T_FOR => TokenKind::ForKeyword,
- T_FOREACH => TokenKind::ForeachKeyword,
- T_FN => TokenKind::FnKeyword,
- T_FUNCTION => TokenKind::FunctionKeyword,
- T_GLOBAL => TokenKind::GlobalKeyword,
- T_GOTO => TokenKind::GotoKeyword,
- T_IF => TokenKind::IfKeyword,
- T_IMPLEMENTS => TokenKind::ImplementsKeyword,
- T_INCLUDE => TokenKind::IncludeKeyword,
- T_INCLUDE_ONCE => TokenKind::IncludeOnceKeyword,
- T_INSTANCEOF => TokenKind::InstanceOfKeyword,
- T_INSTEADOF => TokenKind::InsteadOfKeyword,
- T_INTERFACE => TokenKind::InterfaceKeyword,
- T_ISSET => TokenKind::IsSetKeyword,
- T_LIST => TokenKind::ListKeyword,
- T_MATCH => TokenKind::MatchKeyword,
- T_NAMESPACE => TokenKind::NamespaceKeyword,
- T_NEW => TokenKind::NewKeyword,
- T_LOGICAL_OR => TokenKind::OrKeyword,
- T_PRINT => TokenKind::PrintKeyword,
- T_PRIVATE => TokenKind::PrivateKeyword,
- T_PROTECTED => TokenKind::ProtectedKeyword,
- T_PUBLIC => TokenKind::PublicKeyword,
- T_READONLY => TokenKind::ReadonlyKeyword,
- T_REQUIRE => TokenKind::RequireKeyword,
- T_REQUIRE_ONCE => TokenKind::RequireOnceKeyword,
- T_RETURN => TokenKind::ReturnKeyword,
- T_STATIC => TokenKind::StaticKeyword,
- T_SWITCH => TokenKind::SwitchKeyword,
- T_THROW => TokenKind::ThrowKeyword,
- T_TRAIT => TokenKind::TraitKeyword,
- T_TRY => TokenKind::TryKeyword,
- T_UNSET => TokenKind::UnsetKeyword,
- T_USE => TokenKind::UseKeyword,
- T_VAR => TokenKind::VarKeyword,
- T_WHILE => TokenKind::WhileKeyword,
- T_LOGICAL_XOR => TokenKind::XorKeyword,
- T_YIELD => TokenKind::YieldKeyword,
- T_YIELD_FROM => TokenKind::YieldFromKeyword,
-
- "[" => TokenKind::OpenBracketToken,
- "]" => TokenKind::CloseBracketToken,
- "(" => TokenKind::OpenParenToken,
- ")" => TokenKind::CloseParenToken,
- "{" => TokenKind::OpenBraceToken,
- "}" => TokenKind::CloseBraceToken,
- "." => TokenKind::DotToken,
- T_OBJECT_OPERATOR => TokenKind::ArrowToken,
- T_NULLSAFE_OBJECT_OPERATOR => TokenKind::QuestionArrowToken,
- T_ATTRIBUTE => TokenKind::AttributeToken,
- T_INC => TokenKind::PlusPlusToken,
- T_DEC => TokenKind::MinusMinusToken,
- T_POW => TokenKind::AsteriskAsteriskToken,
- "*" => TokenKind::AsteriskToken,
- "+" => TokenKind::PlusToken,
- "-" => TokenKind::MinusToken,
- "~" => TokenKind::TildeToken,
- "!" => TokenKind::ExclamationToken,
- "$" => TokenKind::DollarToken,
- "/" => TokenKind::SlashToken,
- "%" => TokenKind::PercentToken,
- T_SL => TokenKind::LessThanLessThanToken,
- T_SR => TokenKind::GreaterThanGreaterThanToken,
- "<" => TokenKind::LessThanToken,
- ">" => TokenKind::GreaterThanToken,
- T_IS_SMALLER_OR_EQUAL => TokenKind::LessThanEqualsToken,
- T_IS_GREATER_OR_EQUAL => TokenKind::GreaterThanEqualsToken,
- T_IS_EQUAL => TokenKind::EqualsEqualsToken,
- T_IS_IDENTICAL => TokenKind::EqualsEqualsEqualsToken,
- T_IS_NOT_EQUAL => TokenKind::ExclamationEqualsToken,
- T_IS_NOT_IDENTICAL => TokenKind::ExclamationEqualsEqualsToken,
- "^" => TokenKind::CaretToken,
- "|" => TokenKind::BarToken,
- "&" => TokenKind::AmpersandToken,
- T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG => TokenKind::AmpersandToken,
- T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG => TokenKind::AmpersandToken,
- T_BOOLEAN_AND => TokenKind::AmpersandAmpersandToken,
- T_BOOLEAN_OR => TokenKind::BarBarToken,
- ":" => TokenKind::ColonToken,
- ";" => TokenKind::SemicolonToken,
- "=" => TokenKind::EqualsToken,
- T_POW_EQUAL => TokenKind::AsteriskAsteriskEqualsToken,
- T_MUL_EQUAL => TokenKind::AsteriskEqualsToken,
- T_DIV_EQUAL => TokenKind::SlashEqualsToken,
- T_MOD_EQUAL => TokenKind::PercentEqualsToken,
- T_PLUS_EQUAL => TokenKind::PlusEqualsToken,
- T_MINUS_EQUAL => TokenKind::MinusEqualsToken,
- T_CONCAT_EQUAL => TokenKind::DotEqualsToken,
- T_SL_EQUAL => TokenKind::LessThanLessThanEqualsToken,
- T_SR_EQUAL => TokenKind::GreaterThanGreaterThanEqualsToken,
- T_AND_EQUAL => TokenKind::AmpersandEqualsToken,
- T_XOR_EQUAL => TokenKind::CaretEqualsToken,
- T_OR_EQUAL => TokenKind::BarEqualsToken,
- "," => TokenKind::CommaToken,
- namespace\T_COALESCE_EQUAL => TokenKind::QuestionQuestionEqualsToken,
- T_COALESCE => TokenKind::QuestionQuestionToken,
- T_SPACESHIP => TokenKind::LessThanEqualsGreaterThanToken,
- T_ELLIPSIS => TokenKind::DotDotDotToken,
- T_NS_SEPARATOR => TokenKind::BackslashToken,
- T_PAAMAYIM_NEKUDOTAYIM => TokenKind::ColonColonToken,
- T_DOUBLE_ARROW => TokenKind::DoubleArrowToken, // TODO missing from spec
-
- "@" => TokenKind::AtSymbolToken,
- "`" => TokenKind::BacktickToken,
- "?" => TokenKind::QuestionToken,
-
- T_LNUMBER => TokenKind::IntegerLiteralToken,
-
- T_DNUMBER => TokenKind::FloatingLiteralToken,
-
- T_OPEN_TAG => TokenKind::ScriptSectionStartTag,
- T_OPEN_TAG_WITH_ECHO => TokenKind::ScriptSectionStartWithEchoTag,
- T_CLOSE_TAG => TokenKind::ScriptSectionEndTag,
-
- T_INLINE_HTML => TokenKind::InlineHtml,
-
- "\"" => TokenKind::DoubleQuoteToken,
- "'" => TokenKind::SingleQuoteToken,
- T_ENCAPSED_AND_WHITESPACE => TokenKind::EncapsedAndWhitespace,
- T_DOLLAR_OPEN_CURLY_BRACES => TokenKind::DollarOpenBraceToken,
- T_CURLY_OPEN => TokenKind::OpenBraceDollarToken,
- T_CONSTANT_ENCAPSED_STRING => TokenKind::StringLiteralToken,
-
- T_ARRAY_CAST => TokenKind::ArrayCastToken,
- T_BOOL_CAST => TokenKind::BoolCastToken,
- T_DOUBLE_CAST => TokenKind::DoubleCastToken,
- T_INT_CAST => TokenKind::IntCastToken,
- T_OBJECT_CAST => TokenKind::ObjectCastToken,
- T_STRING_CAST => TokenKind::StringCastToken,
- T_UNSET_CAST => TokenKind::UnsetCastToken,
-
- T_START_HEREDOC => TokenKind::HeredocStart,
- T_END_HEREDOC => TokenKind::HeredocEnd,
- T_STRING_VARNAME => TokenKind::StringVarname,
- T_COMMENT => TokenKind::CommentToken,
- T_DOC_COMMENT => TokenKind::DocCommentToken,
- T_NUM_STRING => TokenKind::IntegerLiteralToken
- ];
-
- const PARSE_CONTEXT_TO_PREFIX = [
- ParseContext::SourceElements => "= $textLength) {
- $pos = $textLength;
- } elseif ($pos < 0) {
- $pos = 0;
- }
-
- // Start strrpos check from the character before the current character,
- // in case the current character is a newline
- $startAt = max(-($textLength - $pos) - 1, -$textLength);
- $lastNewlinePos = \strrpos($text, "\n", $startAt);
- $char = $pos - ($lastNewlinePos === false ? 0 : $lastNewlinePos + 1);
- $line = $pos > 0 ? \substr_count($text, "\n", 0, $pos) : 0;
- return new LineCharacterPosition($line, $char);
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Range.php b/vendor/microsoft/tolerant-php-parser/src/Range.php
deleted file mode 100644
index 36acf40c..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Range.php
+++ /dev/null
@@ -1,17 +0,0 @@
-start = $start;
- $this->end = $end;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/ResolvedName.php b/vendor/microsoft/tolerant-php-parser/src/ResolvedName.php
deleted file mode 100644
index 34acf4ff..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/ResolvedName.php
+++ /dev/null
@@ -1,44 +0,0 @@
-kind === TokenKind::Name) {
- $name->parts[] = $token->getText($content);
- }
- }
- return $name;
- }
-
- public function addNameParts(array $parts, $content) {
- foreach ($parts as $part) {
- if ($part->kind === TokenKind::Name && !($part instanceof MissingToken)) {
- $this->parts[] = $part->getText($content);
- }
- }
- }
-
- public function getNameParts() {
- return $this->parts;
- }
-
- public function getFullyQualifiedNameText() : string {
- return join("\\", $this->parts);
- }
-
- public function __toString() {
- return $this->getFullyQualifiedNameText();
- }
-}
\ No newline at end of file
diff --git a/vendor/microsoft/tolerant-php-parser/src/SkippedToken.php b/vendor/microsoft/tolerant-php-parser/src/SkippedToken.php
deleted file mode 100644
index 34594db4..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/SkippedToken.php
+++ /dev/null
@@ -1,23 +0,0 @@
-kind, $token->fullStart, $token->start, $token->length);
- }
-
- #[ReturnTypeWillChange]
- public function jsonSerialize() {
- return array_merge(
- ["error" => $this->getTokenKindNameFromValue(TokenKind::SkippedToken)],
- parent::jsonSerialize()
- );
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/TextEdit.php b/vendor/microsoft/tolerant-php-parser/src/TextEdit.php
deleted file mode 100644
index a967b381..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/TextEdit.php
+++ /dev/null
@@ -1,53 +0,0 @@
-start = $start;
- $this->length = $length;
- $this->content = $content;
- }
-
- /**
- * Applies array of edits to the document, and returns the resulting text.
- * Supplied $edits must not overlap, and be ordered by increasing start position.
- *
- * Note that after applying edits, the original AST should be invalidated.
- *
- * @param TextEdit[] $edits
- * @param string $text
- * @return string
- */
- public static function applyEdits(array $edits, string $text) : string {
- $prevEditStart = PHP_INT_MAX;
- for ($i = \count($edits) - 1; $i >= 0; $i--) {
- $edit = $edits[$i];
- \assert(
- $prevEditStart > $edit->start && $prevEditStart > $edit->start + $edit->length,
- "Supplied TextEdit[] must not overlap, and be in increasing start position order."
- );
- if ($edit->start < 0 || $edit->length < 0 || $edit->start + $edit->length > \strlen($text)) {
- throw new \OutOfBoundsException("Applied TextEdit range out of bounds.");
- }
- $prevEditStart = $edit->start;
- $head = \substr($text, 0, $edit->start);
- $tail = \substr($text, $edit->start + $edit->length);
- $text = $head . $edit->content . $tail;
- }
- return $text;
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/Token.php b/vendor/microsoft/tolerant-php-parser/src/Token.php
deleted file mode 100644
index 6c9daf74..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/Token.php
+++ /dev/null
@@ -1,137 +0,0 @@
-kind = $kind;
- $this->fullStart = $fullStart;
- $this->start = $start;
- $this->length = $length;
- }
-
- public function getLeadingCommentsAndWhitespaceText(string $document) : string {
- return substr($document, $this->fullStart, $this->start - $this->fullStart);
- }
-
- /**
- * @param string|null $document
- * @return bool|null|string
- */
- public function getText(string $document = null) {
- if ($document === null) {
- return null;
- }
- return substr($document, $this->start, $this->length - ($this->start - $this->fullStart));
- }
-
- public function getFullText(string $document) : string {
- return substr($document, $this->fullStart, $this->length);
- }
-
- /**
- * @return int
- */
- public function getStartPosition() {
- return $this->start;
- }
-
- /**
- * @return int
- */
- public function getFullStartPosition() {
- return $this->fullStart;
- }
-
- /**
- * @return int
- */
- public function getWidth() {
- return $this->length + $this->fullStart - $this->start;
- }
-
- /**
- * @return int
- */
- public function getFullWidth() {
- return $this->length;
- }
-
- /**
- * @return int
- */
- public function getEndPosition() {
- return $this->fullStart + $this->length;
- }
-
- /**
- * @return string[] - A hash map of the format [int $tokenKind => string $tokenName]
- */
- private static function getTokenKindNameFromValueMap() {
- static $mapToKindName;
- if ($mapToKindName === null) {
- $constants = (new \ReflectionClass("Microsoft\\PhpParser\\TokenKind"))->getConstants();
- $mapToKindName = \array_flip($constants);
- }
- return $mapToKindName;
- }
-
- /**
- * Returns the token kind name as a string, or the token number if the name
- * was not found.
- *
- * @param int $kind
- * @return int|string
- */
- public static function getTokenKindNameFromValue($kind) {
- $mapToKindName = self::getTokenKindNameFromValueMap();
- return $mapToKindName[$kind] ?? $kind;
- }
-
- #[ReturnTypeWillChange]
- public function jsonSerialize() {
- $kindName = $this->getTokenKindNameFromValue($this->kind);
-
- if (!isset($GLOBALS["SHORT_TOKEN_SERIALIZE"])) {
- $GLOBALS["SHORT_TOKEN_SERIALIZE"] = false;
- }
-
- if ($GLOBALS["SHORT_TOKEN_SERIALIZE"]) {
- return [
- "kind" => $kindName,
- "textLength" => $this->length - ($this->start - $this->fullStart)
- ];
- } else {
- return [
- "kind" => $kindName,
- "fullStart" => $this->fullStart,
- "start" => $this->start,
- "length" => $this->length
- ];
- }
- }
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/TokenKind.php b/vendor/microsoft/tolerant-php-parser/src/TokenKind.php
deleted file mode 100644
index 29058057..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/TokenKind.php
+++ /dev/null
@@ -1,224 +0,0 @@
- TokenKind::AbstractKeyword,
- "and" => TokenKind::AndKeyword,
- "array" => TokenKind::ArrayKeyword,
- "as" => TokenKind::AsKeyword,
- "break" => TokenKind::BreakKeyword,
- "callable" => TokenKind::CallableKeyword,
- "case" => TokenKind::CaseKeyword,
- "catch" => TokenKind::CatchKeyword,
- "class" => TokenKind::ClassKeyword,
- "clone" => TokenKind::CloneKeyword,
- "const" => TokenKind::ConstKeyword,
- "continue" => TokenKind::ContinueKeyword,
- "declare" => TokenKind::DeclareKeyword,
- "default" => TokenKind::DefaultKeyword,
- "die" => TokenKind::DieKeyword,
- "do" => TokenKind::DoKeyword,
- "echo" => TokenKind::EchoKeyword,
- "else" => TokenKind::ElseKeyword,
- "elseif" => TokenKind::ElseIfKeyword,
- "empty" => TokenKind::EmptyKeyword,
- "enddeclare" => TokenKind::EndDeclareKeyword,
- "endfor" => TokenKind::EndForKeyword,
- "endforeach" => TokenKind::EndForEachKeyword,
- "endif" => TokenKind::EndIfKeyword,
- "endswitch" => TokenKind::EndSwitchKeyword,
- "endwhile" => TokenKind::EndWhileKeyword,
- "enum" => TokenKind::EnumKeyword,
- "eval" => TokenKind::EvalKeyword,
- "exit" => TokenKind::ExitKeyword,
- "extends" => TokenKind::ExtendsKeyword,
- "final" => TokenKind::FinalKeyword,
- "finally" => TokenKind::FinallyKeyword,
- "for" => TokenKind::ForKeyword,
- "foreach" => TokenKind::ForeachKeyword,
- "fn" => TokenKind::FnKeyword,
- "function" => TokenKind::FunctionKeyword,
- "global" => TokenKind::GlobalKeyword,
- "goto" => TokenKind::GotoKeyword,
- "if" => TokenKind::IfKeyword,
- "implements" => TokenKind::ImplementsKeyword,
- "include" => TokenKind::IncludeKeyword,
- "include_once" => TokenKind::IncludeOnceKeyword,
- "instanceof" => TokenKind::InstanceOfKeyword,
- "insteadof" => TokenKind::InsteadOfKeyword,
- "interface" => TokenKind::InterfaceKeyword,
- "isset" => TokenKind::IsSetKeyword,
- "list" => TokenKind::ListKeyword,
- "namespace" => TokenKind::NamespaceKeyword,
- "new" => TokenKind::NewKeyword,
- "or" => TokenKind::OrKeyword,
- "print" => TokenKind::PrintKeyword,
- "private" => TokenKind::PrivateKeyword,
- "protected" => TokenKind::ProtectedKeyword,
- "public" => TokenKind::PublicKeyword,
- "readonly" => TokenKind::ReadonlyKeyword,
- "require" => TokenKind::RequireKeyword,
- "require_once" => TokenKind::RequireOnceKeyword,
- "return" => TokenKind::ReturnKeyword,
- "static" => TokenKind::StaticKeyword,
- "switch" => TokenKind::SwitchKeyword,
- "throw" => TokenKind::ThrowKeyword,
- "trait" => TokenKind::TraitKeyword,
- "try" => TokenKind::TryKeyword,
- "unset" => TokenKind::UnsetKeyword,
- "use" => TokenKind::UseKeyword,
- "var" => TokenKind::VarKeyword,
- "while" => TokenKind::WhileKeyword,
- "xor" => TokenKind::XorKeyword,
- "yield" => TokenKind::YieldKeyword,
- "yield from" => TokenKind::YieldFromKeyword,
-
-
- // TODO soft reserved words?
- ];
-
- const RESERVED_WORDS = [
- // http://php.net/manual/en/reserved.constants.php
- // TRUE, FALSE, NULL are special predefined constants
- // TODO - also consider adding other constants
- "true" => TokenKind::TrueReservedWord,
- "false" => TokenKind::FalseReservedWord,
- "null" => TokenKind::NullReservedWord,
-
- // RESERVED WORDS:
- // http://php.net/manual/en/reserved.other-reserved-words.php
- "int" => TokenKind::IntReservedWord,
- "float" => TokenKind::FloatReservedWord,
- "bool" => TokenKind::BoolReservedWord,
- "string" => TokenKind::StringReservedWord,
- "binary" => TokenKind::BinaryReservedWord,
- "boolean" => TokenKind::BooleanReservedWord,
- "double" => TokenKind::DoubleReservedWord,
- "integer" => TokenKind::IntegerReservedWord,
- "object" => TokenKind::ObjectReservedWord,
- "real" => TokenKind::RealReservedWord,
- "void" => TokenKind::VoidReservedWord,
- "iterable" => TokenKind::IterableReservedWord,
- "mixed" => TokenKind::MixedReservedWord,
- ];
-
- const OPERATORS_AND_PUNCTUATORS = [
- "[" => TokenKind::OpenBracketToken,
- "]" => TokenKind::CloseBracketToken,
- "(" => TokenKind::OpenParenToken,
- ")" => TokenKind::CloseParenToken,
- "{" => TokenKind::OpenBraceToken,
- "}" => TokenKind::CloseBraceToken,
- "." => TokenKind::DotToken,
- "->" => TokenKind::ArrowToken,
- "=>" => TokenKind::DoubleArrowToken,
- "++" => TokenKind::PlusPlusToken,
- "--" => TokenKind::MinusMinusToken,
- "**" => TokenKind::AsteriskAsteriskToken,
- "*" => TokenKind::AsteriskToken,
- "+" => TokenKind::PlusToken,
- "-" => TokenKind::MinusToken,
- "~" => TokenKind::TildeToken,
- "!" => TokenKind::ExclamationToken,
- "$" => TokenKind::DollarToken,
- "/" => TokenKind::SlashToken,
- "%" => TokenKind::PercentToken,
- "<<" => TokenKind::LessThanLessThanToken,
- ">>" => TokenKind::GreaterThanGreaterThanToken,
- "<" => TokenKind::LessThanToken,
- ">" => TokenKind::GreaterThanToken,
- "<=" => TokenKind::LessThanEqualsToken,
- ">=" => TokenKind::GreaterThanEqualsToken,
- "==" => TokenKind::EqualsEqualsToken,
- "===" => TokenKind::EqualsEqualsEqualsToken,
- "!=" => TokenKind::ExclamationEqualsToken,
- "!==" => TokenKind::ExclamationEqualsEqualsToken,
- "^" => TokenKind::CaretToken,
- "|" => TokenKind::BarToken,
- "&" => TokenKind::AmpersandToken,
- "&&" => TokenKind::AmpersandAmpersandToken,
- "||" => TokenKind::BarBarToken,
- "?" => TokenKind::QuestionToken,
- ":" => TokenKind::ColonToken,
- "::" => TokenKind::ColonColonToken,
- ";" => TokenKind::SemicolonToken,
- "=" => TokenKind::EqualsToken,
- "**=" => TokenKind::AsteriskAsteriskEqualsToken,
- "*=" => TokenKind::AsteriskEqualsToken,
- "/=" => TokenKind::SlashEqualsToken,
- "%=" => TokenKind::PercentEqualsToken,
- "+=" => TokenKind::PlusEqualsToken,
- "-=" => TokenKind::MinusEqualsToken,
- ".=" => TokenKind::DotEqualsToken,
- "<<=" => TokenKind::LessThanLessThanEqualsToken,
- ">>=" => TokenKind::GreaterThanGreaterThanEqualsToken,
- "&=" => TokenKind::AmpersandEqualsToken,
- "^=" => TokenKind::CaretEqualsToken,
- "|=" => TokenKind::BarEqualsToken,
- "," => TokenKind::CommaToken,
- "?->" => TokenKind::QuestionArrowToken,
- "??" => TokenKind::QuestionQuestionToken,
- "??=" => TokenKind::QuestionQuestionEqualsToken,
- "<=>" => TokenKind::LessThanEqualsGreaterThanToken,
- "<>" => TokenKind::LessThanGreaterThanToken,
- "..." => TokenKind::DotDotDotToken,
- "\\" => TokenKind::BackslashToken,
- "=" => TokenKind::ScriptSectionStartWithEchoTag, // TODO, technically not an operator
- " TokenKind::ScriptSectionStartTag, // TODO, technically not an operator
- " TokenKind::ScriptSectionStartTag, // TODO add tests
- " TokenKind::ScriptSectionStartTag,
- " TokenKind::ScriptSectionStartTag,
- " TokenKind::ScriptSectionStartTag,
- "?>" => TokenKind::ScriptSectionEndTag, // TODO, technically not an operator
- "?>\n" => TokenKind::ScriptSectionEndTag, // TODO, technically not an operator
- "?>\r\n" => TokenKind::ScriptSectionEndTag, // TODO, technically not an operator
- "?>\r" => TokenKind::ScriptSectionEndTag, // TODO, technically not an operator
- "@" => TokenKind::AtSymbolToken, // TODO not in spec
- "`" => TokenKind::BacktickToken
- ];
-
-// TODO add new tokens
-}
diff --git a/vendor/microsoft/tolerant-php-parser/src/bootstrap.php b/vendor/microsoft/tolerant-php-parser/src/bootstrap.php
deleted file mode 100644
index 821c5444..00000000
--- a/vendor/microsoft/tolerant-php-parser/src/bootstrap.php
+++ /dev/null
@@ -1,12 +0,0 @@
-" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
\ No newline at end of file
diff --git a/vendor/netresearch/jsonmapper/composer.json b/vendor/netresearch/jsonmapper/composer.json
deleted file mode 100644
index 0c5c699e..00000000
--- a/vendor/netresearch/jsonmapper/composer.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "name": "netresearch/jsonmapper",
- "description": "Map nested JSON structures onto PHP classes",
- "license": "OSL-3.0",
- "autoload": {
- "psr-0": {"JsonMapper": "src/"}
- },
- "authors": [
- {
- "name": "Christian Weiske",
- "email": "cweiske@cweiske.de",
- "homepage": "http://github.com/cweiske/jsonmapper/",
- "role": "Developer"
- }
- ],
- "support": {
- "email": "cweiske@cweiske.de",
- "issues": "https://github.com/cweiske/jsonmapper/issues"
- },
- "require":{
- "php": ">=7.1",
- "ext-spl": "*",
- "ext-json": "*",
- "ext-pcre": "*",
- "ext-reflection": "*"
- },
- "require-dev": {
- "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
- "squizlabs/php_codesniffer": "~3.5"
- }
-}
diff --git a/vendor/netresearch/jsonmapper/contributing.rst b/vendor/netresearch/jsonmapper/contributing.rst
deleted file mode 100644
index 1e40f12c..00000000
--- a/vendor/netresearch/jsonmapper/contributing.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-**************************************
-How to add your features to JsonMapper
-**************************************
-
-If you want to add a new feature or a fix to this library, please consider these aspects:
-
-- Respect the original code style and continue using it - it uses `PEAR Coding Standards`__.
-- Pull requests fixing a bug should include a test case that illustrates the wrong behaviour.
-- Pull requests adding a new feature should also include a test for the new feature.
-
- __ http://pear.php.net/manual/en/standards.php
-
-Having test cases included in your pull request greatly helps reviewing it and will increase the chance of it being merged.
diff --git a/vendor/netresearch/jsonmapper/phpunit.xml b/vendor/netresearch/jsonmapper/phpunit.xml
deleted file mode 100644
index 306182e1..00000000
--- a/vendor/netresearch/jsonmapper/phpunit.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
- src/
-
-
-
-
- tests
-
-
-
diff --git a/vendor/netresearch/jsonmapper/src/JsonMapper.php b/vendor/netresearch/jsonmapper/src/JsonMapper.php
deleted file mode 100644
index fdb984f0..00000000
--- a/vendor/netresearch/jsonmapper/src/JsonMapper.php
+++ /dev/null
@@ -1,872 +0,0 @@
-
- * @license OSL-3.0 http://opensource.org/licenses/osl-3.0
- * @link http://cweiske.de/
- */
-
-/**
- * Automatically map JSON structures into objects.
- *
- * @category Netresearch
- * @package JsonMapper
- * @author Christian Weiske
- * @license OSL-3.0 http://opensource.org/licenses/osl-3.0
- * @link http://cweiske.de/
- */
-class JsonMapper
-{
- /**
- * PSR-3 compatible logger object
- *
- * @link http://www.php-fig.org/psr/psr-3/
- * @var object
- * @see setLogger()
- */
- protected $logger;
-
- /**
- * Throw an exception when JSON data contain a property
- * that is not defined in the PHP class
- *
- * @var boolean
- */
- public $bExceptionOnUndefinedProperty = false;
-
- /**
- * Throw an exception if the JSON data miss a property
- * that is marked with @required in the PHP class
- *
- * @var boolean
- */
- public $bExceptionOnMissingData = false;
-
- /**
- * If the types of map() parameters shall be checked.
- *
- * You have to disable it if you're using the json_decode "assoc" parameter.
- *
- * json_decode($str, false)
- *
- * @var boolean
- */
- public $bEnforceMapType = true;
-
- /**
- * Throw an exception when an object is expected but the JSON contains
- * a non-object type.
- *
- * @var boolean
- */
- public $bStrictObjectTypeChecking = false;
-
- /**
- * Throw an exception, if null value is found
- * but the type of attribute does not allow nulls.
- *
- * @var bool
- */
- public $bStrictNullTypes = true;
-
- /**
- * Allow mapping of private and proteted properties.
- *
- * @var boolean
- */
- public $bIgnoreVisibility = false;
-
- /**
- * Remove attributes that were not passed in JSON,
- * to avoid confusion between them and NULL values.
- *
- * @var boolean
- */
- public $bRemoveUndefinedAttributes = false;
-
- /**
- * Override class names that JsonMapper uses to create objects.
- * Useful when your setter methods accept abstract classes or interfaces.
- *
- * @var array
- */
- public $classMap = array();
-
- /**
- * Callback used when an undefined property is found.
- *
- * Works only when $bExceptionOnUndefinedProperty is disabled.
- *
- * Parameters to this function are:
- * 1. Object that is being filled
- * 2. Name of the unknown JSON property
- * 3. JSON value of the property
- *
- * @var callable
- */
- public $undefinedPropertyHandler = null;
-
- /**
- * Runtime cache for inspected classes. This is particularly effective if
- * mapArray() is called with a large number of objects
- *
- * @var array property inspection result cache
- */
- protected $arInspectedClasses = array();
-
- /**
- * Method to call on each object after deserialization is done.
- *
- * Is only called if it exists on the object.
- *
- * @var string|null
- */
- public $postMappingMethod = null;
-
- /**
- * Map data all data in $json into the given $object instance.
- *
- * @param object|array $json JSON object structure from json_decode()
- * @param object $object Object to map $json data into
- *
- * @return mixed Mapped object is returned.
- * @see mapArray()
- */
- public function map($json, $object)
- {
- if ($this->bEnforceMapType && !is_object($json)) {
- throw new InvalidArgumentException(
- 'JsonMapper::map() requires first argument to be an object'
- . ', ' . gettype($json) . ' given.'
- );
- }
- if (!is_object($object)) {
- throw new InvalidArgumentException(
- 'JsonMapper::map() requires second argument to be an object'
- . ', ' . gettype($object) . ' given.'
- );
- }
-
- $strClassName = get_class($object);
- $rc = new ReflectionClass($object);
- $strNs = $rc->getNamespaceName();
- $providedProperties = array();
- foreach ($json as $key => $jvalue) {
- $key = $this->getSafeName($key);
- $providedProperties[$key] = true;
-
- // Store the property inspection results so we don't have to do it
- // again for subsequent objects of the same type
- if (!isset($this->arInspectedClasses[$strClassName][$key])) {
- $this->arInspectedClasses[$strClassName][$key]
- = $this->inspectProperty($rc, $key);
- }
-
- list($hasProperty, $accessor, $type, $isNullable)
- = $this->arInspectedClasses[$strClassName][$key];
-
- if (!$hasProperty) {
- if ($this->bExceptionOnUndefinedProperty) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" does not exist'
- . ' in object of type ' . $strClassName
- );
- } else if ($this->undefinedPropertyHandler !== null) {
- call_user_func(
- $this->undefinedPropertyHandler,
- $object, $key, $jvalue
- );
- } else {
- $this->log(
- 'info',
- 'Property {property} does not exist in {class}',
- array('property' => $key, 'class' => $strClassName)
- );
- }
- continue;
- }
-
- if ($accessor === null) {
- if ($this->bExceptionOnUndefinedProperty) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" has no public setter method'
- . ' in object of type ' . $strClassName
- );
- }
- $this->log(
- 'info',
- 'Property {property} has no public setter method in {class}',
- array('property' => $key, 'class' => $strClassName)
- );
- continue;
- }
-
- if ($isNullable || !$this->bStrictNullTypes) {
- if ($jvalue === null) {
- $this->setProperty($object, $accessor, null);
- continue;
- }
- $type = $this->removeNullable($type);
- } else if ($jvalue === null) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" in class "'
- . $strClassName . '" must not be NULL'
- );
- }
-
- $type = $this->getFullNamespace($type, $strNs);
- $type = $this->getMappedType($type, $jvalue);
-
- if ($type === null || $type === 'mixed') {
- //no given type - simply set the json data
- $this->setProperty($object, $accessor, $jvalue);
- continue;
- } else if ($this->isObjectOfSameType($type, $jvalue)) {
- $this->setProperty($object, $accessor, $jvalue);
- continue;
- } else if ($this->isSimpleType($type)) {
- if ($type === 'string' && is_object($jvalue)) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" in class "'
- . $strClassName . '" is an object and'
- . ' cannot be converted to a string'
- );
- }
- settype($jvalue, $type);
- $this->setProperty($object, $accessor, $jvalue);
- continue;
- }
-
- //FIXME: check if type exists, give detailed error message if not
- if ($type === '') {
- throw new JsonMapper_Exception(
- 'Empty type at property "'
- . $strClassName . '::$' . $key . '"'
- );
- } else if (strpos($type, '|')) {
- throw new JsonMapper_Exception(
- 'Cannot decide which of the union types shall be used: '
- . $type
- );
- }
-
- $array = null;
- $subtype = null;
- if ($this->isArrayOfType($type)) {
- //array
- $array = array();
- $subtype = substr($type, 0, -2);
- } else if (substr($type, -1) == ']') {
- list($proptype, $subtype) = explode('[', substr($type, 0, -1));
- if ($proptype == 'array') {
- $array = array();
- } else {
- $array = $this->createInstance($proptype, false, $jvalue);
- }
- } else {
- if (is_a($type, 'ArrayObject', true)) {
- $array = $this->createInstance($type, false, $jvalue);
- }
- }
-
- if ($array !== null) {
- if (!is_array($jvalue) && $this->isFlatType(gettype($jvalue))) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" must be an array, '
- . gettype($jvalue) . ' given'
- );
- }
-
- $cleanSubtype = $this->removeNullable($subtype);
- $subtype = $this->getFullNamespace($cleanSubtype, $strNs);
- $child = $this->mapArray($jvalue, $array, $subtype, $key);
- } else if ($this->isFlatType(gettype($jvalue))) {
- //use constructor parameter if we have a class
- // but only a flat type (i.e. string, int)
- if ($this->bStrictObjectTypeChecking) {
- throw new JsonMapper_Exception(
- 'JSON property "' . $key . '" must be an object, '
- . gettype($jvalue) . ' given'
- );
- }
- $child = $this->createInstance($type, true, $jvalue);
- } else {
- $child = $this->createInstance($type, false, $jvalue);
- $this->map($jvalue, $child);
- }
- $this->setProperty($object, $accessor, $child);
- }
-
- if ($this->bExceptionOnMissingData) {
- $this->checkMissingData($providedProperties, $rc);
- }
-
- if ($this->bRemoveUndefinedAttributes) {
- $this->removeUndefinedAttributes($object, $providedProperties);
- }
-
- if ($this->postMappingMethod !== null
- && $rc->hasMethod($this->postMappingMethod)
- ) {
- $refDeserializePostMethod = $rc->getMethod(
- $this->postMappingMethod
- );
- $refDeserializePostMethod->setAccessible(true);
- $refDeserializePostMethod->invoke($object);
- }
-
- return $object;
- }
-
- /**
- * Convert a type name to a fully namespaced type name.
- *
- * @param string $type Type name (simple type or class name)
- * @param string $strNs Base namespace that gets prepended to the type name
- *
- * @return string Fully-qualified type name with namespace
- */
- protected function getFullNamespace($type, $strNs)
- {
- if ($type === null || $type === '' || $type[0] === '\\' || $strNs === '') {
- return $type;
- }
- list($first) = explode('[', $type, 2);
- if ($first === 'mixed' || $this->isSimpleType($first)) {
- return $type;
- }
-
- //create a full qualified namespace
- return '\\' . $strNs . '\\' . $type;
- }
-
- /**
- * Check required properties exist in json
- *
- * @param array $providedProperties array with json properties
- * @param object $rc Reflection class to check
- *
- * @throws JsonMapper_Exception
- *
- * @return void
- */
- protected function checkMissingData($providedProperties, ReflectionClass $rc)
- {
- foreach ($rc->getProperties() as $property) {
- $rprop = $rc->getProperty($property->name);
- $docblock = $rprop->getDocComment();
- $annotations = static::parseAnnotations($docblock);
- if (isset($annotations['required'])
- && !isset($providedProperties[$property->name])
- ) {
- throw new JsonMapper_Exception(
- 'Required property "' . $property->name . '" of class '
- . $rc->getName()
- . ' is missing in JSON data'
- );
- }
- }
- }
-
- /**
- * Remove attributes from object that were not passed in JSON data.
- *
- * This is to avoid confusion between those that were actually passed
- * as NULL, and those that weren't provided at all.
- *
- * @param object $object Object to remove properties from
- * @param array $providedProperties Array with JSON properties
- *
- * @return void
- */
- protected function removeUndefinedAttributes($object, $providedProperties)
- {
- foreach (get_object_vars($object) as $propertyName => $dummy) {
- if (!isset($providedProperties[$propertyName])) {
- unset($object->{$propertyName});
- }
- }
- }
-
- /**
- * Map an array
- *
- * @param array $json JSON array structure from json_decode()
- * @param mixed $array Array or ArrayObject that gets filled with
- * data from $json
- * @param string $class Class name for children objects.
- * All children will get mapped onto this type.
- * Supports class names and simple types
- * like "string" and nullability "string|null".
- * Pass "null" to not convert any values
- * @param string $parent_key Defines the key this array belongs to
- * in order to aid debugging.
- *
- * @return mixed Mapped $array is returned
- */
- public function mapArray($json, $array, $class = null, $parent_key = '')
- {
- $originalClass = $class;
- foreach ($json as $key => $jvalue) {
- $class = $this->getMappedType($originalClass, $jvalue);
- if ($class === null) {
- $array[$key] = $jvalue;
- } else if ($this->isArrayOfType($class)) {
- $array[$key] = $this->mapArray(
- $jvalue,
- array(),
- substr($class, 0, -2)
- );
- } else if ($this->isFlatType(gettype($jvalue))) {
- //use constructor parameter if we have a class
- // but only a flat type (i.e. string, int)
- if ($jvalue === null) {
- $array[$key] = null;
- } else {
- if ($this->isSimpleType($class)) {
- settype($jvalue, $class);
- $array[$key] = $jvalue;
- } else {
- $array[$key] = $this->createInstance(
- $class, true, $jvalue
- );
- }
- }
- } else if ($this->isFlatType($class)) {
- throw new JsonMapper_Exception(
- 'JSON property "' . ($parent_key ? $parent_key : '?') . '"'
- . ' is an array of type "' . $class . '"'
- . ' but contained a value of type'
- . ' "' . gettype($jvalue) . '"'
- );
- } else if (is_a($class, 'ArrayObject', true)) {
- $array[$key] = $this->mapArray(
- $jvalue,
- $this->createInstance($class)
- );
- } else {
- $array[$key] = $this->map(
- $jvalue, $this->createInstance($class, false, $jvalue)
- );
- }
- }
- return $array;
- }
-
- /**
- * Try to find out if a property exists in a given class.
- * Checks property first, falls back to setter method.
- *
- * @param ReflectionClass $rc Reflection class to check
- * @param string $name Property name
- *
- * @return array First value: if the property exists
- * Second value: the accessor to use (
- * ReflectionMethod or ReflectionProperty, or null)
- * Third value: type of the property
- * Fourth value: if the property is nullable
- */
- protected function inspectProperty(ReflectionClass $rc, $name)
- {
- //try setter method first
- $setter = 'set' . $this->getCamelCaseName($name);
-
- if ($rc->hasMethod($setter)) {
- $rmeth = $rc->getMethod($setter);
- if ($rmeth->isPublic() || $this->bIgnoreVisibility) {
- $isNullable = false;
- $rparams = $rmeth->getParameters();
- if (count($rparams) > 0) {
- $isNullable = $rparams[0]->allowsNull();
- $ptype = $rparams[0]->getType();
- if ($ptype !== null) {
- $typeName = $this->stringifyReflectionType($ptype);
- //allow overriding an "array" type hint
- // with a more specific class in the docblock
- if ($typeName !== 'array') {
- return array(
- true, $rmeth,
- $typeName,
- $isNullable,
- );
- }
- }
- }
-
- $docblock = $rmeth->getDocComment();
- $annotations = static::parseAnnotations($docblock);
-
- if (!isset($annotations['param'][0])) {
- return array(true, $rmeth, null, $isNullable);
- }
- list($type) = explode(' ', trim($annotations['param'][0]));
- return array(true, $rmeth, $type, $this->isNullable($type));
- }
- }
-
- //now try to set the property directly
- //we have to look it up in the class hierarchy
- $class = $rc;
- $rprop = null;
- do {
- if ($class->hasProperty($name)) {
- $rprop = $class->getProperty($name);
- }
- } while ($rprop === null && $class = $class->getParentClass());
-
- if ($rprop === null) {
- //case-insensitive property matching
- foreach ($rc->getProperties() as $p) {
- if ((strcasecmp($p->name, $name) === 0)) {
- $rprop = $p;
- break;
- }
- }
- }
- if ($rprop !== null) {
- if ($rprop->isPublic() || $this->bIgnoreVisibility) {
- $docblock = $rprop->getDocComment();
- $annotations = static::parseAnnotations($docblock);
-
- if (!isset($annotations['var'][0])) {
- // If there is no annotations (higher priority) inspect
- // if there's a scalar type being defined
- if (PHP_VERSION_ID >= 70400 && $rprop->hasType()) {
- $rPropType = $rprop->getType();
- $propTypeName = $this->stringifyReflectionType($rPropType);
- if ($this->isSimpleType($propTypeName)) {
- return array(
- true,
- $rprop,
- $propTypeName,
- $rPropType->allowsNull()
- );
- }
-
- return array(
- true,
- $rprop,
- '\\' . ltrim($propTypeName, '\\'),
- $rPropType->allowsNull()
- );
- }
-
- return array(true, $rprop, null, false);
- }
-
- //support "@var type description"
- list($type) = explode(' ', $annotations['var'][0]);
-
- return array(true, $rprop, $type, $this->isNullable($type));
- } else {
- //no setter, private property
- return array(true, null, null, false);
- }
- }
-
- //no setter, no property
- return array(false, null, null, false);
- }
-
- /**
- * Removes - and _ and makes the next letter uppercase
- *
- * @param string $name Property name
- *
- * @return string CamelCasedVariableName
- */
- protected function getCamelCaseName($name)
- {
- return str_replace(
- ' ', '', ucwords(str_replace(array('_', '-'), ' ', $name))
- );
- }
-
- /**
- * Since hyphens cannot be used in variables we have to uppercase them.
- *
- * Technically you may use them, but they are awkward to access.
- *
- * @param string $name Property name
- *
- * @return string Name without hyphen
- */
- protected function getSafeName($name)
- {
- if (strpos($name, '-') !== false) {
- $name = $this->getCamelCaseName($name);
- }
-
- return $name;
- }
-
- /**
- * Set a property on a given object to a given value.
- *
- * Checks if the setter or the property are public are made before
- * calling this method.
- *
- * @param object $object Object to set property on
- * @param object $accessor ReflectionMethod or ReflectionProperty
- * @param mixed $value Value of property
- *
- * @return void
- */
- protected function setProperty(
- $object, $accessor, $value
- ) {
- if (!$accessor->isPublic() && $this->bIgnoreVisibility) {
- $accessor->setAccessible(true);
- }
- if ($accessor instanceof ReflectionProperty) {
- $accessor->setValue($object, $value);
- } else {
- //setter method
- $accessor->invoke($object, $value);
- }
- }
-
- /**
- * Create a new object of the given type.
- *
- * This method exists to be overwritten in child classes,
- * so you can do dependency injection or so.
- *
- * @param string $class Class name to instantiate
- * @param boolean $useParameter Pass $parameter to the constructor or not
- * @param mixed $jvalue Constructor parameter (the json value)
- *
- * @return object Freshly created object
- */
- protected function createInstance(
- $class, $useParameter = false, $jvalue = null
- ) {
- if ($useParameter) {
- return new $class($jvalue);
- } else {
- $reflectClass = new ReflectionClass($class);
- $constructor = $reflectClass->getConstructor();
- if (null === $constructor
- || $constructor->getNumberOfRequiredParameters() > 0
- ) {
- return $reflectClass->newInstanceWithoutConstructor();
- }
- return $reflectClass->newInstance();
- }
- }
-
- /**
- * Get the mapped class/type name for this class.
- * Returns the incoming classname if not mapped.
- *
- * @param string $type Type name to map
- * @param mixed $jvalue Constructor parameter (the json value)
- *
- * @return string The mapped type/class name
- */
- protected function getMappedType($type, $jvalue = null)
- {
- if (isset($this->classMap[$type])) {
- $target = $this->classMap[$type];
- } else if (is_string($type) && $type !== '' && $type[0] == '\\'
- && isset($this->classMap[substr($type, 1)])
- ) {
- $target = $this->classMap[substr($type, 1)];
- } else {
- $target = null;
- }
-
- if ($target) {
- if (is_callable($target)) {
- $type = $target($type, $jvalue);
- } else {
- $type = $target;
- }
- }
- return $type;
- }
-
- /**
- * Checks if the given type is a "simple type"
- *
- * @param string $type type name from gettype()
- *
- * @return boolean True if it is a simple PHP type
- *
- * @see isFlatType()
- */
- protected function isSimpleType($type)
- {
- return $type == 'string'
- || $type == 'boolean' || $type == 'bool'
- || $type == 'integer' || $type == 'int'
- || $type == 'double' || $type == 'float'
- || $type == 'array' || $type == 'object';
- }
-
- /**
- * Checks if the object is of this type or has this type as one of its parents
- *
- * @param string $type class name of type being required
- * @param mixed $value Some PHP value to be tested
- *
- * @return boolean True if $object has type of $type
- */
- protected function isObjectOfSameType($type, $value)
- {
- if (false === is_object($value)) {
- return false;
- }
-
- return is_a($value, $type);
- }
-
- /**
- * Checks if the given type is a type that is not nested
- * (simple type except array and object)
- *
- * @param string $type type name from gettype()
- *
- * @return boolean True if it is a non-nested PHP type
- *
- * @see isSimpleType()
- */
- protected function isFlatType($type)
- {
- return $type == 'NULL'
- || $type == 'string'
- || $type == 'boolean' || $type == 'bool'
- || $type == 'integer' || $type == 'int'
- || $type == 'double' || $type == 'float';
- }
-
- /**
- * Returns true if type is an array of elements
- * (bracket notation)
- *
- * @param string $strType type to be matched
- *
- * @return bool
- */
- protected function isArrayOfType($strType)
- {
- return substr($strType, -2) === '[]';
- }
-
- /**
- * Checks if the given type is nullable
- *
- * @param string $type type name from the phpdoc param
- *
- * @return boolean True if it is nullable
- */
- protected function isNullable($type)
- {
- return stripos('|' . $type . '|', '|null|') !== false;
- }
-
- /**
- * Remove the 'null' section of a type
- *
- * @param string $type type name from the phpdoc param
- *
- * @return string The new type value
- */
- protected function removeNullable($type)
- {
- if ($type === null) {
- return null;
- }
- return substr(
- str_ireplace('|null|', '|', '|' . $type . '|'),
- 1, -1
- );
- }
-
- /**
- * Get a string representation of the reflection type.
- * Required because named, union and intersection types need to be handled.
- *
- * @param ReflectionType $type Native PHP type
- *
- * @return string "foo|bar"
- */
- protected function stringifyReflectionType(ReflectionType $type)
- {
- if ($type instanceof ReflectionNamedType) {
- return ($type->isBuiltin() ? '' : '\\') . $type->getName();
- }
-
- return implode(
- '|',
- array_map(
- function (ReflectionNamedType $type) {
- return ($type->isBuiltin() ? '' : '\\') . $type->getName();
- },
- $type->getTypes()
- )
- );
- }
-
- /**
- * Copied from PHPUnit 3.7.29, Util/Test.php
- *
- * @param string $docblock Full method docblock
- *
- * @return array Array of arrays.
- * Key is the "@"-name like "param",
- * each value is an array of the rest of the @-lines
- */
- protected static function parseAnnotations($docblock)
- {
- $annotations = array();
- // Strip away the docblock header and footer
- // to ease parsing of one line annotations
- $docblock = substr($docblock, 3, -2);
-
- $re = '/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m';
- if (preg_match_all($re, $docblock, $matches)) {
- $numMatches = count($matches[0]);
-
- for ($i = 0; $i < $numMatches; ++$i) {
- $annotations[$matches['name'][$i]][] = $matches['value'][$i];
- }
- }
-
- return $annotations;
- }
-
- /**
- * Log a message to the $logger object
- *
- * @param string $level Logging level
- * @param string $message Text to log
- * @param array $context Additional information
- *
- * @return null
- */
- protected function log($level, $message, array $context = array())
- {
- if ($this->logger) {
- $this->logger->log($level, $message, $context);
- }
- }
-
- /**
- * Sets a logger instance on the object
- *
- * @param LoggerInterface $logger PSR-3 compatible logger object
- *
- * @return null
- */
- public function setLogger($logger)
- {
- $this->logger = $logger;
- }
-}
-?>
diff --git a/vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php b/vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php
deleted file mode 100644
index bb8040c6..00000000
--- a/vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php
+++ /dev/null
@@ -1,26 +0,0 @@
-
- * @license OSL-3.0 http://opensource.org/licenses/osl-3.0
- * @link http://cweiske.de/
- */
-
-/**
- * Simple exception
- *
- * @category Netresearch
- * @package JsonMapper
- * @author Christian Weiske
- * @license OSL-3.0 http://opensource.org/licenses/osl-3.0
- * @link http://cweiske.de/
- */
-class JsonMapper_Exception extends Exception
-{
-}
-?>
diff --git a/vendor/phan/phan/.dockerignore b/vendor/phan/phan/.dockerignore
deleted file mode 100644
index 8c458950..00000000
--- a/vendor/phan/phan/.dockerignore
+++ /dev/null
@@ -1,28 +0,0 @@
-.git/
-internal/PHP_CodeSniffer
-tests/docker
-tests/run_all_tests_dockerized
-.git
-vendor
-.phan/data
-composer.phar
-\#*#
-build
-report
-samples
-all_output.*
-.vscode/
-*.joblog
-*.swo
-*.swp
-*.zip
-tags
-.phan/config.local.php
-nohup.out
-*.md.new
-.idea/
-src_copy
-out/
-phpspy.*
-.phpunit.result.cache
-errors.txt
diff --git a/vendor/phan/phan/.phan/baseline.php.example b/vendor/phan/phan/.phan/baseline.php.example
deleted file mode 100644
index 02dae031..00000000
--- a/vendor/phan/phan/.phan/baseline.php.example
+++ /dev/null
@@ -1,21 +0,0 @@
- [
- '.phan/plugins/DuplicateExpressionPlugin.php' => ['PhanTypeMismatchArgumentInternal'],
- ],
- // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueNames']] can be manually added if needed.
- // (directory_suppressions will currently be ignored by subsequent calls to --save-baseline, but may be preserved in future Phan releases)
-];
diff --git a/vendor/phan/phan/.phan/bin/mkfilelist b/vendor/phan/phan/.phan/bin/mkfilelist
deleted file mode 100755
index deb8b018..00000000
--- a/vendor/phan/phan/.phan/bin/mkfilelist
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-if [[ -z $WORKSPACE ]]
-then
- export WORKSPACE=.
-fi
-cd $WORKSPACE
-
-for dir in \
- src \
- tests/Phan \
- vendor/phpunit/phpunit/src vendor/symfony/console
-do
- if [ -d "$dir" ]; then
- find $dir -name '*.php'
- fi
-done
diff --git a/vendor/phan/phan/.phan/bin/phan b/vendor/phan/phan/.phan/bin/phan
deleted file mode 100755
index b25f6e89..00000000
--- a/vendor/phan/phan/.phan/bin/phan
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-# Root directory of project
-export ROOT=`git rev-parse --show-toplevel`
-
-# Phan's directory for executables
-export BIN=$ROOT/.phan/bin
-
-# Phan's data directory
-export DATA=$ROOT/.phan/data
-mkdir -p $DATA;
-
-# Go to the root of this git repo
-pushd $ROOT > /dev/null
-
- # Get the current hash of HEAD
- export REV=`git rev-parse HEAD`
-
- # Create the data directory for this run if it
- # doesn't exist yet
- export RUN=$DATA/$REV
- mkdir -p $RUN
-
- $BIN/mkfilelist > $RUN/files
-
- # Run the analysis, emitting output to the console
- # and using a previous state file.
- phan \
- --progress-bar \
- --project-root-directory $ROOT \
- --output $RUN/issues && exit $?
-
- # Re-link the latest directory
- rm -f $ROOT/.phan/data/latest
- ln -s $RUN $DATA/latest
-
- # Output any issues that were found
- cat $RUN/issues
-
-popd > /dev/null
diff --git a/vendor/phan/phan/.phan/config.php b/vendor/phan/phan/.phan/config.php
deleted file mode 100644
index 24f778e7..00000000
--- a/vendor/phan/phan/.phan/config.php
+++ /dev/null
@@ -1,680 +0,0 @@
- null,
-
- // The PHP version that will be used for feature/syntax compatibility warnings.
- // Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`, `'7.4'`,
- // `'8.0'`, `'8.1'`, `null`.
- // If this is set to `null`, Phan will first attempt to infer the value from
- // the project's composer.json's `{"require": {"php": "version range"}}` if possible.
- // If that could not be determined, then Phan assumes `target_php_version`.
- //
- // For analyzing Phan 3.x, this is determined to be `'7.2'` from `"version": "^7.2.0"`.
- 'minimum_target_php_version' => '7.2',
-
- // Default: true. If this is set to true,
- // and target_php_version is newer than the version used to run Phan,
- // Phan will act as though functions added in newer PHP versions exist.
- //
- // NOTE: Currently, this only affects Closure::fromCallable
- 'pretend_newer_core_functions_exist' => true,
-
- // If true, missing properties will be created when
- // they are first seen. If false, we'll report an
- // error message.
- 'allow_missing_properties' => false,
-
- // Allow null to be cast as any type and for any
- // type to be cast to null.
- 'null_casts_as_any_type' => false,
-
- // Allow null to be cast as any array-like type
- // This is an incremental step in migrating away from null_casts_as_any_type.
- // If null_casts_as_any_type is true, this has no effect.
- 'null_casts_as_array' => false,
-
- // Allow any array-like type to be cast to null.
- // This is an incremental step in migrating away from null_casts_as_any_type.
- // If null_casts_as_any_type is true, this has no effect.
- 'array_casts_as_null' => false,
-
- // If enabled, Phan will warn if **any** type in a method invocation's object
- // is definitely not an object,
- // or if **any** type in an invoked expression is not a callable.
- // Setting this to true will introduce numerous false positives
- // (and reveal some bugs).
- 'strict_method_checking' => true,
-
- // If enabled, Phan will warn if **any** type in the argument's union type
- // cannot be cast to a type in the parameter's expected union type.
- // Setting this to true will introduce numerous false positives
- // (and reveal some bugs).
- 'strict_param_checking' => true,
-
- // If enabled, Phan will warn if **any** type in a property assignment's union type
- // cannot be cast to a type in the property's declared union type.
- // Setting this to true will introduce numerous false positives
- // (and reveal some bugs).
- // (For self-analysis, Phan has a large number of suppressions and file-level suppressions, due to \ast\Node being difficult to type check)
- 'strict_property_checking' => true,
-
- // If enabled, Phan will warn if **any** type in a returned value's union type
- // cannot be cast to the declared return type.
- // Setting this to true will introduce numerous false positives
- // (and reveal some bugs).
- // (For self-analysis, Phan has a large number of suppressions and file-level suppressions, due to \ast\Node being difficult to type check)
- 'strict_return_checking' => true,
-
- // If enabled, Phan will warn if **any** type of the object expression for a property access
- // does not contain that property.
- 'strict_object_checking' => true,
-
- // If enabled, scalars (int, float, bool, string, null)
- // are treated as if they can cast to each other.
- // This does not affect checks of array keys. See `scalar_array_key_cast`.
- 'scalar_implicit_cast' => false,
-
- // If enabled, any scalar array keys (int, string)
- // are treated as if they can cast to each other.
- // E.g. `array` can cast to `array` and vice versa.
- // Normally, a scalar type such as int could only cast to/from int and mixed.
- 'scalar_array_key_cast' => false,
-
- // If this has entries, scalars (int, float, bool, string, null)
- // are allowed to perform the casts listed.
- //
- // E.g. `['int' => ['float', 'string'], 'float' => ['int'], 'string' => ['int'], 'null' => ['string']]`
- // allows casting null to a string, but not vice versa.
- // (subset of `scalar_implicit_cast`)
- 'scalar_implicit_partial' => [],
-
- // If true, Phan will convert the type of a possibly undefined array offset to the nullable, defined equivalent.
- // If false, Phan will convert the type of a possibly undefined array offset to the defined equivalent (without converting to nullable).
- 'convert_possibly_undefined_offset_to_nullable' => false,
-
- // If true, seemingly undeclared variables in the global
- // scope will be ignored.
- //
- // This is useful for projects with complicated cross-file
- // globals that you have no hope of fixing.
- 'ignore_undeclared_variables_in_global_scope' => false,
-
- // Backwards Compatibility Checking (This is very slow)
- 'backward_compatibility_checks' => false,
-
- // If true, check to make sure the return type declared
- // in the doc-block (if any) matches the return type
- // declared in the method signature.
- 'check_docblock_signature_return_type_match' => true,
-
- // If true, check to make sure the param types declared
- // in the doc-block (if any) matches the param types
- // declared in the method signature.
- 'check_docblock_signature_param_type_match' => true,
-
- // If true, make narrowed types from phpdoc params override
- // the real types from the signature, when real types exist.
- // (E.g. allows specifying desired lists of subclasses,
- // or to indicate a preference for non-nullable types over nullable types)
- //
- // Affects analysis of the body of the method and the param types passed in by callers.
- //
- // (*Requires `check_docblock_signature_param_type_match` to be true*)
- 'prefer_narrowed_phpdoc_param_type' => true,
-
- // (*Requires `check_docblock_signature_return_type_match` to be true*)
- //
- // If true, make narrowed types from phpdoc returns override
- // the real types from the signature, when real types exist.
- //
- // (E.g. allows specifying desired lists of subclasses,
- // or to indicate a preference for non-nullable types over nullable types)
- // Affects analysis of return statements in the body of the method and the return types passed in by callers.
- 'prefer_narrowed_phpdoc_return_type' => true,
-
- // If enabled, check all methods that override a
- // parent method to make sure its signature is
- // compatible with the parent's. This check
- // can add quite a bit of time to the analysis.
- // This will also check if final methods are overridden, etc.
- 'analyze_signature_compatibility' => true,
-
- // Set this to true to make Phan guess that undocumented parameter types
- // (for optional parameters) have the same type as default values
- // (Instead of combining that type with `mixed`).
- // E.g. `function($x = 'val')` would make Phan infer that $x had a type of `string`, not `string|mixed`.
- // Phan will not assume it knows specific types if the default value is false or null.
- 'guess_unknown_parameter_type_using_default' => false,
-
- // Allow adding types to vague return types such as @return object, @return ?mixed in function/method/closure union types.
- // Normally, Phan only adds inferred returned types when there is no `@return` type or real return type signature..
- // This setting can be disabled on individual methods by adding `@phan-hardcode-return-type` to the doc comment.
- //
- // Disabled by default. This is more useful with `--analyze-twice`.
- 'allow_overriding_vague_return_types' => true,
-
- // When enabled, infer that the types of the properties of `$this` are equal to their default values at the start of `__construct()`.
- // This will have some false positives due to Phan not checking for setters and initializing helpers.
- // This does not affect inherited properties.
- 'infer_default_properties_in_construct' => true,
-
- // Set this to true to enable the plugins that Phan uses to infer more accurate return types of `implode`, `json_decode`, and many other functions.
- //
- // Phan is slightly faster when these are disabled.
- 'enable_extended_internal_return_type_plugins' => true,
-
- // This setting maps case-insensitive strings to union types.
- //
- // This is useful if a project uses phpdoc that differs from the phpdoc2 standard.
- //
- // If the corresponding value is the empty string,
- // then Phan will ignore that union type (E.g. can ignore 'the' in `@return the value`)
- //
- // If the corresponding value is not empty,
- // then Phan will act as though it saw the corresponding UnionTypes(s)
- // when the keys show up in a UnionType of `@param`, `@return`, `@var`, `@property`, etc.
- //
- // This matches the **entire string**, not parts of the string.
- // (E.g. `@return the|null` will still look for a class with the name `the`, but `@return the` will be ignored with the below setting)
- //
- // (These are not aliases, this setting is ignored outside of doc comments).
- // (Phan does not check if classes with these names exist)
- //
- // Example setting: `['unknown' => '', 'number' => 'int|float', 'char' => 'string', 'long' => 'int', 'the' => '']`
- 'phpdoc_type_mapping' => [ ],
-
- // Set to true in order to attempt to detect dead
- // (unreferenced) code. Keep in mind that the
- // results will only be a guess given that classes,
- // properties, constants and methods can be referenced
- // as variables (like `$class->$property` or
- // `$class->$method()`) in ways that we're unable
- // to make sense of.
- //
- // To more aggressively detect dead code,
- // you may want to set `dead_code_detection_prefer_false_negative` to `false`.
- 'dead_code_detection' => false,
-
- // Set to true in order to attempt to detect unused variables.
- // `dead_code_detection` will also enable unused variable detection.
- //
- // This has a few known false positives, e.g. for loops or branches.
- 'unused_variable_detection' => true,
-
- // Set to true in order to force tracking references to elements
- // (functions/methods/consts/protected).
- // dead_code_detection is another option which also causes references
- // to be tracked.
- 'force_tracking_references' => false,
-
- // Set to true in order to attempt to detect redundant and impossible conditions.
- //
- // This has some false positives involving loops,
- // variables set in branches of loops, and global variables.
- 'redundant_condition_detection' => true,
-
- // Set to true in order to attempt to detect error-prone truthiness/falsiness checks.
- //
- // This is not suitable for all codebases.
- 'error_prone_truthy_condition_detection' => true,
-
- // Enable this to warn about harmless redundant use for classes and namespaces such as `use Foo\bar` in namespace Foo.
- //
- // Note: This does not affect warnings about redundant uses in the global namespace.
- 'warn_about_redundant_use_namespaced_class' => true,
-
- // If true, then run a quick version of checks that takes less time.
- // False by default.
- 'quick_mode' => false,
-
- // If true, then before analysis, try to simplify AST into a form
- // which improves Phan's type inference in edge cases.
- //
- // This may conflict with 'dead_code_detection'.
- // When this is true, this slows down analysis slightly.
- //
- // E.g. rewrites `if ($a = value() && $a > 0) {...}`
- // into $a = value(); if ($a) { if ($a > 0) {...}}`
- 'simplify_ast' => true,
-
- // If true, Phan will read `class_alias` calls in the global scope,
- // then (1) create aliases from the *parsed* files if no class definition was found,
- // and (2) emit issues in the global scope if the source or target class is invalid.
- // (If there are multiple possible valid original classes for an aliased class name,
- // the one which will be created is unspecified.)
- // NOTE: THIS IS EXPERIMENTAL, and the implementation may change.
- 'enable_class_alias_support' => false,
-
- // Enable or disable support for generic templated
- // class types.
- 'generic_types_enabled' => true,
-
- // If enabled, warn about throw statement where the exception types
- // are not documented in the PHPDoc of functions, methods, and closures.
- 'warn_about_undocumented_throw_statements' => true,
-
- // If enabled (and warn_about_undocumented_throw_statements is enabled),
- // warn about function/closure/method calls that have (at)throws
- // without the invoking method documenting that exception.
- 'warn_about_undocumented_exceptions_thrown_by_invoked_functions' => true,
-
- // If this is a list, Phan will not warn about lack of documentation of (at)throws
- // for any of the listed classes or their subclasses.
- // This setting only matters when warn_about_undocumented_throw_statements is true.
- // The default is the empty array (Warn about every kind of Throwable)
- 'exception_classes_with_optional_throws_phpdoc' => [
- 'LogicException',
- 'RuntimeException',
- 'InvalidArgumentException',
- 'AssertionError',
- 'TypeError',
- 'Phan\Exception\IssueException', // TODO: Make Phan aware that some arguments suppress certain issues
- 'Phan\AST\TolerantASTConverter\InvalidNodeException', // This is used internally in TolerantASTConverter
-
- // TODO: Undo the suppressions for the below categories of issues:
- 'Phan\Exception\CodeBaseException',
- // phpunit
- 'PHPUnit\Framework\ExpectationFailedException',
- 'SebastianBergmann\RecursionContext\InvalidArgumentException',
- ],
-
- // Increase this to properly analyze require_once statements
- 'max_literal_string_type_length' => 1000,
-
- // Setting this to true makes the process assignment for file analysis
- // as predictable as possible, using consistent hashing.
- // Even if files are added or removed, or process counts change,
- // relatively few files will move to a different group.
- // (use when the number of files is much larger than the process count)
- // NOTE: If you rely on Phan parsing files/directories in the order
- // that they were provided in this config, don't use this)
- // See https://github.com/phan/phan/wiki/Different-Issue-Sets-On-Different-Numbers-of-CPUs
- 'consistent_hashing_file_order' => false,
-
- // If enabled, Phan will act as though it's certain of real return types of a subset of internal functions,
- // even if those return types aren't available in reflection (real types were taken from php 7.3 or 8.0-dev, depending on target_php_version).
- //
- // Note that with php 7 and earlier, php would return null or false for many internal functions if the argument types or counts were incorrect.
- // As a result, enabling this setting with target_php_version 8.0 may result in false positives for `--redundant-condition-detection` when codebases also support php 7.x.
- 'assume_real_types_for_internal_functions' => true,
-
- // Override to hardcode existence and types of (non-builtin) globals.
- // Class names should be prefixed with '\\'.
- // (E.g. ['_FOO' => '\\FooClass', 'page' => '\\PageClass', 'userId' => 'int'])
- 'globals_type_map' => [],
-
- // The minimum severity level to report on. This can be
- // set to Issue::SEVERITY_LOW, Issue::SEVERITY_NORMAL or
- // Issue::SEVERITY_CRITICAL.
- 'minimum_severity' => Issue::SEVERITY_LOW,
-
- // Add any issue types (such as `'PhanUndeclaredMethod'`)
- // to this list to inhibit them from being reported.
- 'suppress_issue_types' => [
- 'PhanUnreferencedClosure', // False positives seen with closures in arrays, TODO: move closure checks closer to what is done by unused variable plugin
- 'PhanPluginNoCommentOnProtectedMethod',
- 'PhanPluginDescriptionlessCommentOnProtectedMethod',
- 'PhanPluginNoCommentOnPrivateMethod',
- 'PhanPluginDescriptionlessCommentOnPrivateMethod',
- 'PhanPluginDescriptionlessCommentOnPrivateProperty',
- // TODO: Fix edge cases in --automatic-fix for PhanPluginRedundantClosureComment
- 'PhanPluginRedundantClosureComment',
- 'PhanPluginPossiblyStaticPublicMethod',
- 'PhanPluginPossiblyStaticProtectedMethod',
- // The types of ast\Node->children are all possibly unset.
- 'PhanTypePossiblyInvalidDimOffset',
- // TODO: Fix PhanParamNameIndicatingUnusedInClosure instances (low priority)
- 'PhanParamNameIndicatingUnusedInClosure',
- ],
-
- // If this list is empty, no filter against issues types will be applied.
- // If this list is non-empty, only issues within the list
- // will be emitted by Phan.
- //
- // See https://github.com/phan/phan/wiki/Issue-Types-Caught-by-Phan
- // for the full list of issues that Phan detects.
- //
- // Phan is capable of detecting hundreds of types of issues.
- // Projects should almost always use `suppress_issue_types` instead.
- 'whitelist_issue_types' => [
- // 'PhanUndeclaredClass',
- ],
-
- // A list of files to include in analysis
- 'file_list' => [
- 'phan',
- 'phan_client',
- 'plugins/codeclimate/engine',
- 'tool/analyze_phpt',
- 'tool/make_stubs',
- 'tool/pdep',
- 'tool/phantasm',
- 'tool/phoogle',
- 'tool/phan_repl_helpers.php',
- 'internal/dump_fallback_ast.php',
- 'internal/dump_html_styles.php',
- 'internal/emit_signature_map_for_php_version.php',
- 'internal/extract_arg_info.php',
- 'internal/flatten_signature_map.php',
- 'internal/internalsignatures.php',
- 'internal/line_deleter.php',
- 'internal/package.php',
- 'internal/reflection_completeness_check.php',
- 'internal/sanitycheck.php',
- 'internal/sort_signature_map.php',
- 'vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php',
- 'vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php',
- 'vendor/phpdocumentor/reflection-docblock/src/DocBlock.php',
- // 'vendor/phpunit/phpunit/src/Framework/TestCase.php',
- ],
-
- // A regular expression to match files to be excluded
- // from parsing and analysis and will not be read at all.
- //
- // This is useful for excluding groups of test or example
- // directories/files, unanalyzable files, or files that
- // can't be removed for whatever reason.
- // (e.g. '@Test\.php$@', or '@vendor/.*/(tests|Tests)/@')
- 'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
-
- // Enable this to enable checks of require/include statements referring to valid paths.
- 'enable_include_path_checks' => true,
-
- // A list of include paths to check when checking if `require_once`, `include`, etc. are valid.
- //
- // To refer to the directory of the file being analyzed, use `'.'`
- // To refer to the project root directory, you must use \Phan\Config::getProjectRootDirectory()
- //
- // (E.g. `['.', \Phan\Config::getProjectRootDirectory() . '/src/folder-added-to-include_path']`)
- 'include_paths' => ['.'],
-
- // Enable this to warn about the use of relative paths in `require_once`, `include`, etc.
- // Relative paths are harder to reason about, and opcache may have issues with relative paths in edge cases.
- 'warn_about_relative_include_statement' => true,
-
- // A list of files that will be excluded from parsing and analysis
- // and will not be read at all.
- //
- // This is useful for excluding hopelessly unanalyzable
- // files that can't be removed for whatever reason.
- 'exclude_file_list' => [
- 'internal/Sniffs/ValidUnderscoreVariableNameSniff.php',
- ],
-
- // The number of processes to fork off during the analysis
- // phase.
- 'processes' => 1,
-
- // A list of directories that should be parsed for class and
- // method information. After excluding the directories
- // defined in exclude_analysis_directory_list, the remaining
- // files will be statically analyzed for errors.
- //
- // Thus, both first-party and third-party code being used by
- // your application should be included in this list.
- 'directory_list' => [
- 'internal/lib',
- 'src',
- 'tests/Phan',
- 'vendor/composer/semver/src',
- 'vendor/composer/xdebug-handler/src',
- 'vendor/felixfbecker/advanced-json-rpc/lib',
- 'vendor/microsoft/tolerant-php-parser/src',
- 'vendor/netresearch/jsonmapper/src',
- 'vendor/phpunit/phpunit/src',
- 'vendor/psr/log', // subdirectory depends on dependency version
- 'vendor/sabre/event/lib',
- 'vendor/symfony/console',
- 'vendor/symfony/polyfill-php80',
- 'vendor/tysonandre/var_representation_polyfill/src',
- '.phan/plugins',
- '.phan/stubs',
- ],
-
- // List of case-insensitive file extensions supported by Phan.
- // (e.g. php, html, htm)
- 'analyzed_file_extensions' => ['php'],
-
- // A directory list that defines files that will be excluded
- // from static analysis, but whose class and method
- // information should be included.
- //
- // Generally, you'll want to include the directories for
- // third-party code (such as 'vendor/') in this list.
- //
- // n.b.: If you'd like to parse but not analyze 3rd
- // party code, directories containing that code
- // should be added to the `directory_list` as
- // to `exclude_analysis_directory_list`.
- 'exclude_analysis_directory_list' => [
- 'vendor/'
- ],
-
- // By default, Phan will log error messages to stdout if PHP is using options that slow the analysis.
- // (e.g. PHP is compiled with `--enable-debug` or when using Xdebug)
- 'skip_slow_php_options_warning' => false,
-
- // You can put paths to internal stubs in this config option.
- // Phan will continue using its detailed type annotations, but load the constants, classes, functions, and classes (and their Reflection types) from these stub files (doubling as valid php files).
- // Use a different extension from php to avoid accidentally loading these.
- // The 'tool/mkstubs' script can be used to generate your own stubs (compatible with php 7.2+ right now)
- //
- // Also see `include_extension_subset` to configure Phan to analyze a codebase as if a certain extension is not available.
- 'autoload_internal_extension_signatures' => [
- 'ast' => '.phan/internal_stubs/ast.phan_php',
- 'ctype' => '.phan/internal_stubs/ctype.phan_php',
- 'igbinary' => '.phan/internal_stubs/igbinary.phan_php',
- 'mbstring' => '.phan/internal_stubs/mbstring.phan_php',
- 'pcntl' => '.phan/internal_stubs/pcntl.phan_php',
- 'phar' => '.phan/internal_stubs/phar.phan_php',
- 'posix' => '.phan/internal_stubs/posix.phan_php',
- 'readline' => '.phan/internal_stubs/readline.phan_php',
- 'simplexml' => '.phan/internal_stubs/simplexml.phan_php',
- 'sysvmsg' => '.phan/internal_stubs/sysvmsg.phan_php',
- 'sysvsem' => '.phan/internal_stubs/sysvsem.phan_php',
- 'sysvshm' => '.phan/internal_stubs/sysvshm.phan_php',
- ],
-
- // This can be set to a list of extensions to limit Phan to using the reflection information of.
- // If this is a list, then Phan will not use the reflection information of extensions outside of this list.
- // The extensions loaded for a given php installation can be seen with `php -m` or `get_loaded_extensions(true)`.
- //
- // Note that this will only prevent Phan from loading reflection information for extensions outside of this set.
- // If you want to add stubs, see `autoload_internal_extension_signatures`.
- //
- // If this is used, 'core', 'date', 'pcre', 'reflection', 'spl', and 'standard' will be automatically added.
- //
- // When this is an array, `ignore_undeclared_functions_with_known_signatures` will always be set to false.
- // (because many of those functions will be outside of the configured list)
- //
- // Also see `ignore_undeclared_functions_with_known_signatures` to warn about using unknown functions.
- // E.g. this is what Phan would use for self-analysis
- /*
- 'included_extension_subset' => [
- 'core',
- 'standard',
- 'filter',
- 'json',
- 'tokenizer', // parsing php code
- 'ast', // parsing php code
-
- 'ctype', // misc uses, also polyfilled
- 'dom', // checkstyle output format
- 'iconv', // symfony mbstring polyfill
- 'igbinary', // serializing/unserializing polyfilled ASTs
- 'libxml', // internal tools for extracting stubs
- 'mbstring', // utf-8 support
- 'pcntl', // daemon/language server and parallel analysis
- 'phar', // packaging
- 'posix', // parallel analysis
- 'readline', // internal debugging utility, rarely used
- 'simplexml', // report generation
- 'sysvmsg', // parallelism
- 'sysvsem',
- 'sysvshm',
- ],
- */
-
- // Set this to false to emit `PhanUndeclaredFunction` issues for internal functions that Phan has signatures for,
- // but aren't available in the codebase, or from Reflection.
- // (may lead to false positives if an extension isn't loaded)
- //
- // If this is true(default), then Phan will not warn.
- //
- // Even when this is false, Phan will still infer return values and check parameters of internal functions
- // if Phan has the signatures.
- 'ignore_undeclared_functions_with_known_signatures' => false,
-
- 'plugin_config' => [
- // A list of 1 or more PHP binaries (Absolute path or program name found in $PATH)
- // to use to analyze your files with PHP's native `--syntax-check`.
- //
- // This can be used to simultaneously run PHP's syntax checks with multiple PHP versions.
- // e.g. `'plugin_config' => ['php_native_syntax_check_binaries' => ['php72', 'php70', 'php56']]`
- // if all of those programs can be found in $PATH
-
- // 'php_native_syntax_check_binaries' => [PHP_BINARY],
-
- // The maximum number of `php --syntax-check` processes to run at any point in time (Minimum: 1).
- // This may be temporarily higher if php_native_syntax_check_binaries has more elements than this process count.
- 'php_native_syntax_check_max_processes' => 4,
-
- // List of methods to suppress warnings about for HasPHPDocPlugin
- 'has_phpdoc_method_ignore_regex' => '@^Phan\\\\Tests\\\\.*::(test.*|.*Provider)$@',
- // Warn about duplicate descriptions for methods and property groups within classes.
- // (This skips over deprecated methods)
- // This may not apply to all code bases,
- // but is useful in avoiding copied and pasted descriptions that may be inapplicable or too vague.
- 'has_phpdoc_check_duplicates' => true,
-
- // If true, then never allow empty statement lists, even if there is a TODO/FIXME/"deliberately empty" comment.
- 'empty_statement_list_ignore_todos' => true,
-
- // Automatically infer which methods are pure (i.e. should have no side effects) in UseReturnValuePlugin.
- 'infer_pure_methods' => true,
-
- // Warn if newline is allowed before end of string for `$` (the default unless the `D` modifier (`PCRE_DOLLAR_ENDONLY`) is passed in).
- // This is specific to coding styles.
- 'regex_warn_if_newline_allowed_at_end' => true,
- ],
-
- // A list of plugin files to execute
- // NOTE: values can be the base name without the extension for plugins bundled with Phan (E.g. 'AlwaysReturnPlugin')
- // or relative/absolute paths to the plugin (Relative to the project root).
- 'plugins' => [
- 'AlwaysReturnPlugin', // i.e. '.phan/plugin/AlwaysReturnPlugin.php' in phan itself
- 'DollarDollarPlugin',
- 'UnreachableCodePlugin',
- 'DuplicateArrayKeyPlugin',
- 'PregRegexCheckerPlugin',
- 'PrintfCheckerPlugin',
- 'PHPUnitAssertionPlugin', // analyze assertSame/assertInstanceof/assertTrue/assertFalse
- 'UseReturnValuePlugin',
-
- // UnknownElementTypePlugin warns about unknown types in element signatures.
- 'UnknownElementTypePlugin',
- 'DuplicateExpressionPlugin',
- // warns about carriage returns("\r"), trailing whitespace, and tabs in PHP files.
- 'WhitespacePlugin',
- // Warn about inline HTML anywhere in the files.
- 'InlineHTMLPlugin',
- ////////////////////////////////////////////////////////////////////////
- // Plugins for Phan's self-analysis
- ////////////////////////////////////////////////////////////////////////
-
- // Warns about the usage of assert() for Phan's self-analysis. See https://github.com/phan/phan/issues/288
- 'NoAssertPlugin',
- 'PossiblyStaticMethodPlugin',
-
- 'HasPHPDocPlugin',
- 'PHPDocToRealTypesPlugin', // suggests replacing (at)return void with `: void` in the declaration, etc.
- 'PHPDocRedundantPlugin',
- 'PreferNamespaceUsePlugin',
- 'EmptyStatementListPlugin',
-
- // Report empty (not overridden or overriding) methods and functions
- // 'EmptyMethodAndFunctionPlugin',
-
- // This should only be enabled if the code being analyzed contains Phan plugins.
- 'PhanSelfCheckPlugin',
- // Warn about using the same loop variable name as a loop variable of an outer loop.
- 'LoopVariableReusePlugin',
- // Warn about assigning the value the variable already had to that variable.
- 'RedundantAssignmentPlugin',
- // These are specific to Phan's coding style
- 'StrictComparisonPlugin',
- // Warn about `$var == SOME_INT_OR_STRING_CONST` due to unintuitive behavior such as `0 == 'a'`
- 'StrictLiteralComparisonPlugin',
- 'ShortArrayPlugin',
- 'SimplifyExpressionPlugin',
- // 'UnknownClassElementAccessPlugin' is more useful with batch analysis than in an editor.
- // It's used in tests/run_test __FakeSelfFallbackTest
-
- // This checks that there are no accidental echos/printfs left inside Phan's code.
- 'RemoveDebugStatementPlugin',
- 'UnsafeCodePlugin',
- 'DeprecateAliasPlugin',
- // Suggest '@return never'
- '.phan/plugins/AddNeverReturnTypePlugin.php',
- // Still have false positives to suppress
- // '.phan/plugins/StaticVariableMisusePlugin.php',
-
- ////////////////////////////////////////////////////////////////////////
- // End plugins for Phan's self-analysis
- ////////////////////////////////////////////////////////////////////////
-
- // 'SleepCheckerPlugin' is useful for projects which heavily use the __sleep() method. Phan doesn't use __sleep().
- // InvokePHPNativeSyntaxCheckPlugin invokes 'php --no-php-ini --syntax-check ${abs_path_to_analyzed_file}.php' and reports any error messages.
- // Using this can cause phan's overall analysis time to more than double.
- // 'InvokePHPNativeSyntaxCheckPlugin',
-
- // 'PHPUnitNotDeadCodePlugin', // Marks PHPUnit test case subclasses and test cases as referenced code. This is only useful for runs when dead code detection is enabled.
-
- // 'PHPDocInWrongCommentPlugin', // Useful to warn about using "/*" instead of ""/**" where phpdoc annotations are used. This is slow due to needing to tokenize files.
-
- // NOTE: This plugin only produces correct results when
- // Phan is run on a single core (-j1).
- // 'UnusedSuppressionPlugin',
- ],
-];
diff --git a/vendor/phan/phan/.phan/internal_stubs/README.md b/vendor/phan/phan/.phan/internal_stubs/README.md
deleted file mode 100644
index 27754acf..00000000
--- a/vendor/phan/phan/.phan/internal_stubs/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-This folder will eventually contain stubs for the latest versions of various extensions.
-If the extension is loaded in the binary to run phan, then phan will do nothing.
-The plan is to make phan load these files and act as though internal classes, constants,
-and functions existed with the same signatures as these php files.
diff --git a/vendor/phan/phan/.phan/internal_stubs/ast.phan_php b/vendor/phan/phan/.phan/internal_stubs/ast.phan_php
deleted file mode 100644
index 561b7db3..00000000
--- a/vendor/phan/phan/.phan/internal_stubs/ast.phan_php
+++ /dev/null
@@ -1,234 +0,0 @@
-getFQSEN() !== $method->getDefiningFQSEN()) {
- // Check if this was inherited by a descendant class.
- return;
- }
-
- if ($method->getUnionType()->hasType(NeverType::instance(false))) {
- return;
- }
- if ($method->isOverriddenByAnother()) {
- return;
- }
-
- // This modifies the nodes in place, check this last
- if (!BlockExitStatusChecker::willUnconditionallyNeverReturn($stmts_list)) {
- return;
- }
- self::emitIssue(
- $code_base,
- $method->getContext(),
- 'PhanPluginNeverReturnMethod',
- "Method {METHOD} never returns and has a return type of {TYPE}, but phpdoc type {TYPE} could be used instead",
- [$method->getRepresentationForIssue(), $method->getUnionType(), 'never']
- );
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the function exists
- *
- * @param Func $function
- * A function or closure being analyzed
- *
- * @override
- */
- public function analyzeFunction(
- CodeBase $code_base,
- Func $function
- ): void {
- $stmts_list = self::getStatementListToAnalyze($function);
- if ($stmts_list === null) {
- // check for abstract methods, generators, etc.
- return;
- }
-
- if ($function->getUnionType()->hasType(NeverType::instance(false))) {
- return;
- }
- // This modifies the nodes in place, check this last
- if (!BlockExitStatusChecker::willUnconditionallyNeverReturn($stmts_list)) {
- return;
- }
- self::emitIssue(
- $code_base,
- $function->getContext(),
- 'PhanPluginNeverReturnFunction',
- "Function {FUNCTION} never returns and has a return type of {TYPE}, but phpdoc type {TYPE} could be used instead",
- [$function->getRepresentationForIssue(), $function->getUnionType(), 'never']
- );
- }
-
- /**
- * @param Func|Method $func
- * @return ?Node - returns null if there's no statement list to analyze
- */
- private static function getStatementListToAnalyze($func): ?Node
- {
- if (!$func->hasNode()) {
- return null;
- } elseif ($func->hasYield()) {
- // generators always return Generator.
- return null;
- }
- $node = $func->getNode();
- if (!$node) {
- return null;
- }
- return $node->children['stmts'];
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new NeverReturnPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php b/vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php
deleted file mode 100644
index 467eb107..00000000
--- a/vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php
+++ /dev/null
@@ -1,193 +0,0 @@
-getFQSEN() !== $method->getDefiningFQSEN()) {
- // Check if this was inherited by a descendant class.
- return;
- }
-
- if (self::returnTypeOfFunctionLikeAllowsNull($method)) {
- // This has at least one return statement with an expression
- if (!BlockExitStatusChecker::willUnconditionallyThrowOrReturn($stmts_list)) {
- if ($method->getUnionType()->isEmpty() && $method->hasReturn()) {
- if (!$method->checkHasSuppressIssueAndIncrementCount('PhanPluginInconsistentReturnMethod')) {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- 'PhanPluginInconsistentReturnMethod',
- "Method {METHOD} has no return type and will inconsistently return or not return",
- [(string)$method->getFQSEN()]
- );
- }
- }
- }
- return;
- }
- if (!BlockExitStatusChecker::willUnconditionallyThrowOrReturn($stmts_list)) {
- if (!$method->checkHasSuppressIssueAndIncrementCount('PhanPluginAlwaysReturnMethod')) {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- 'PhanPluginAlwaysReturnMethod',
- "Method {METHOD} has a return type of {TYPE}, but may fail to return a value",
- [(string)$method->getFQSEN(), (string)$method->getUnionType()]
- );
- }
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the function exists
- *
- * @param Func $function
- * A function or closure being analyzed
- *
- * @override
- */
- public function analyzeFunction(
- CodeBase $code_base,
- Func $function
- ): void {
- $stmts_list = self::getStatementListToAnalyze($function);
- if ($stmts_list === null) {
- // check for generators, etc.
- return;
- }
-
- if (self::returnTypeOfFunctionLikeAllowsNull($function)) {
- if ($function->getUnionType()->isEmpty() && $function->hasReturn()) {
- // This has at least one return statement with an expression
- if (!BlockExitStatusChecker::willUnconditionallyThrowOrReturn($stmts_list)) {
- if (!$function->checkHasSuppressIssueAndIncrementCount('PhanPluginInconsistentReturnFunction')) {
- self::emitIssue(
- $code_base,
- $function->getContext(),
- 'PhanPluginInconsistentReturnFunction',
- "Function {FUNCTION} has no return type and will inconsistently return or not return",
- [(string)$function->getFQSEN()]
- );
- }
- }
- }
- return;
- }
- if (!BlockExitStatusChecker::willUnconditionallyThrowOrReturn($stmts_list)) {
- if (!$function->checkHasSuppressIssueAndIncrementCount('PhanPluginAlwaysReturnFunction')) {
- self::emitIssue(
- $code_base,
- $function->getContext(),
- 'PhanPluginAlwaysReturnFunction',
- "Function {FUNCTION} has a return type of {TYPE}, but may fail to return a value",
- [(string)$function->getFQSEN(), (string)$function->getUnionType()]
- );
- }
- }
- }
-
- /**
- * @param Func|Method $func
- * @return ?Node - returns null if there's no statement list to analyze
- */
- private static function getStatementListToAnalyze($func): ?Node
- {
- if (!$func->hasNode()) {
- return null;
- } elseif ($func->hasYield()) {
- // generators always return Generator.
- return null;
- }
- $node = $func->getNode();
- if (!$node) {
- return null;
- }
- return $node->children['stmts'];
- }
-
- /**
- * @param FunctionInterface $func
- * @return bool - Is void(absence of a return type) an acceptable return type.
- * NOTE: projects can customize this as needed.
- */
- private static function returnTypeOfFunctionLikeAllowsNull(FunctionInterface $func): bool
- {
- $real_return_type = $func->getRealReturnType();
- if (!$real_return_type->isEmpty() && !$real_return_type->isType(VoidType::instance(false))) {
- return false;
- }
- $return_type = $func->getUnionType();
- return ($return_type->isEmpty()
- || $return_type->containsNullableLabeled()
- || $return_type->hasType(NeverType::instance(false))
- || $return_type->hasType(VoidType::instance(false))
- || $return_type->hasType(NullType::instance(false)));
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new AlwaysReturnPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/AvoidableGetterPlugin.php b/vendor/phan/phan/.phan/plugins/AvoidableGetterPlugin.php
deleted file mode 100644
index 9f3c190d..00000000
--- a/vendor/phan/phan/.phan/plugins/AvoidableGetterPlugin.php
+++ /dev/null
@@ -1,130 +0,0 @@
-getProperty()` when the property is accessible, and the getter is not overridden.
- */
-class AvoidableGetterPlugin extends PluginV3 implements
- PostAnalyzeNodeCapability
-{
-
- /**
- * @return class-string - name of PluginAwarePostAnalysisVisitor subclass
- */
- public static function getPostAnalyzeNodeVisitorClassName(): string
- {
- return AvoidableGetterVisitor::class;
- }
-}
-
-/**
- * This visitor analyzes node kinds that can be the root of expressions
- * containing duplicate expressions, and is called on nodes in post-order.
- */
-class AvoidableGetterVisitor extends PluginAwarePostAnalysisVisitor
-{
- /**
- * @var array maps getter method names to property names.
- */
- private $getter_to_property_map = [];
-
- public function visitClass(Node $node): void
- {
- if (!$this->context->isInClassScope()) {
- // should be impossible
- return;
- }
- $code_base = $this->code_base;
- $class = $this->context->getClassInScope($code_base);
- $getters = $class->getGettersMap($code_base);
- if (!$getters) {
- return;
- }
- $getter_to_property_map = [];
- foreach ($getters as $prop_name => $methods) {
- $prop_name = (string)$prop_name;
- if (!$class->hasPropertyWithName($code_base, $prop_name)) {
- continue;
- }
- if (!$class->getPropertyByName($code_base, $prop_name)->isAccessibleFromClass($code_base, $class->getFQSEN())) {
- continue;
- }
- foreach ($methods as $method) {
- if ($method->isOverriddenByAnother()) {
- continue;
- }
- $getter_to_property_map[$method->getName()] = $prop_name;
- }
- }
- if (!$getter_to_property_map) {
- return;
- }
- $this->getter_to_property_map = $getter_to_property_map;
- // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
- $this->recursivelyCheck($node->children['stmts']);
- }
-
- private function recursivelyCheck(Node $node): void
- {
- switch ($node->kind) {
- // TODO: Handle phan-closure-scope.
- // case ast\AST_CLOSURE:
- // case ast\AST_ARROW_FUNC:
- case ast\AST_FUNC_DECL:
- case ast\AST_CLASS:
- return;
- // This only supports instance method getters, not static getters (AST_STATIC_CALL)
- case ast\AST_METHOD_CALL:
- if (!ConditionVisitor::isThisVarNode($node->children['expr'])) {
- break;
- }
- $method_name = $node->children['method'];
- if (is_string($method_name)) {
- $property_name = $this->getter_to_property_map[$method_name] ?? null;
- if ($property_name !== null) {
- $this->warnCanReplaceGetterWithProperty($node, $property_name);
- return;
- }
- }
- break;
- }
- foreach ($node->children as $child_node) {
- if ($child_node instanceof Node) {
- $this->recursivelyCheck($child_node);
- }
- }
- }
-
- private function warnCanReplaceGetterWithProperty(Node $node, string $property_name): void
- {
- $class = $this->context->getClassInScope($this->code_base);
- if ($class->isTrait()) {
- $issue_name = 'PhanPluginAvoidableGetterInTrait';
- } else {
- $issue_name = 'PhanPluginAvoidableGetter';
- }
-
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($node->lineno),
- $issue_name,
- "Can replace {METHOD} with {PROPERTY}",
- [ASTReverter::toShortString($node), '$this->' . $property_name]
- );
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-
-return new AvoidableGetterPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/ConstantVariablePlugin.php b/vendor/phan/phan/.phan/plugins/ConstantVariablePlugin.php
deleted file mode 100644
index 78744513..00000000
--- a/vendor/phan/phan/.phan/plugins/ConstantVariablePlugin.php
+++ /dev/null
@@ -1,97 +0,0 @@
-flags & PhanAnnotationAdder::FLAG_INITIALIZES || isset($node->is_reference)) {
- return;
- }
- $var_name = $node->children['name'];
- if (!is_string($var_name)) {
- return;
- }
- if ($this->context->isInLoop() || $this->context->isInGlobalScope()) {
- return;
- }
- $parent_node = end($this->parent_node_list);
- if ($parent_node instanceof Node) {
- switch ($parent_node->kind) {
- case ast\AST_IF_ELEM:
- // Phan modifies type to match condition before plugins are called.
- // --redundant-condition-detection would warn
- return;
- case ast\AST_ASSIGN_OP:
- if ($parent_node->children['var'] === $node) {
- return;
- }
- break;
- }
- }
- $variable = $this->context->getScope()->getVariableByNameOrNull($var_name);
- if (!$variable) {
- return;
- }
- $type = $variable->getUnionType();
- if ($type->isPossiblyUndefined()) {
- return;
- }
- $value = $type->getRealUnionType()->asSingleScalarValueOrNullOrSelf();
- if (is_object($value)) {
- return;
- }
- // TODO: Account for methods expecting references
- if (is_bool($value)) {
- $issue_type = 'PhanPluginConstantVariableBool';
- } elseif (is_null($value)) {
- $issue_type = 'PhanPluginConstantVariableNull';
- } else {
- $issue_type = 'PhanPluginConstantVariableScalar';
- }
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- $issue_type,
- 'Variable ${VARIABLE} is probably constant with a value of {TYPE}',
- [$var_name, $type]
- );
- }
-}
-
-return new ConstantVariablePlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DemoPlugin.php b/vendor/phan/phan/.phan/plugins/DemoPlugin.php
deleted file mode 100644
index 658860c7..00000000
--- a/vendor/phan/phan/.phan/plugins/DemoPlugin.php
+++ /dev/null
@@ -1,228 +0,0 @@
-getName() === 'Class') {
- self::emitIssue(
- $code_base,
- $class->getContext(),
- 'DemoPluginClassName',
- "Class {CLASS} cannot be called `Class`",
- [(string)$class->getFQSEN()]
- );
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the method exists
- *
- * @param Method $method
- * A method being analyzed
- *
- * @override
- */
- public function analyzeMethod(
- CodeBase $code_base,
- Method $method
- ): void {
- // As an example, we test to see if the name of the
- // method is `function`, and emit an issue if it is.
- // NOTE: Placeholders can be found in \Phan\Issue::uncolored_format_string_for_replace
- if ($method->getName() === 'function') {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- 'DemoPluginMethodName',
- "Method {METHOD} cannot be called `function`",
- [(string)$method->getFQSEN()]
- );
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the function exists
- *
- * @param Func $function
- * A function being analyzed
- *
- * @override
- */
- public function analyzeFunction(
- CodeBase $code_base,
- Func $function
- ): void {
- // As an example, we test to see if the name of the
- // function is `function`, and emit an issue if it is.
- if ($function->getName() === 'function') {
- self::emitIssue(
- $code_base,
- $function->getContext(),
- 'DemoPluginFunctionName',
- "Function {FUNCTION} cannot be called `function`",
- [(string)$function->getFQSEN()]
- );
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the property exists
- *
- * @param Property $property
- * A property being analyzed
- *
- * @override
- */
- public function analyzeProperty(
- CodeBase $code_base,
- Property $property
- ): void {
- // As an example, we test to see if the name of the
- // property is `property`, and emit an issue if it is.
- if ($property->getName() === 'property') {
- self::emitIssue(
- $code_base,
- $property->getContext(),
- 'DemoPluginPropertyName',
- "Property {PROPERTY} should not be called `property`",
- [(string)$property->getFQSEN()]
- );
- }
- }
-}
-
-/**
- * When __invoke on this class is called with a node, a method
- * will be dispatched based on the `kind` of the given node.
- *
- * Visitors such as this are useful for defining lots of different
- * checks on a node based on its kind.
- */
-class DemoNodeVisitor extends PluginAwarePostAnalysisVisitor
-{
- // Subclasses should declare protected $parent_node_list as an instance property if they need to know the list.
-
- // @var list - Set after the constructor is called if an instance property with this name is declared
- // protected $parent_node_list;
-
- // A plugin's visitors should NOT implement visit(), unless they need to.
-
- /**
- * @param Node $node
- * A node of kind ast\AST_INSTANCEOF to analyze
- *
- * @override
- */
- public function visitInstanceof(Node $node): void
- {
- // Debug::printNode($node);
-
- $class_name = $node->children['class']->children['name'] ?? null;
-
- // If we can't figure out the name of the class, don't
- // bother continuing.
- if (!is_string($class_name)) {
- return;
- }
-
- // As an example, enforce that we cannot call
- // instanceof against 'object'.
- if ($class_name === 'object') {
- $this->emit(
- 'PhanPluginInstanceOfObject',
- "Cannot call instanceof against `object`"
- );
- }
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new DemoPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin.php b/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin.php
deleted file mode 100644
index f6b2a274..00000000
--- a/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin.php
+++ /dev/null
@@ -1,393 +0,0 @@
- 'gettext',
- 'add' => 'swfmovie_add',
- //'add' => 'swfsprite_add and others',
- 'addaction' => 'swfbutton_addAction',
- 'addcolor' => 'swfdisplayitem_addColor',
- 'addentry' => 'swfgradient_addEntry',
- 'addfill' => 'swfshape_addfill',
- 'addshape' => 'swfbutton_addShape',
- 'addstring' => 'swftext_addString and others',
- //'addstring' => 'swftextfield_addString',
- 'align' => 'swftextfield_align',
- 'chop' => 'rtrim',
- 'close' => 'closedir',
- 'com_get' => 'com_propget',
- 'com_propset' => 'com_propput',
- 'com_set' => 'com_propput',
- 'die' => 'exit',
- 'diskfreespace' => 'disk_free_space',
- 'doubleval' => 'floatval',
- 'drawarc' => 'swfshape_drawarc',
- 'drawcircle' => 'swfshape_drawcircle',
- 'drawcubic' => 'swfshape_drawcubic',
- 'drawcubicto' => 'swfshape_drawcubicto',
- 'drawcurve' => 'swfshape_drawcurve',
- 'drawcurveto' => 'swfshape_drawcurveto',
- 'drawglyph' => 'swfshape_drawglyph',
- 'drawline' => 'swfshape_drawline',
- 'drawlineto' => 'swfshape_drawlineto',
- 'fbsql' => 'fbsql_db_query',
- 'fputs' => 'fwrite',
- 'getascent' => 'swffont_getAscent and others',
- //'getascent' => 'swftext_getAscent',
- 'getdescent' => 'swffont_getDescent and others',
- //'getdescent' => 'swftext_getDescent',
- 'getheight' => 'swfbitmap_getHeight',
- 'getleading' => 'swffont_getLeading and others and others',
- //'getleading' => 'swftext_getLeading',
- 'getshape1' => 'swfmorph_getShape1',
- 'getshape2' => 'swfmorph_getShape2',
- 'getwidth' => 'swfbitmap_getWidth and others',
- //'getwidth' => 'swffont_getWidth',
- //'getwidth' => 'swftext_getWidth',
- 'gzputs' => 'gzwrite',
- 'i18n_convert' => 'mb_convert_encoding',
- 'i18n_discover_encoding' => 'mb_detect_encoding',
- 'i18n_http_input' => 'mb_http_input',
- 'i18n_http_output' => 'mb_http_output',
- 'i18n_internal_encoding' => 'mb_internal_encoding',
- 'i18n_ja_jp_hantozen' => 'mb_convert_kana',
- 'i18n_mime_header_decode' => 'mb_decode_mimeheader',
- 'i18n_mime_header_encode' => 'mb_encode_mimeheader',
- 'imap_create' => 'imap_createmailbox',
- 'imap_fetchtext' => 'imap_body',
- 'imap_getmailboxes' => 'imap_list_full',
- 'imap_getsubscribed' => 'imap_lsub_full',
- 'imap_header' => 'imap_headerinfo',
- 'imap_listmailbox' => 'imap_list',
- 'imap_listsubscribed' => 'imap_lsub',
- 'imap_rename' => 'imap_renamemailbox',
- 'imap_scan' => 'imap_listscan',
- 'imap_scanmailbox' => 'imap_listscan',
- 'ini_alter' => 'ini_set',
- 'is_double' => 'is_float',
- 'is_integer' => 'is_int',
- 'is_long' => 'is_int',
- 'is_real' => 'is_float',
- 'is_writeable' => 'is_writable',
- 'join' => 'implode',
- 'key_exists' => 'array_key_exists',
- 'labelframe' => 'swfmovie_labelFrame and others',
- //'labelframe' => 'swfsprite_labelFrame',
- 'ldap_close' => 'ldap_unbind',
- 'magic_quotes_runtime' => 'set_magic_quotes_runtime',
- 'mbstrcut' => 'mb_strcut',
- 'mbstrlen' => 'mb_strlen',
- 'mbstrpos' => 'mb_strpos',
- 'mbstrrpos' => 'mb_strrpos',
- 'mbsubstr' => 'mb_substr',
- 'ming_setcubicthreshold' => 'ming_setCubicThreshold',
- 'ming_setscale' => 'ming_setScale',
- 'move' => 'swfdisplayitem_move',
- 'movepen' => 'swfshape_movepen',
- 'movepento' => 'swfshape_movepento',
- 'moveto' => 'swfdisplayitem_moveTo and others',
- //'moveto' => 'swffill_moveTo',
- //'moveto' => 'swftext_moveTo',
- 'msql' => 'msql_db_query',
- 'msql_createdb' => 'msql_create_db',
- 'msql_dbname' => 'msql_result',
- 'msql_dropdb' => 'msql_drop_db',
- 'msql_fieldflags' => 'msql_field_flags',
- 'msql_fieldlen' => 'msql_field_len',
- 'msql_fieldname' => 'msql_field_name',
- 'msql_fieldtable' => 'msql_field_table',
- 'msql_fieldtype' => 'msql_field_type',
- 'msql_freeresult' => 'msql_free_result',
- 'msql_listdbs' => 'msql_list_dbs',
- 'msql_listfields' => 'msql_list_fields',
- 'msql_listtables' => 'msql_list_tables',
- 'msql_numfields' => 'msql_num_fields',
- 'msql_numrows' => 'msql_num_rows',
- 'msql_regcase' => 'sql_regcase',
- 'msql_selectdb' => 'msql_select_db',
- 'msql_tablename' => 'msql_result',
- 'mssql_affected_rows' => 'sybase_affected_rows',
- 'mssql_close' => 'sybase_close',
- 'mssql_connect' => 'sybase_connect',
- 'mssql_data_seek' => 'sybase_data_seek',
- 'mssql_fetch_array' => 'sybase_fetch_array',
- 'mssql_fetch_field' => 'sybase_fetch_field',
- 'mssql_fetch_object' => 'sybase_fetch_object',
- 'mssql_fetch_row' => 'sybase_fetch_row',
- 'mssql_field_seek' => 'sybase_field_seek',
- 'mssql_free_result' => 'sybase_free_result',
- 'mssql_get_last_message' => 'sybase_get_last_message',
- 'mssql_min_client_severity' => 'sybase_min_client_severity',
- 'mssql_min_error_severity' => 'sybase_min_error_severity',
- 'mssql_min_message_severity' => 'sybase_min_message_severity',
- 'mssql_min_server_severity' => 'sybase_min_server_severity',
- 'mssql_num_fields' => 'sybase_num_fields',
- 'mssql_num_rows' => 'sybase_num_rows',
- 'mssql_pconnect' => 'sybase_pconnect',
- 'mssql_query' => 'sybase_query',
- 'mssql_result' => 'sybase_result',
- 'mssql_select_db' => 'sybase_select_db',
- 'multcolor' => 'swfdisplayitem_multColor',
- 'mysql' => 'mysql_db_query',
- 'mysql_createdb' => 'mysql_create_db',
- 'mysql_db_name' => 'mysql_result',
- 'mysql_dbname' => 'mysql_result',
- 'mysql_dropdb' => 'mysql_drop_db',
- 'mysql_fieldflags' => 'mysql_field_flags',
- 'mysql_fieldlen' => 'mysql_field_len',
- 'mysql_fieldname' => 'mysql_field_name',
- 'mysql_fieldtable' => 'mysql_field_table',
- 'mysql_fieldtype' => 'mysql_field_type',
- 'mysql_freeresult' => 'mysql_free_result',
- 'mysql_listdbs' => 'mysql_list_dbs',
- 'mysql_listfields' => 'mysql_list_fields',
- 'mysql_listtables' => 'mysql_list_tables',
- 'mysql_numfields' => 'mysql_num_fields',
- 'mysql_numrows' => 'mysql_num_rows',
- 'mysql_selectdb' => 'mysql_select_db',
- 'mysql_tablename' => 'mysql_result',
- 'nextframe' => 'swfmovie_nextFrame and others',
- //'nextframe' => 'swfsprite_nextFrame',
- 'ociassignelem' => 'OCI-Collection::assignElem',
- 'ocibindbyname' => 'oci_bind_by_name',
- 'ocicancel' => 'oci_cancel',
- 'ocicloselob' => 'OCI-Lob::close',
- 'ocicollappend' => 'OCI-Collection::append',
- 'ocicollassign' => 'OCI-Collection::assign',
- 'ocicollmax' => 'OCI-Collection::max',
- 'ocicollsize' => 'OCI-Collection::size',
- 'ocicolltrim' => 'OCI-Collection::trim',
- 'ocicolumnisnull' => 'oci_field_is_null',
- 'ocicolumnname' => 'oci_field_name',
- 'ocicolumnprecision' => 'oci_field_precision',
- 'ocicolumnscale' => 'oci_field_scale',
- 'ocicolumnsize' => 'oci_field_size',
- 'ocicolumntype' => 'oci_field_type',
- 'ocicolumntyperaw' => 'oci_field_type_raw',
- 'ocicommit' => 'oci_commit',
- 'ocidefinebyname' => 'oci_define_by_name',
- 'ocierror' => 'oci_error',
- 'ociexecute' => 'oci_execute',
- 'ocifetch' => 'oci_fetch',
- 'ocifetchinto' => 'oci_fetch_array,',
- 'ocifetchstatement' => 'oci_fetch_all',
- 'ocifreecollection' => 'OCI-Collection::free',
- 'ocifreecursor' => 'oci_free_statement',
- 'ocifreedesc' => 'oci_free_descriptor',
- 'ocifreestatement' => 'oci_free_statement',
- 'ocigetelem' => 'OCI-Collection::getElem',
- 'ociinternaldebug' => 'oci_internal_debug',
- 'ociloadlob' => 'OCI-Lob::load',
- 'ocilogon' => 'oci_connect',
- 'ocinewcollection' => 'oci_new_collection',
- 'ocinewcursor' => 'oci_new_cursor',
- 'ocinewdescriptor' => 'oci_new_descriptor',
- 'ocinlogon' => 'oci_new_connect',
- 'ocinumcols' => 'oci_num_fields',
- 'ociparse' => 'oci_parse',
- 'ocipasswordchange' => 'oci_password_change',
- 'ociplogon' => 'oci_pconnect',
- 'ociresult' => 'oci_result',
- 'ocirollback' => 'oci_rollback',
- 'ocisavelob' => 'OCI-Lob::save',
- 'ocisavelobfile' => 'OCI-Lob::import',
- 'ociserverversion' => 'oci_server_version',
- 'ocisetprefetch' => 'oci_set_prefetch',
- 'ocistatementtype' => 'oci_statement_type',
- 'ociwritelobtofile' => 'OCI-Lob::export',
- 'ociwritetemporarylob' => 'OCI-Lob::writeTemporary',
- 'odbc_do' => 'odbc_exec',
- 'odbc_field_precision' => 'odbc_field_len',
- 'output' => 'swfmovie_output',
- 'pdf_add_outline' => 'pdf_add_bookmark',
- 'pg_clientencoding' => 'pg_client_encoding',
- 'pg_setclientencoding' => 'pg_set_client_encoding',
- 'pos' => 'current',
- 'recode' => 'recode_string',
- 'remove' => 'swfmovie_remove and others',
- // 'remove' => 'swfsprite_remove',
- 'rotate' => 'swfdisplayitem_rotate',
- 'rotateto' => 'swfdisplayitem_rotateTo and others',
- // 'rotateto' => 'swffill_rotateTo',
- 'save' => 'swfmovie_save',
- 'savetofile' => 'swfmovie_saveToFile',
- 'scale' => 'swfdisplayitem_scale',
- 'scaleto' => 'swfdisplayitem_scaleTo and others',
- // 'scaleto' => 'swffill_scaleTo',
- 'setaction' => 'swfbutton_setAction',
- 'setbackground' => 'swfmovie_setBackground',
- 'setbounds' => 'swftextfield_setBounds',
- 'setcolor' => 'swftext_setColor and others',
- // 'setcolor' => 'swftextfield_setColor',
- 'setdepth' => 'swfdisplayitem_setDepth',
- 'setdimension' => 'swfmovie_setDimension',
- 'setdown' => 'swfbutton_setDown',
- 'setfont' => 'swftext_setFont and others',
- // 'setfont' => 'swftextfield_setFont',
- 'setframes' => 'swfmovie_setFrames and others',
- // 'setframes' => 'swfsprite_setFrames',
- 'setheight' => 'swftext_setHeight and others',
- // 'setheight' => 'swftextfield_setHeight',
- 'sethit' => 'swfbutton_setHit',
- 'setindentation' => 'swftextfield_setIndentation',
- 'setleftfill' => 'swfshape_setleftfill',
- 'setleftmargin' => 'swftextfield_setLeftMargin',
- 'setline' => 'swfshape_setline',
- 'setlinespacing' => 'swftextfield_setLineSpacing',
- 'setmargins' => 'swftextfield_setMargins',
- 'setmatrix' => 'swfdisplayitem_setMatrix',
- 'setname' => 'swfdisplayitem_setName and others',
- // 'setname' => 'swftextfield_setName',
- 'setover' => 'swfbutton_setOver',
- 'setrate' => 'swfmovie_setRate',
- 'setratio' => 'swfdisplayitem_setRatio',
- 'setrightfill' => 'swfshape_setrightfill',
- 'setrightmargin' => 'swftextfield_setRightMargin',
- 'setspacing' => 'swftext_setSpacing',
- 'setup' => 'swfbutton_setUp',
- 'show_source' => 'highlight_file',
- 'sizeof' => 'count',
- 'skewx' => 'swfdisplayitem_skewX',
- 'skewxto' => 'swfdisplayitem_skewXTo',
- // 'skewxto' => 'swffill_skewXTo',
- 'skewy' => 'swfdisplayitem_skewY and others',
- 'skewyto' => 'swfdisplayitem_skewYTo and others',
- // 'skewyto' => 'swffill_skewYTo',
- 'snmpwalkoid' => 'snmprealwalk',
- 'strchr' => 'strstr',
- 'streammp3' => 'swfmovie_streamMp3',
- 'swfaction' => 'swfaction_init',
- 'swfbitmap' => 'swfbitmap_init',
- 'swfbutton' => 'swfbutton_init',
- 'swffill' => 'swffill_init',
- 'swffont' => 'swffont_init',
- 'swfgradient' => 'swfgradient_init',
- 'swfmorph' => 'swfmorph_init',
- 'swfmovie' => 'swfmovie_init',
- 'swfshape' => 'swfshape_init',
- 'swfsprite' => 'swfsprite_init',
- 'swftext' => 'swftext_init',
- 'swftextfield' => 'swftextfield_init',
- 'xptr_new_context' => 'xpath_new_context',
- // miscellaneous
- 'bzclose' => 'fclose',
- 'bzflush' => 'fflush',
- 'bzwrite' => 'fwrite',
- 'checkdnsrr' => 'dns_check_record',
- 'dir' => 'getdir',
- 'ftp_quit' => 'ftp_close',
- 'getmxrr' => 'dns_get_mx',
- // 'getrandmax' => 'mt_getrandmax', // confusing because rand is not an alias of mt_rand
- 'get_required_files' => 'get_included_files',
- 'gmp_div' => 'gmp_div_q',
- // This may change in the future
- // 'gzclose' => 'fclose',
- // 'gzeof' => 'feof',
- // 'gzgetc' => 'fgetc',
- // 'gzgets' => 'fgets',
- // 'gzpassthru' => 'fpassthru',
- // 'gzread' => 'fread',
- // 'gzrewind' => 'rewind',
- // 'gzseek' => 'fseek',
- // 'gztell' => 'ftell',
- // 'gzwrite' => 'fwrite',
- 'ldap_get_values' => 'ldap_get_values_len',
- 'ldap_modify' => 'ldap_mod_replace',
- 'mysqli_escape_string' => 'mysqli_real_escape_string',
- 'mysqli_execute' => 'mysqli_stmt_execute',
- 'mysqli_set_opt' => 'mysqli_options',
- 'oci_free_cursor' => 'oci_free_statement',
- 'openssl_get_privatekey' => 'openssl_pkey_get_private',
- 'openssl_get_publickey' => 'openssl_pkey_get_public',
- 'pcntl_errno' => 'pcntl_get_last_error',
- 'pg_cmdtuples' => 'pg_affected_rows',
- 'pg_errormessage' => 'pg_last_error',
- 'pg_exec' => 'pg_query',
- 'pg_fieldisnull' => 'pg_field_is_null',
- 'pg_fieldname' => 'pg_field_name',
- 'pg_fieldnum' => 'pg_field_num',
- 'pg_fieldprtlen' => 'pg_field_prtlen',
- 'pg_fieldsize' => 'pg_field_size',
- 'pg_fieldtype' => 'pg_field_type',
- 'pg_freeresult' => 'pg_free_result',
- 'pg_getlastoid' => 'pg_last_oid',
- 'pg_loclose' => 'pg_lo_close',
- 'pg_locreate' => 'pg_lo_create',
- 'pg_loexport' => 'pg_lo_export',
- 'pg_loimport' => 'pg_lo_import',
- 'pg_loopen' => 'pg_lo_open',
- 'pg_loreadall' => 'pg_lo_read_all',
- 'pg_loread' => 'pg_lo_read',
- 'pg_lounlink' => 'pg_lo_unlink',
- 'pg_lowrite' => 'pg_lo_write',
- 'pg_numfields' => 'pg_num_fields',
- 'pg_numrows' => 'pg_num_rows',
- 'pg_result' => 'pg_fetch_result',
- 'posix_errno' => 'posix_get_last_error',
- 'session_commit' => 'session_write_close',
- 'set_file_buffer' => 'stream_set_write_buffer',
- 'snmp_set_oid_numeric_print' => 'snmp_set_oid_output_format',
- 'socket_getopt' => 'socket_get_option',
- 'socket_get_status' => 'stream_get_meta_data',
- 'socket_set_blocking' => 'stream_set_blocking',
- 'socket_setopt' => 'socket_set_option',
- 'socket_set_timeout' => 'stream_set_timeout',
- 'sodium_crypto_scalarmult_base' => 'sodium_crypto_box_publickey_from_secretkey',
- 'srand' => 'mt_srand',
- 'stream_register_wrapper' => 'stream_wrapper_register',
- 'user_error' => 'trigger_error',
- ];
-
- public function beforeAnalyzePhase(CodeBase $code_base): void
- {
- foreach (self::KNOWN_ALIASES as $alias => $original_name) {
- try {
- $fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString($alias);
- } catch (Exception $_) {
- continue;
- }
- if (!$code_base->hasFunctionWithFQSEN($fqsen)) {
- continue;
- }
- $function = $code_base->getFunctionByFQSEN($fqsen);
- if (!$function->isPHPInternal()) {
- continue;
- }
- $function->setIsDeprecated(true);
- if (!$function->getDocComment()) {
- $function->setDocComment('/** @deprecated DeprecateAliasPlugin marked this as an alias of ' .
- $original_name . (strpos($original_name, ' ') === false ? '()' : '') . '*/');
- }
- }
- }
-}
-
-if (Config::isIssueFixingPluginEnabled()) {
- require_once __DIR__ . '/DeprecateAliasPlugin/fixers.php';
-}
-
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new DeprecateAliasPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin/fixers.php b/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin/fixers.php
deleted file mode 100644
index bc5b5d66..00000000
--- a/vendor/phan/phan/.phan/plugins/DeprecateAliasPlugin/fixers.php
+++ /dev/null
@@ -1,72 +0,0 @@
-getLine();
- $reason = (string)$instance->getTemplateParameters()[1];
- if (!preg_match('/Deprecated because: DeprecateAliasPlugin marked this as an alias of (\w+)\(\)/', $reason, $match)) {
- return null;
- }
- $new_name = (string)$match[1];
-
- $function_repr = (string)$instance->getTemplateParameters()[0];
- if (!preg_match('/\\\\(\w+)\(\)/', $function_repr, $match)) {
- return null;
- }
- $expected_name = $match[1];
- $edits = [];
- foreach ($contents->getNodesAtLine($line) as $node) {
- if (!$node instanceof QualifiedName) {
- continue;
- }
- $is_actual_call = $node->parent instanceof CallExpression;
- if (!$is_actual_call) {
- continue;
- }
- $file_contents = $contents->getContents();
- $actual_name = strtolower((new NodeUtils($file_contents))->phpParserNameToString($node));
- if ($actual_name !== $expected_name) {
- continue;
- }
- //fwrite(STDERR, "name is: " . get_class($node->parent) . "\n");
-
- // They are case-sensitively identical.
- // Generate a fix.
- // @phan-suppress-next-line PhanThrowTypeAbsentForCall
- $start = $node->getStartPosition();
- // @phan-suppress-next-line PhanThrowTypeAbsentForCall
- $end = $node->getEndPosition();
- $edits[] = new FileEdit($start, $end, (($file_contents[$start] ?? '') === '\\' ? '\\' : '') . $new_name);
- }
- if ($edits) {
- return new FileEditSet($edits);
- }
- return null;
- };
- IssueFixer::registerFixerClosure(
- 'PhanDeprecatedFunctionInternal',
- $fix
- );
-});
diff --git a/vendor/phan/phan/.phan/plugins/DollarDollarPlugin.php b/vendor/phan/phan/.phan/plugins/DollarDollarPlugin.php
deleted file mode 100644
index 3564402e..00000000
--- a/vendor/phan/phan/.phan/plugins/DollarDollarPlugin.php
+++ /dev/null
@@ -1,78 +0,0 @@
-children['name'] instanceof Node) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDollarDollar',
- "$$ Variables are not allowed.",
- []
- );
- }
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new DollarDollarPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DuplicateArrayKeyPlugin.php b/vendor/phan/phan/.phan/plugins/DuplicateArrayKeyPlugin.php
deleted file mode 100644
index 913e27c3..00000000
--- a/vendor/phan/phan/.phan/plugins/DuplicateArrayKeyPlugin.php
+++ /dev/null
@@ -1,376 +0,0 @@
- value, with `value,`.
- *
- * @see DollarDollarPlugin for generic plugin documentation.
- */
-class DuplicateArrayKeyPlugin extends PluginV3 implements PostAnalyzeNodeCapability
-{
- /**
- * @return string - name of PluginAwarePostAnalysisVisitor subclass
- * @override
- */
- public static function getPostAnalyzeNodeVisitorClassName(): string
- {
- return DuplicateArrayKeyVisitor::class;
- }
-}
-
-/**
- * This class has visitArray called on all array literals in files to check for potential problems with keys.
- *
- * When __invoke on this class is called with a node, a method
- * will be dispatched based on the `kind` of the given node.
- *
- * Visitors such as this are useful for defining lots of different
- * checks on a node based on its kind.
- */
-class DuplicateArrayKeyVisitor extends PluginAwarePostAnalysisVisitor
-{
- private const HASH_PREFIX = "\x00__phan_dnu_";
-
- // Do not define the visit() method unless a plugin has code and needs to visit most/all node types.
-
- /**
- * @param Node $node
- * A switch statement's case statement(AST_SWITCH_LIST) node to analyze
- * @override
- */
- public function visitSwitchList(Node $node): void
- {
- $children = $node->children;
- if (count($children) <= 1) {
- // This plugin will never emit errors if there are 0 or 1 elements.
- return;
- }
-
- $case_constant_set = [];
- $values_to_check = [];
- foreach ($children as $i => $case_node) {
- if (!$case_node instanceof Node) {
- throw new AssertionError("Switch list must contain nodes");
- }
- $case_cond = $case_node->children['cond'];
- if ($case_cond === null) {
- continue; // This is `default:`. php --syntax-check already checks for duplicates.
- }
- // Skip array entries without literal keys. (Do it before resolving the key value)
- if (!is_scalar($case_cond)) {
- $original_case_cond = $case_cond;
- $case_cond = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $case_cond)->asSingleScalarValueOrNullOrSelf();
- if (is_object($case_cond)) {
- $case_cond = $original_case_cond;
- }
- }
- if (is_string($case_cond)) {
- $cond_key = "s$case_cond";
- $values_to_check[$i] = $case_cond;
- } elseif (is_int($case_cond)) {
- $cond_key = $case_cond;
- $values_to_check[$i] = $case_cond;
- } elseif (is_bool($case_cond)) {
- $cond_key = $case_cond ? "T" : "F";
- $values_to_check[$i] = $case_cond;
- } else {
- // could be literal null?
- $cond_key = ASTHasher::hash($case_cond);
- if (!is_object($case_cond)) {
- $values_to_check[$i] = $case_cond;
- }
- }
- if (isset($case_constant_set[$cond_key])) {
- $normalized_case_cond = is_object($case_cond) ? ASTReverter::toShortString($case_cond) : self::normalizeSwitchKey($case_cond);
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($case_node->lineno),
- 'PhanPluginDuplicateSwitchCase',
- "Duplicate/Equivalent switch case({STRING_LITERAL}) detected in switch statement - the later entry will be ignored in favor of case {CODE} at line {LINE}.",
- [$normalized_case_cond, ASTReverter::toShortString($case_constant_set[$cond_key]->children['cond']), $case_constant_set[$cond_key]->lineno],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15071
- );
- // Add a fake value to indicate loose equality checks are redundant
- $values_to_check[-1] = true;
- }
- $case_constant_set[$cond_key] = $case_node;
- }
- if (!isset($values_to_check[-1]) && count($values_to_check) > 1 && !self::areAllSwitchCasesTheSameType($values_to_check)) {
- // @phan-suppress-next-line PhanPartialTypeMismatchArgument array keys are integers for switch
- $this->extendedLooseEqualityCheck($values_to_check, $children);
- }
- }
-
- /**
- * @param array $values_to_check scalar constant values of case statements
- */
- private static function areAllSwitchCasesTheSameType(array $values_to_check): bool
- {
- $categories = 0;
- foreach ($values_to_check as $value) {
- if (is_int($value)) {
- $categories |= 1;
- if ($categories !== 1) {
- return false;
- }
- } elseif (is_string($value)) {
- if (is_numeric($value)) {
- // This includes float-like strings such as `"1e0"`, which adds ambiguity ("1e0" == "1")
- return false;
- }
- $categories |= 2;
- if ($categories !== 2) {
- return false;
- }
- } else {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Perform a heuristic check if any element is `==` a previous element.
- *
- * This is intended to perform well for large arrays.
- *
- * TODO: Do a better job for small arrays.
- * @param array $values_to_check
- * @param list $children an array of scalars
- */
- private function extendedLooseEqualityCheck(array $values_to_check, array $children): void
- {
- $numeric_set = [];
- $fuzzy_numeric_set = [];
- foreach ($values_to_check as $i => $value) {
- if (is_numeric($value)) {
- if (is_int($value)) {
- $old_index = $numeric_set[$value] ?? $fuzzy_numeric_set[$value] ?? null;
- $numeric_set[$value] = $i;
- } else {
- // For `"1"`, search for `"1foo"`, `"1bar"`, etc.
- $original_value = $value;
- $value = is_float($value) ? (string)$value : (string)filter_var($value, FILTER_VALIDATE_FLOAT);
- $old_index = $numeric_set[$value] ?? null;
- if ($value === (string)$original_value) {
- $old_index = $old_index ?? $fuzzy_numeric_set[$value] ?? null;
- $numeric_set[$value] = $i;
- } else {
- $fuzzy_numeric_set[$value] = $i;
- }
- }
- } else {
- $value = (float)$value;
- // For `"1foo"`, search for `1` but not `"1bar"`
- $old_index = $numeric_set[$value] ?? null;
- // @phan-suppress-next-line PhanTypeMismatchDimAssignment
- $fuzzy_numeric_set[$value] = $i;
- }
- if ($old_index !== null) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($children[$i]->lineno),
- 'PhanPluginDuplicateSwitchCaseLooseEquality',
- "Switch case({STRING_LITERAL}) is loosely equivalent (==) to an earlier case ({STRING_LITERAL}) in switch statement - the earlier entry may be chosen instead.",
- [self::normalizeSwitchKey($values_to_check[$i]), self::normalizeSwitchKey($values_to_check[$old_index])],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15072
- );
- }
- }
- }
-
- /**
- * @param Node $node
- * A match expressions's arms list (AST_MATCH_ARM_LIST) node to analyze
- * @override
- * @suppress PhanPossiblyUndeclaredProperty
- */
- public function visitMatchArmList(Node $node): void
- {
- $children = $node->children;
- if (!$children) {
- // This plugin will never emit errors if there are 0 elements.
- return;
- }
-
- $arm_expr_constant_set = [];
- foreach ($children as $arm_node) {
- foreach ($arm_node->children['cond']->children ?? [] as $arm_expr_cond) {
- if ($arm_expr_cond === null) {
- continue; // This is `default:`. php --syntax-check already checks for duplicates.
- }
- $lineno = $arm_expr_cond->lineno ?? $arm_node->lineno;
- // Skip array entries without literal keys. (Do it before resolving the key value)
- if (is_object($arm_expr_cond) && ParseVisitor::isConstExpr($arm_expr_cond, ParseVisitor::CONSTANT_EXPRESSION_FORBID_NEW_EXPRESSION)) {
- // Only infer the value for values not affected by conditions - that will change after the expressions are analyzed
- $original_cond = $arm_expr_cond;
- $arm_expr_cond = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $arm_expr_cond)->asSingleScalarValueOrNullOrSelf();
- if (is_object($arm_expr_cond)) {
- $arm_expr_cond = $original_cond;
- }
- }
- if (is_string($arm_expr_cond)) {
- $cond_key = "s$arm_expr_cond";
- } elseif (is_int($arm_expr_cond)) {
- $cond_key = $arm_expr_cond;
- } elseif (is_bool($arm_expr_cond)) {
- $cond_key = $arm_expr_cond ? "T" : "F";
- } else {
- // TODO: This seems like it'd be flaky with ast\Node->flags and lineno?
- $cond_key = ASTHasher::hash($arm_expr_cond);
- }
- if (isset($arm_expr_constant_set[$cond_key])) {
- $normalized_arm_expr_cond = ASTReverter::toShortString($arm_expr_cond);
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($lineno),
- 'PhanPluginDuplicateMatchArmExpression',
- "Duplicate match arm expression({STRING_LITERAL}) detected in match expression - the later entry will be ignored in favor of expression {CODE} at line {LINE}.",
- [$normalized_arm_expr_cond, ASTReverter::toShortString($arm_expr_constant_set[$cond_key][0]), $arm_expr_constant_set[$cond_key][1]],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15071
- );
- }
- $arm_expr_constant_set[$cond_key] = [$arm_expr_cond, $arm_node->lineno];
- }
- }
- }
-
- /**
- * @param Node $node
- * An array literal(AST_ARRAY) node to analyze
- * @override
- */
- public function visitArray(Node $node): void
- {
- $children = $node->children;
- if (count($children) <= 1) {
- // This plugin will never emit errors if there are 0 or 1 elements.
- return;
- }
-
- $has_entry_without_key = false;
- $key_set = [];
- foreach ($children as $entry) {
- if (!($entry instanceof Node)) {
- continue; // Triggered by code such as `list(, $a) = $expr`. In php 7.1, the array and list() syntax was unified.
- }
- $key = $entry->children['key'] ?? null;
- // Skip array entries without literal keys. (Do it before resolving the key value)
- if (is_null($key)) {
- $has_entry_without_key = true;
- continue;
- }
- if (is_object($key)) {
- $key = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $key)->asSingleScalarValueOrNullOrSelf();
- if (is_object($key)) {
- $key = self::HASH_PREFIX . ASTHasher::hash($entry->children['key']);
- }
- }
-
- if (isset($key_set[$key])) {
- // @phan-suppress-next-line PhanTypeMismatchDimFetchNullable
- $this->warnAboutDuplicateArrayKey($entry, $key, $key_set[$key]);
- }
- // @phan-suppress-next-line PhanTypeMismatchDimAssignment
- $key_set[$key] = $entry;
- }
- if ($has_entry_without_key && count($key_set) > 0) {
- // This is probably a typo in most codebases. (e.g. ['foo' => 'bar', 'baz'])
- // In phan, InternalFunctionSignatureMap.php does this deliberately with the first parameter being the return type.
- $this->emit(
- 'PhanPluginMixedKeyNoKey',
- "Should not mix array entries of the form [key => value,] with entries of the form [value,].",
- [],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15071
- );
- }
- }
-
- /**
- * @param int|string|float|bool|null $key
- */
- private function warnAboutDuplicateArrayKey(Node $entry, $key, Node $old_entry): void
- {
- if (is_string($key) && strncmp($key, self::HASH_PREFIX, strlen(self::HASH_PREFIX)) === 0) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($entry->lineno),
- 'PhanPluginDuplicateArrayKeyExpression',
- "Duplicate dynamic array key expression ({CODE}) detected in array - the earlier entry at line {LINE} will be ignored if the expression had the same value.",
- [ASTReverter::toShortString($entry->children['key']), $old_entry->lineno],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15071
- );
- return;
- }
- $normalized_key = self::normalizeKey($key);
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($entry->lineno),
- 'PhanPluginDuplicateArrayKey',
- "Duplicate/Equivalent array key value({STRING_LITERAL}) detected in array - the earlier entry {CODE} at line {LINE} will be ignored.",
- [$normalized_key, ASTReverter::toShortString($old_entry->children['key']), $old_entry->lineno],
- Issue::SEVERITY_NORMAL,
- Issue::REMEDIATION_A,
- 15071
- );
- }
-
- /**
- * Converts a key to the value it would be if used as a case.
- * E.g. 0, 0.5, and "0" all become the same value(0) when used as an array key.
- *
- * @param int|string|float|bool|null $key - The array key literal to be normalized.
- * @return string - The normalized representation.
- */
- private static function normalizeSwitchKey($key): string
- {
- if (is_int($key)) {
- return (string)$key;
- } elseif (!is_string($key)) {
- return (string)json_encode($key);
- }
- $tmp = [$key => true];
- return ASTReverter::toShortString(key($tmp));
- }
-
- /**
- * Converts a key to the value it would be if used as an array key.
- * E.g. 0, 0.5, and "0" all become the same value(0) when used as an array key.
- *
- * @param int|string|float|bool|null $key - The array key literal to be normalized.
- * @return string - The normalized representation.
- */
- private static function normalizeKey($key): string
- {
- if (is_int($key)) {
- return (string)$key;
- }
- $tmp = [$key => true];
- return ASTReverter::toShortString(key($tmp));
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new DuplicateArrayKeyPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DuplicateConstantPlugin.php b/vendor/phan/phan/.phan/plugins/DuplicateConstantPlugin.php
deleted file mode 100644
index 056727ab..00000000
--- a/vendor/phan/phan/.phan/plugins/DuplicateConstantPlugin.php
+++ /dev/null
@@ -1,114 +0,0 @@
-children) <= 1) {
- return;
- }
- $declarations = [];
-
- foreach ($node->children as $child) {
- if (!$child instanceof Node) {
- continue;
- }
- if ($child->kind === ast\AST_CONST_DECL) {
- foreach ($child->children as $const) {
- if (!$const instanceof Node) {
- continue;
- }
- $name = (string) $const->children['name'];
- if (isset($declarations[$name])) {
- $this->warnDuplicateConstant($name, $declarations[$name], $const);
- } else {
- $declarations[$name] = $const;
- }
- }
- } elseif ($child->kind === ast\AST_CALL) {
- $expr = $child->children['expr'];
- if ($expr instanceof Node && $expr->kind === ast\AST_NAME && strcasecmp((string) $expr->children['name'], 'define') === 0) {
- $name = $child->children['args']->children[0] ?? null;
- if (is_string($name)) {
- if (isset($declarations[$name])) {
- $this->warnDuplicateConstant($name, $declarations[$name], $expr);
- } else {
- $declarations[$name] = $expr;
- }
- }
- }
- }
- }
- }
-
- private function warnDuplicateConstant(string $name, Node $original_def, Node $new_def): void
- {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($new_def->lineno),
- 'PhanPluginDuplicateConstant',
- 'Constant {CONST} was previously declared at line {LINE} - the previous declaration will be used instead',
- [$name, $original_def->lineno]
- );
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new DuplicateConstantPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/DuplicateExpressionPlugin.php b/vendor/phan/phan/.phan/plugins/DuplicateExpressionPlugin.php
deleted file mode 100644
index ff451ba8..00000000
--- a/vendor/phan/phan/.phan/plugins/DuplicateExpressionPlugin.php
+++ /dev/null
@@ -1,575 +0,0 @@
- true,
- flags\BINARY_BOOL_OR => true,
- flags\BINARY_BOOL_XOR => true,
- flags\BINARY_BITWISE_OR => true,
- flags\BINARY_BITWISE_AND => true,
- flags\BINARY_BITWISE_XOR => true,
- flags\BINARY_SUB => true,
- flags\BINARY_DIV => true,
- flags\BINARY_MOD => true,
- flags\BINARY_IS_IDENTICAL => true,
- flags\BINARY_IS_NOT_IDENTICAL => true,
- flags\BINARY_IS_EQUAL => true,
- flags\BINARY_IS_NOT_EQUAL => true,
- flags\BINARY_IS_SMALLER => true,
- flags\BINARY_IS_SMALLER_OR_EQUAL => true,
- flags\BINARY_IS_GREATER => true,
- flags\BINARY_IS_GREATER_OR_EQUAL => true,
- flags\BINARY_SPACESHIP => true,
- flags\BINARY_COALESCE => true,
- ];
-
- /**
- * A subset of REDUNDANT_BINARY_OP_SET.
- *
- * These binary operations will make this plugin warn if both sides are literals.
- */
- private const BINARY_OP_BOTH_LITERAL_WARN_SET = [
- flags\BINARY_BOOL_AND => true,
- flags\BINARY_BOOL_OR => true,
- flags\BINARY_BOOL_XOR => true,
- flags\BINARY_IS_IDENTICAL => true,
- flags\BINARY_IS_NOT_IDENTICAL => true,
- flags\BINARY_IS_EQUAL => true,
- flags\BINARY_IS_NOT_EQUAL => true,
- flags\BINARY_IS_SMALLER => true,
- flags\BINARY_IS_SMALLER_OR_EQUAL => true,
- flags\BINARY_IS_GREATER => true,
- flags\BINARY_IS_GREATER_OR_EQUAL => true,
- flags\BINARY_SPACESHIP => true,
- flags\BINARY_COALESCE => true,
- ];
-
- /**
- * @param Node $node
- * A binary operation node to analyze
- * @override
- * @suppress PhanAccessClassConstantInternal
- */
- public function visitBinaryOp(Node $node): void
- {
- $flags = $node->flags;
- if (!\array_key_exists($flags, self::REDUNDANT_BINARY_OP_SET)) {
- // Nothing to warn about
- return;
- }
- $left = $node->children['left'];
- $right = $node->children['right'];
- if (ASTHasher::hash($left) === ASTHasher::hash($right)) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateExpressionBinaryOp',
- 'Both sides of the binary operator {OPERATOR} are the same: {CODE}',
- [
- PostOrderAnalysisVisitor::NAME_FOR_BINARY_OP[$node->flags],
- ASTReverter::toShortString($left),
- ]
- );
- return;
- }
- if (!\array_key_exists($flags, self::BINARY_OP_BOTH_LITERAL_WARN_SET)) {
- return;
- }
- if ($left instanceof Node) {
- $left = self::resolveLiteralValue($left);
- if ($left instanceof Node) {
- return;
- }
- }
- if ($right instanceof Node) {
- $right = self::resolveLiteralValue($right);
- if ($right instanceof Node) {
- return;
- }
- }
- try {
- // @phan-suppress-next-line PhanPartialTypeMismatchArgument TODO: handle
- $result_representation = ASTReverter::toShortString(InferValue::computeBinaryOpResult($left, $right, $flags));
- } catch (Error $_) {
- $result_representation = '(unknown)';
- }
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginBothLiteralsBinaryOp',
- 'Suspicious usage of a binary operator where both operands are literals. Expression: {CODE} {OPERATOR} {CODE} (result is {CODE})',
- [
- ASTReverter::toShortString($left),
- PostOrderAnalysisVisitor::NAME_FOR_BINARY_OP[$flags],
- ASTReverter::toShortString($right),
- $result_representation,
- ]
- );
- }
-
- /**
- * @param Node $node
- * An assignment operation node to analyze
- * @override
- */
- public function visitAssignRef(Node $node): void
- {
- $this->visitAssign($node);
- }
-
- private const ASSIGN_OP_FLAGS = [
- flags\BINARY_BITWISE_OR => '|',
- flags\BINARY_BITWISE_AND => '&',
- flags\BINARY_BITWISE_XOR => '^',
- flags\BINARY_CONCAT => '.',
- flags\BINARY_ADD => '+',
- flags\BINARY_SUB => '-',
- flags\BINARY_MUL => '*',
- flags\BINARY_DIV => '/',
- flags\BINARY_MOD => '%',
- flags\BINARY_POW => '**',
- flags\BINARY_SHIFT_LEFT => '<<',
- flags\BINARY_SHIFT_RIGHT => '>>',
- flags\BINARY_COALESCE => '??',
- ];
-
- /**
- * @param Node $node
- * An assignment operation node to analyze
- * @override
- */
- public function visitAssign(Node $node): void
- {
- $expr = $node->children['expr'];
- if (!$expr instanceof Node) {
- // Guaranteed not to contain duplicate expressions in valid php assignments.
- return;
- }
- $var = $node->children['var'];
- if ($expr->kind === ast\AST_BINARY_OP) {
- $op_str = self::ASSIGN_OP_FLAGS[$expr->flags] ?? null;
- if (is_string($op_str) && ASTHasher::hash($var) === ASTHasher::hash($expr->children['left'])) {
- $message = 'Can simplify this assignment to {CODE} {OPERATOR} {CODE}';
- if ($expr->flags === ast\flags\BINARY_COALESCE) {
- if (Config::get_closest_minimum_target_php_version_id() < 70400) {
- return;
- }
- $message .= ' (requires php version 7.4 or newer)';
- }
-
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateExpressionAssignmentOperation',
- $message,
- [
- ASTReverter::toShortString($var),
- $op_str . '=',
- ASTReverter::toShortString($expr->children['right']),
- ]
- );
- }
- return;
- }
- if (ASTHasher::hash($var) === ASTHasher::hash($expr)) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateExpressionAssignment',
- 'Both sides of the assignment {OPERATOR} are the same: {CODE}',
- [
- $node->kind === ast\AST_ASSIGN_REF ? '=&' : '=',
- ASTReverter::toShortString($var),
- ]
- );
- return;
- }
- }
-
- /**
- * @return bool|null|Node the resolved value of $node, or $node if it could not be resolved
- * This could be more permissive about what constants are allowed (e.g. user-defined constants, real constants like PI, etc.),
- * but that may cause more false positives.
- */
- private static function resolveLiteralValue(Node $node)
- {
- if ($node->kind !== ast\AST_CONST) {
- return $node;
- }
- // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal
- switch (\strtolower($node->children['name']->children['name'] ?? null)) {
- case 'false':
- return false;
- case 'true':
- return true;
- case 'null':
- return null;
- default:
- return $node;
- }
- }
-
- /**
- * @param Node $node
- * A binary operation node to analyze
- * @override
- */
- public function visitConditional(Node $node): void
- {
- $cond_node = $node->children['cond'];
- $true_node_hash = ASTHasher::hash($node->children['true']);
-
- if (ASTHasher::hash($cond_node) === $true_node_hash) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateConditionalTernaryDuplication',
- '"X ? X : Y" can usually be simplified to "X ?: Y". The duplicated expression X was {CODE}',
- [ASTReverter::toShortString($cond_node)]
- );
- return;
- }
- $false_node_hash = ASTHasher::hash($node->children['false']);
- if ($true_node_hash === $false_node_hash) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateConditionalUnnecessary',
- '"X ? Y : Y" results in the same expression Y no matter what X evaluates to. Y was {CODE}',
- [ASTReverter::toShortString($cond_node)]
- );
- return;
- }
-
- if (!$cond_node instanceof Node) {
- return;
- }
- switch ($cond_node->kind) {
- case ast\AST_ISSET:
- if (ASTHasher::hash($cond_node->children['var']) === $true_node_hash) {
- $this->warnDuplicateConditionalNullCoalescing('isset(X) ? X : Y', $node->children['true']);
- }
- break;
- case ast\AST_BINARY_OP:
- $this->checkBinaryOpOfConditional($cond_node, $true_node_hash);
- break;
- case ast\AST_UNARY_OP:
- $this->checkUnaryOpOfConditional($cond_node, $true_node_hash);
- break;
- }
- }
-
- /**
- * @param Node $node
- * A statement list of kind ast\AST_STMT_LIST to analyze.
- * @override
- */
- public function visitStmtList(Node $node): void
- {
- $children = $node->children;
- if (count($children) < 2) {
- return;
- }
- $prev_hash = null;
- foreach ($children as $child) {
- $hash = ASTHasher::hash($child);
- if ($hash === $prev_hash) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($child->lineno ?? $node->lineno),
- 'PhanPluginDuplicateAdjacentStatement',
- "Statement {CODE} is a duplicate of the statement on the above line. Suppress this issue instance if there's a good reason for this.",
- [ASTReverter::toShortString($child)]
- );
- }
- $prev_hash = $hash;
- }
- }
-
- /**
- * @param int|string $true_node_hash
- */
- private function checkBinaryOpOfConditional(Node $cond_node, $true_node_hash): void
- {
- if ($cond_node->flags !== ast\flags\BINARY_IS_NOT_IDENTICAL) {
- return;
- }
- $left_node = $cond_node->children['left'];
- $right_node = $cond_node->children['right'];
- if (self::isNullConstantNode($left_node)) {
- if (ASTHasher::hash($right_node) === $true_node_hash) {
- $this->warnDuplicateConditionalNullCoalescing('null !== X ? X : Y', $right_node);
- }
- } elseif (self::isNullConstantNode($right_node)) {
- if (ASTHasher::hash($left_node) === $true_node_hash) {
- $this->warnDuplicateConditionalNullCoalescing('X !== null ? X : Y', $left_node);
- }
- }
- }
-
- /**
- * @param int|string $true_node_hash
- */
- private function checkUnaryOpOfConditional(Node $cond_node, $true_node_hash): void
- {
- if ($cond_node->flags !== ast\flags\UNARY_BOOL_NOT) {
- return;
- }
- $expr = $cond_node->children['expr'];
- if (!$expr instanceof Node) {
- return;
- }
- if ($expr->kind === ast\AST_CALL) {
- $function = $expr->children['expr'];
- if (!$function instanceof Node ||
- $function->kind !== ast\AST_NAME ||
- strcasecmp((string)($function->children['name'] ?? ''), 'is_null') !== 0
- ) {
- return;
- }
- $args = $expr->children['args']->children;
- if (count($args) !== 1) {
- return;
- }
- if (ASTHasher::hash($args[0]) === $true_node_hash) {
- $this->warnDuplicateConditionalNullCoalescing('!is_null(X) ? X : Y', $args[0]);
- }
- }
- }
-
- /**
- * @param Node|mixed $node
- */
- private static function isNullConstantNode($node): bool
- {
- if (!$node instanceof Node) {
- return false;
- }
- return $node->kind === ast\AST_CONST && strcasecmp((string)($node->children['name']->children['name'] ?? ''), 'null') === 0;
- }
-
- /**
- * @param ?(Node|string|int|float) $x_node
- */
- private function warnDuplicateConditionalNullCoalescing(string $expr, $x_node): void
- {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginDuplicateConditionalNullCoalescing',
- '"' . $expr . '" can usually be simplified to "X ?? Y" in PHP 7. The duplicated expression X was {CODE}',
- [ASTReverter::toShortString($x_node)]
- );
- }
-}
-
-/**
- * This visitor analyzes node kinds that can be the root of expressions
- * containing duplicate expressions, and is called on nodes in pre-order.
- */
-class RedundantNodePreAnalysisVisitor extends PluginAwarePreAnalysisVisitor
-{
- /**
- * @override
- */
- public function visitIf(Node $node): void
- {
- if (count($node->children) <= 1) {
- // There can't be any duplicates.
- return;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- if (isset($node->is_inside_else)) {
- return;
- }
- $children = self::extractIfElseifChain($node);
- // The checks of visitIf are done in pre-order (parent nodes analyzed before child nodes)
- // so that checked_duplicate_if can be set, to avoid redundant work.
- // @phan-suppress-next-line PhanUndeclaredProperty
- if (isset($node->checked_duplicate_if)) {
- return;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- $node->checked_duplicate_if = true;
- ['cond' => $prev_cond /*, 'stmts' => $prev_stmts */] = $children[0]->children;
- // $prev_stmts_hash = ASTHasher::hash($prev_cond);
- $condition_set = [ASTHasher::hash($prev_cond) => true];
- $N = count($children);
- for ($i = 1; $i < $N; $i++) {
- ['cond' => $cond /*, 'stmts' => $stmts */] = $children[$i]->children;
- $cond_hash = ASTHasher::hash($cond);
- if (isset($condition_set[$cond_hash])) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($cond->lineno ?? $children[$i]->lineno),
- 'PhanPluginDuplicateIfCondition',
- 'Saw the same condition {CODE} in an earlier if/elseif statement',
- [ASTReverter::toShortString($cond)]
- );
- } else {
- $condition_set[$cond_hash] = true;
- }
- }
- if (!isset($cond)) {
- $stmts = $children[$N - 1]->children['stmts'];
- if (($stmts->children ?? null) && ASTHasher::hash($stmts) === ASTHasher::hash($children[$N - 2]->children['stmts'])) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($children[$N - 1]->lineno),
- 'PhanPluginDuplicateIfStatements',
- 'The statements of the else duplicate the statements of the previous if/elseif statement with condition {CODE}',
- [ASTReverter::toShortString($children[$N - 2]->children['cond'])]
- );
- }
- }
- }
-
- /**
- * Visit a node of kind ast\AST_TRY, to check for adjacent catch blocks
- *
- * @override
- * @suppress PhanPossiblyUndeclaredProperty
- */
- public function visitTry(Node $node): void
- {
- if (Config::get_closest_target_php_version_id() < 70100) {
- return;
- }
- $catches = $node->children['catches']->children ?? [];
- $n = count($catches);
- if ($n <= 1) {
- // There can't be any duplicates.
- return;
- }
- $prev_hash = ASTHasher::hash($catches[0]->children['stmts']) . ASTHasher::hash($catches[0]->children['var']);
- for ($i = 1; $i < $n; $prev_hash = $cur_hash, $i++) {
- $cur_hash = ASTHasher::hash($catches[$i]->children['stmts']) . ASTHasher::hash($catches[$i]->children['var']);
- if ($prev_hash === $cur_hash) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($catches[$i]->lineno),
- 'PhanPluginDuplicateCatchStatementBody',
- 'The implementation of catch({CODE}) and catch({CODE}) are identical, and can be combined if the application only needs to supports php 7.1 and newer',
- [
- ASTReverter::toShortString($catches[$i - 1]->children['class']),
- ASTReverter::toShortString($catches[$i]->children['class']),
- ]
- );
- }
- }
- }
-
- /**
- * @param Node $node a node of kind ast\AST_IF
- * @return list the list of AST_IF_ELEM nodes making up the chain of if/elseif/else if conditions.
- * @suppress PhanPartialTypeMismatchReturn
- */
- private static function extractIfElseifChain(Node $node): array
- {
- $children = $node->children;
- if (count($children) <= 1) {
- return $children;
- }
- $last_child = \end($children);
- // Loop over the `} else {` blocks.
- // @phan-suppress-next-line PhanPossiblyUndeclaredProperty
- while ($last_child->children['cond'] === null) {
- $first_stmt = $last_child->children['stmts']->children[0] ?? null;
- if (!($first_stmt instanceof Node)) {
- break;
- }
- if ($first_stmt->kind !== ast\AST_IF) {
- break;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- $first_stmt->is_inside_else = true;
- \array_pop($children);
- $if_elems = $first_stmt->children;
- foreach ($if_elems as $elem) {
- $children[] = $elem;
- }
- $last_child = \end($children);
- }
- return $children;
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-
-return new DuplicateExpressionPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/EmptyMethodAndFunctionPlugin.php b/vendor/phan/phan/.phan/plugins/EmptyMethodAndFunctionPlugin.php
deleted file mode 100644
index bf920bf3..00000000
--- a/vendor/phan/phan/.phan/plugins/EmptyMethodAndFunctionPlugin.php
+++ /dev/null
@@ -1,125 +0,0 @@
-children['stmts'] ?? null;
-
- if (!$stmts_node || $stmts_node->children) {
- return;
- }
- $method = $this->context->getFunctionLikeInScope($this->code_base);
- if (!($method instanceof Method)) {
- throw new AssertionError("Expected $method to be a method");
- }
- if ($method->isNewConstructor()) {
- foreach ($node->children['params']->children as $param) {
- if ($param instanceof Node && ($param->flags & Parameter::PARAM_MODIFIER_VISIBILITY_FLAGS)) {
- // This uses constructor property promotion
- return;
- }
- }
- }
-
- if (!$method->isOverriddenByAnother()
- && !$method->isOverride()
- && !$method->isDeprecated()
- ) {
- $this->emitIssue(
- self::getIssueTypeForEmptyMethod($method),
- $node->lineno,
- $method->getRepresentationForIssue()
- );
- }
- }
-
- public function visitFuncDecl(Node $node): void
- {
- $this->analyzeFunction($node);
- }
-
- public function visitClosure(Node $node): void
- {
- $this->analyzeFunction($node);
- }
-
- // No need for visitArrowFunc.
- // By design, `fn($args) => expr` can't have an empty statement list because it must have an expression.
- // It's always equivalent to `return expr;`
-
- private function analyzeFunction(Node $node): void
- {
- $stmts_node = $node->children['stmts'] ?? null;
-
- if ($stmts_node && !$stmts_node->children) {
- $function = $this->context->getFunctionLikeInScope($this->code_base);
- if (!($function instanceof Func)) {
- throw new AssertionError("Expected $function to be Func\n");
- }
-
- if (!$function->isDeprecated()) {
- $this->emitIssue(
- $function->isClosure() ? Issue::EmptyClosure : Issue::EmptyFunction,
- $node->lineno,
- $function->getRepresentationForIssue()
- );
- }
- }
- }
-
- private static function getIssueTypeForEmptyMethod(FunctionInterface $method): string
- {
- if (!$method instanceof Method) {
- throw new \InvalidArgumentException("\$method is not an instance of Method");
- }
-
- if ($method->isPrivate()) {
- return Issue::EmptyPrivateMethod;
- }
-
- if ($method->isProtected()) {
- return Issue::EmptyProtectedMethod;
- }
-
- return Issue::EmptyPublicMethod;
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new EmptyMethodAndFunctionPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/EmptyStatementListPlugin.php b/vendor/phan/phan/.phan/plugins/EmptyStatementListPlugin.php
deleted file mode 100644
index 1f3900b9..00000000
--- a/vendor/phan/phan/.phan/plugins/EmptyStatementListPlugin.php
+++ /dev/null
@@ -1,370 +0,0 @@
- set by plugin framework
- * @suppress PhanReadOnlyProtectedProperty
- */
- protected $parent_node_list;
-
- /**
- * @param Node $node
- * A node to analyze
- * @override
- */
- public function visitIf(Node $node): void
- {
- // @phan-suppress-next-line PhanUndeclaredProperty set by ASTSimplifier
- if (isset($node->is_simplified)) {
- $first_child = end($node->children);
- if (!$first_child instanceof Node || $first_child->children['cond'] === null) {
- return;
- }
- $last_if_elem = reset($node->children);
- } else {
- $last_if_elem = end($node->children);
- }
- if (!$last_if_elem instanceof Node) {
- // probably impossible
- return;
- }
- $stmts_node = $last_if_elem->children['stmts'];
- if (!$stmts_node instanceof Node) {
- // probably impossible
- return;
- }
- if ($stmts_node->children) {
- // the last if element has statements
- return;
- }
- if ($last_if_elem->children['cond'] === null) {
- // Don't bother warning about else
- return;
- }
- if ($this->hasTODOComment($stmts_node->lineno, $node)) {
- // Don't warn if there is a FIXME/TODO comment in/around the empty statement list
- return;
- }
-
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($last_if_elem->children['stmts']->lineno ?? $last_if_elem->lineno),
- 'PhanPluginEmptyStatementIf',
- 'Empty statement list statement detected for the last if/elseif statement',
- []
- );
- }
-
- private function hasTODOComment(int $lineno, Node $analyzed_node, ?int $end_lineno = null): bool
- {
- if (EmptyStatementListPlugin::$ignore_todos) {
- return false;
- }
- $file = FileCache::getOrReadEntry($this->context->getFile());
- $lines = $file->getLines();
- $end_lineno = max($lineno, $end_lineno ?? $this->findEndLine($lineno, $analyzed_node));
- for ($i = $lineno; $i <= $end_lineno; $i++) {
- $line = $lines[$i] ?? null;
- if (!is_string($line)) {
- break;
- }
- if (preg_match('/todo|fixme|deliberately empty/i', $line) > 0) {
- return true;
- }
- }
- return false;
- }
-
- private function findEndLine(int $lineno, Node $search_node): int
- {
- for ($node_index = count($this->parent_node_list) - 1; $node_index >= 0; $node_index--) {
- $node = $this->parent_node_list[$node_index] ?? null;
- if (!$node) {
- continue;
- }
- if (isset($node->endLineno)) {
- // Return the end line of the function declaration.
- return $node->endLineno;
- }
- if ($node->kind === ast\AST_STMT_LIST) {
- foreach ($node->children as $i => $c) {
- if ($c === $search_node) {
- $next_node = $node->children[$i + 1] ?? null;
- if ($next_node instanceof Node) {
- return $next_node->lineno - 1;
- }
- break;
- }
- }
- }
- $search_node = $node;
- }
- // Give up and guess.
- return $lineno + 5;
- }
-
- /**
- * @param Node $node
- * A node of kind ast\AST_FOR to analyze
- * @override
- */
- public function visitFor(Node $node): void
- {
- $stmts_node = $node->children['stmts'];
- if (!$stmts_node instanceof Node) {
- // impossible
- return;
- }
- if ($stmts_node->children || ($node->children['loop']->children ?? null)) {
- // the for loop has statements, in the body and/or in the loop condition.
- return;
- }
- if ($this->hasTODOComment($stmts_node->lineno, $node)) {
- // Don't warn if there is a FIXME/TODO comment in/around the empty statement list
- return;
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($stmts_node->lineno ?? $node->lineno),
- 'PhanPluginEmptyStatementForLoop',
- 'Empty statement list statement detected for the for loop',
- []
- );
- }
-
- /**
- * @param Node $node
- * A node to analyze
- * @override
- */
- public function visitWhile(Node $node): void
- {
- $stmts_node = $node->children['stmts'];
- if (!$stmts_node instanceof Node) {
- return; // impossible
- }
- if ($stmts_node->children) {
- // the while loop has statements
- return;
- }
- if ($this->hasTODOComment($stmts_node->lineno, $node)) {
- // Don't warn if there is a FIXME/TODO comment in/around the empty statement list
- return;
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($stmts_node->lineno ?? $node->lineno),
- 'PhanPluginEmptyStatementWhileLoop',
- 'Empty statement list statement detected for the while loop',
- []
- );
- }
-
- /**
- * @param Node $node
- * A node to analyze
- * @override
- */
- public function visitDoWhile(Node $node): void
- {
- $stmts_node = $node->children['stmts'];
- if (!$stmts_node instanceof Node) {
- return; // impossible
- }
- if ($stmts_node->children ?? null) {
- // the while loop has statements
- return;
- }
- if ($this->hasTODOComment($stmts_node->lineno, $node)) {
- // Don't warn if there is a FIXME/TODO comment in/around the empty statement list
- return;
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($stmts_node->lineno),
- 'PhanPluginEmptyStatementDoWhileLoop',
- 'Empty statement list statement detected for the do-while loop',
- []
- );
- }
-
- /**
- * @param Node $node
- * A node to analyze
- * @override
- */
- public function visitForeach(Node $node): void
- {
- $stmts_node = $node->children['stmts'];
- if (!$stmts_node instanceof Node) {
- // impossible
- return;
- }
- if ($stmts_node->children) {
- // the while loop has statements
- return;
- }
- if ($this->hasTODOComment($stmts_node->lineno, $node)) {
- // Don't warn if there is a FIXME/TODO comment in/around the empty statement list
- return;
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($stmts_node->lineno),
- 'PhanPluginEmptyStatementForeachLoop',
- 'Empty statement list statement detected for the foreach loop',
- []
- );
- }
-
- /**
- * @param Node $node
- * A node to analyze
- * @override
- */
- public function visitTry(Node $node): void
- {
- ['try' => $try_node, 'finally' => $finally_node] = $node->children;
- if (!$try_node->children) {
- if (!$this->hasTODOComment($try_node->lineno, $node, $node->children['catches']->children[0]->lineno ?? $finally_node->lineno ?? null)) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($try_node->lineno),
- 'PhanPluginEmptyStatementTryBody',
- 'Empty statement list statement detected for the try statement\'s body',
- []
- );
- }
- } elseif (InferPureAndNoThrowVisitor::isUnlikelyToThrow($this->code_base, $this->context, $try_node)) {
- if (!$this->hasTODOComment($try_node->lineno, $node, $node->children['catches']->children[0]->lineno ?? $finally_node->lineno ?? null)) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($try_node->lineno),
- 'PhanPluginEmptyStatementPossiblyNonThrowingTryBody',
- 'Found a try block that looks like it might not throw. Note that this check is a heuristic prone to false positives, especially because error handlers, signal handlers, destructors, and other things may all lead to throwing.'
- );
- }
- }
- if ($finally_node instanceof Node && !$finally_node->children) {
- if (!$this->hasTODOComment($finally_node->lineno, $node)) {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($finally_node->lineno),
- 'PhanPluginEmptyStatementTryFinally',
- 'Empty statement list statement detected for the try\'s finally body',
- []
- );
- }
- }
- }
-
- /**
- * @param Node $node
- * A node of kind ast\AST_SWITCH to analyze
- * @override
- */
- public function visitSwitch(Node $node): void
- {
- // Check all case statements and return if something that isn't a no-op is seen.
- foreach ($node->children['stmts']->children ?? [] as $c) {
- if (!$c instanceof Node) {
- // impossible
- continue;
- }
-
- $children = $c->children['stmts']->children ?? null;
- if ($children) {
- if (count($children) > 1) {
- return;
- }
- $only_node = $children[0];
- if ($only_node instanceof Node) {
- if (!in_array($only_node->kind, [ast\AST_CONTINUE, ast\AST_BREAK], true)) {
- return;
- }
- if (($only_node->children['depth'] ?? 1) !== 1) {
- // not a no-op
- return;
- }
- }
- }
- if (!ParseVisitor::isConstExpr($c->children['cond'], ParseVisitor::CONSTANT_EXPRESSION_FORBID_NEW_EXPRESSION)) {
- return;
- }
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($node->lineno),
- 'PhanPluginEmptyStatementSwitch',
- 'No side effects seen for any cases of this switch statement',
- []
- );
- }
-}
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new EmptyStatementListPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/FFIAnalysisPlugin.php b/vendor/phan/phan/.phan/plugins/FFIAnalysisPlugin.php
deleted file mode 100644
index d6d28f69..00000000
--- a/vendor/phan/phan/.phan/plugins/FFIAnalysisPlugin.php
+++ /dev/null
@@ -1,153 +0,0 @@
-children['var'];
- if (!($left instanceof Node)) {
- return;
- }
- if ($left->kind !== ast\AST_VAR) {
- return;
- }
- $var_name = $left->children['name'];
- if (!is_string($var_name)) {
- return;
- }
- $scope = $this->context->getScope();
- if (!$scope->hasVariableWithName($var_name)) {
- return;
- }
- $var = $scope->getVariableByName($var_name);
- $category = self::containsFFICDataType($var->getUnionType());
- if (!$category) {
- return;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- $node->is_ffi = $category;
- }
-
- public const PARTIALLY_FFI_CDATA = 1;
- public const ENTIRELY_FFI_CDATA = 2;
-
- /**
- * Check if the type contains FFI\CData
- */
- private static function containsFFICDataType(UnionType $union_type): int
- {
- foreach ($union_type->getTypeSet() as $type) {
- if (strcasecmp('\FFI', $type->getNamespace()) !== 0) {
- continue;
- }
- if (strcasecmp('CData', $type->getName()) !== 0) {
- continue;
- }
- if ($type->isNullable()) {
- return self::PARTIALLY_FFI_CDATA;
- }
- if ($union_type->typeCount() > 1) {
- return self::PARTIALLY_FFI_CDATA;
- }
- return self::ENTIRELY_FFI_CDATA;
- }
- return 0;
- }
-}
-
-/**
- * This visitor restores FFI\CData types after assignments if the original value was FFI\CData
- */
-class FFIPostAnalysisVisitor extends PluginAwarePostAnalysisVisitor
-{
- /**
- * @override
- */
- public function visitAssign(Node $node): void
- {
- // @phan-suppress-next-line PhanUndeclaredProperty
- if (isset($node->is_ffi)) {
- $this->analyzeFFIAssign($node);
- }
- }
-
- private function analyzeFFIAssign(Node $node): void
- {
- $var_name = $node->children['var']->children['name'] ?? null;
- if (!is_string($var_name)) {
- return;
- }
- $cdata_type = UnionType::fromFullyQualifiedPHPDocString('\FFI\CData');
- $scope = $this->context->getScope();
- // @phan-suppress-next-line PhanUndeclaredProperty
- if ($node->is_ffi !== FFIPreAnalysisVisitor::ENTIRELY_FFI_CDATA) {
- if ($scope->hasVariableWithName($var_name)) {
- $cdata_type = $cdata_type->withUnionType($scope->getVariableByName($var_name)->getUnionType());
- }
- }
- $this->context->getScope()->addVariable(
- new Variable($this->context, $var_name, $cdata_type, 0)
- );
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-
-return new FFIAnalysisPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/HasPHPDocPlugin.php b/vendor/phan/phan/.phan/plugins/HasPHPDocPlugin.php
deleted file mode 100644
index 42979520..00000000
--- a/vendor/phan/phan/.phan/plugins/HasPHPDocPlugin.php
+++ /dev/null
@@ -1,402 +0,0 @@
-isAnonymous()) {
- // Probably not useful in many cases to document a short anonymous class.
- return;
- }
- $doc_comment = $class->getDocComment();
- if (!StringUtil::isNonZeroLengthString($doc_comment)) {
- self::emitIssue(
- $code_base,
- $class->getContext(),
- 'PhanPluginNoCommentOnClass',
- 'Class {CLASS} has no doc comment',
- [$class->getFQSEN()]
- );
- return;
- }
- $description = MarkupDescription::extractDescriptionFromDocComment($class);
- if (!StringUtil::isNonZeroLengthString($description)) {
- if (strpos($doc_comment, '@deprecated') !== false) {
- return;
- }
- self::emitIssue(
- $code_base,
- $class->getContext(),
- 'PhanPluginDescriptionlessCommentOnClass',
- 'Class {CLASS} has no readable description: {STRING_LITERAL}',
- [$class->getFQSEN(), self::getDocCommentRepresentation($doc_comment)]
- );
- return;
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the function exists
- *
- * @param Func $function
- * A function being analyzed
- * @override
- */
- public function analyzeFunction(
- CodeBase $code_base,
- Func $function
- ): void {
- if ($function->isPHPInternal()) {
- // This isn't user-defined, there's no reason to warn or way to change it.
- return;
- }
- if ($function->isNSInternal($code_base)) {
- // (at)internal are internal to the library, and there's less of a need to document them
- return;
- }
- if ($function->isClosure()) {
- // Probably not useful in many cases to document a short closure passed to array_map, etc.
- return;
- }
- $doc_comment = $function->getDocComment();
- if (!StringUtil::isNonZeroLengthString($doc_comment)) {
- self::emitIssue(
- $code_base,
- $function->getContext(),
- "PhanPluginNoCommentOnFunction",
- "Function {FUNCTION} has no doc comment",
- [$function->getFQSEN()]
- );
- return;
- }
- $description = MarkupDescription::extractDescriptionFromDocComment($function);
- if (!StringUtil::isNonZeroLengthString($description)) {
- self::emitIssue(
- $code_base,
- $function->getContext(),
- "PhanPluginDescriptionlessCommentOnFunction",
- "Function {FUNCTION} has no readable description: {STRING_LITERAL}",
- [$function->getFQSEN(), self::getDocCommentRepresentation($doc_comment)]
- );
- return;
- }
- }
-
- /**
- * Encode the doc comment in a one-line form that can be used in Phan's issue message.
- * @internal
- */
- public static function getDocCommentRepresentation(string $doc_comment): string
- {
- return (string)json_encode(MarkupDescription::getDocCommentWithoutWhitespace($doc_comment), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
- }
-
- public static function getPostAnalyzeNodeVisitorClassName(): string
- {
- return (bool)(Config::getValue('plugin_config')['has_phpdoc_check_duplicates'] ?? false)
- ? DuplicatePHPDocCheckerPlugin::class
- : BasePHPDocCheckerPlugin::class;
- }
-}
-
-/** Infer property and class doc comments and warn */
-class BasePHPDocCheckerPlugin extends PluginAwarePostAnalysisVisitor
-{
- /** @return array{0:list,1:list} */
- public function visitClass(Node $node): array
- {
- $class = $this->context->getClassInScope($this->code_base);
- $property_descriptions = [];
- $method_descriptions = [];
- foreach ($node->children['stmts']->children ?? [] as $element) {
- if (!($element instanceof Node)) {
- throw new AssertionError("All properties of ast\AST_CLASS's statement list must be nodes, saw " . gettype($element));
- }
- switch ($element->kind) {
- case ast\AST_METHOD:
- $entry = $this->checkMethodDescription($class, $element);
- if ($entry) {
- $method_descriptions[] = $entry;
- }
- break;
- case ast\AST_PROP_GROUP:
- $entry = $this->checkPropGroupDescription($class, $element);
- if ($entry) {
- $property_descriptions[] = $entry;
- }
- break;
- }
- }
- return [$property_descriptions, $method_descriptions];
- }
-
- /**
- * @param Node $node a node of kind ast\AST_METHOD
- */
- private function checkMethodDescription(Clazz $class, Node $node): ?ClassElementEntry
- {
- $method_name = (string)$node->children['name'];
- $method = $class->getMethodByName($this->code_base, $method_name);
- if ($method->isMagic()) {
- // Ignore construct
- return null;
- }
- if ($method->isOverride()) {
- return null;
- }
- $method_filter = HasPHPDocPlugin::$method_filter;
- if (is_string($method_filter)) {
- $fqsen_string = ltrim((string)$method->getFQSEN(), '\\');
- if (preg_match($method_filter, $fqsen_string) > 0) {
- return null;
- }
- }
-
- $doc_comment = $method->getDocComment();
- if (!StringUtil::isNonZeroLengthString($doc_comment)) {
- $visibility_upper = ucfirst($method->getVisibilityName());
- self::emitPluginIssue(
- $this->code_base,
- $method->getContext(),
- "PhanPluginNoCommentOn{$visibility_upper}Method",
- "$visibility_upper method {METHOD} has no doc comment",
- [$method->getFQSEN()]
- );
- return null;
- }
- $description = MarkupDescription::extractDescriptionFromDocComment($method);
- if (!StringUtil::isNonZeroLengthString($description)) {
- $visibility_upper = ucfirst($method->getVisibilityName());
- self::emitPluginIssue(
- $this->code_base,
- $method->getContext(),
- "PhanPluginDescriptionlessCommentOn{$visibility_upper}Method",
- "$visibility_upper method {METHOD} has no readable description: {STRING_LITERAL}",
- [$method->getFQSEN(), HasPHPDocPlugin::getDocCommentRepresentation($doc_comment)]
- );
- return null;
- }
- return new ClassElementEntry($method, \trim(\preg_replace('/\s+/', ' ', $description)));
- }
-
- /**
- * @param Node $node a node of type ast\AST_PROP_GROUP
- */
- private function checkPropGroupDescription(Clazz $class, Node $node): ?ClassElementEntry
- {
- $property_name = $node->children['props']->children[0]->children['name'] ?? null;
- if (!is_string($property_name)) {
- return null;
- }
- $property = $class->getPropertyByName($this->code_base, $property_name);
- $doc_comment = $property->getDocComment();
- if (!StringUtil::isNonZeroLengthString($doc_comment)) {
- $visibility_upper = ucfirst($property->getVisibilityName());
- self::emitPluginIssue(
- $this->code_base,
- $property->getContext(),
- "PhanPluginNoCommentOn{$visibility_upper}Property",
- "$visibility_upper property {PROPERTY} has no doc comment",
- [$property->getRepresentationForIssue()]
- );
- return null;
- }
- // @phan-suppress-next-line PhanAccessMethodInternal
- $description = MarkupDescription::extractDocComment($doc_comment, Comment::ON_PROPERTY, null, true);
- if (!StringUtil::isNonZeroLengthString($description)) {
- $visibility_upper = ucfirst($property->getVisibilityName());
- self::emitPluginIssue(
- $this->code_base,
- $property->getContext(),
- "PhanPluginDescriptionlessCommentOn{$visibility_upper}Property",
- "$visibility_upper property {PROPERTY} has no readable description: {STRING_LITERAL}",
- [$property->getRepresentationForIssue(), HasPHPDocPlugin::getDocCommentRepresentation($doc_comment)]
- );
- return null;
- }
- return new ClassElementEntry($property, \trim(\preg_replace('/\s+/', ' ', $description)));
- }
-}
-
-/**
- * Describes a property group or a method node and the associated description
- * @phan-immutable
- * @internal
- */
-final class ClassElementEntry
-{
- /** @var ClassElement the element (or element group) */
- public $element;
- /** @var string the phpdoc description */
- public $description;
-
- public function __construct(ClassElement $element, string $description)
- {
- $this->element = $element;
- $this->description = $description;
- }
-}
-
-/**
- * Check if phpdoc of property groups and methods are duplicated
- * @internal
- */
-final class DuplicatePHPDocCheckerPlugin extends BasePHPDocCheckerPlugin
-{
- /** No-op */
- public function visitClass(Node $node): array
- {
- [$property_descriptions, $method_descriptions] = parent::visitClass($node);
- foreach (self::findGroups($property_descriptions) as $entries) {
- $first_entry = array_shift($entries);
- if (!$first_entry instanceof ClassElementEntry) {
- throw new AssertionError('Expected $entries of $property_descriptions to be a group of 1 or more entries');
- }
- $first_property = $first_entry->element;
- foreach ($entries as $entry) {
- $property = $entry->element;
- self::emitPluginIssue(
- $this->code_base,
- $property->getContext(),
- "PhanPluginDuplicatePropertyDescription",
- "Property {PROPERTY} has the same description as the property \${PROPERTY} on line {LINE}: {COMMENT}",
- [$property->getRepresentationForIssue(), $first_property->getName(), $first_property->getContext()->getLineNumberStart(), $first_entry->description]
- );
- }
- }
- foreach (self::findGroups($method_descriptions) as $entries) {
- $first_entry = array_shift($entries);
- if (!$first_entry instanceof ClassElementEntry) {
- throw new AssertionError('Expected $entries of $property_descriptions to be a group of 1 or more entries');
- }
- $first_method = $first_entry->element;
- foreach ($entries as $entry) {
- $method = $entry->element;
- self::emitPluginIssue(
- $this->code_base,
- $method->getContext(),
- "PhanPluginDuplicateMethodDescription",
- "Method {METHOD} has the same description as the method {METHOD} on line {LINE}: {COMMENT}",
- [$method->getRepresentationForIssue(), $first_method->getName() . '()', $first_method->getContext()->getLineNumberStart(), $first_entry->description]
- );
- }
- }
- return [$property_descriptions, $method_descriptions];
- }
-
- /**
- * @param list $values
- * @return array>
- */
- private static function findGroups(array $values): array
- {
- $result = [];
- foreach ($values as $v) {
- if ($v->element->isDeprecated()) {
- continue;
- }
- $result[$v->description][] = $v;
- }
- foreach ($result as $description => $keys) {
- if (count($keys) <= 1) {
- unset($result[$description]);
- }
- }
- return $result;
- }
-}
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new HasPHPDocPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/InlineHTMLPlugin.php b/vendor/phan/phan/.phan/plugins/InlineHTMLPlugin.php
deleted file mode 100644
index fcab2dcc..00000000
--- a/vendor/phan/phan/.phan/plugins/InlineHTMLPlugin.php
+++ /dev/null
@@ -1,178 +0,0 @@
- set of files that have echo statements */
- public static $file_set_to_analyze = [];
-
- /** @var ?string */
- private $whitelist_regex;
- /** @var ?string */
- private $blacklist_regex;
-
- public function __construct()
- {
- $plugin_config = Config::getValue('plugin_config');
- $this->whitelist_regex = $plugin_config['inline_html_whitelist_regex'] ?? null;
- $this->blacklist_regex = $plugin_config['inline_html_blacklist_regex'] ?? null;
- }
-
- private function shouldCheckFile(string $path): bool
- {
- if (is_string($this->blacklist_regex)) {
- if (CLI::isPathMatchedByRegex($this->blacklist_regex, $path)) {
- return false;
- }
- }
- if (is_string($this->whitelist_regex)) {
- return CLI::isPathMatchedByRegex($this->whitelist_regex, $path);
- }
- return true;
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the node exists
- *
- * @param Context $context @phan-unused-param
- * A context with the file name for $file_contents and the scope after analyzing $node.
- *
- * @param string $file_contents the unmodified file contents @phan-unused-param
- * @param Node $node the node @phan-unused-param
- * @override
- * @throws Error if a process fails to shut down
- */
- public function afterAnalyzeFile(
- CodeBase $code_base,
- Context $context,
- string $file_contents,
- Node $node
- ): void {
- $file = $context->getFile();
- if (!isset(self::$file_set_to_analyze[$file])) {
- // token_get_all is noticeably slow when there are a lot of files, so we check for the existence of echo statements in the parsed AST as a heuristic to avoid calling token_get_all.
- return;
- }
- if (!self::shouldCheckFile($file)) {
- return;
- }
- $file_contents = Parser::removeShebang($file_contents);
- $tokens = token_get_all($file_contents);
- foreach ($tokens as $i => $token) {
- if (!is_array($token)) {
- continue;
- }
- if ($token[0] !== T_INLINE_HTML) {
- continue;
- }
- $N = count($tokens);
- $this->warnAboutInlineHTML($code_base, $context, $token, $i, $N);
- if ($i < $N - 1) {
- // Make sure to always check if the last token is inline HTML
- $token = $tokens[$N - 1] ?? null;
- if (!is_array($token)) {
- break;
- }
- if ($token[0] !== T_INLINE_HTML) {
- break;
- }
- $this->warnAboutInlineHTML($code_base, $context, $token, $N - 1, $N);
- }
- break;
- }
- }
-
- /**
- * @param array{0:int,1:string,2:int} $token a token from token_get_all
- */
- private function warnAboutInlineHTML(CodeBase $code_base, Context $context, array $token, int $i, int $n): void
- {
- if ($i === 0) {
- $issue = self::InlineHTMLLeading;
- $message = 'Saw inline HTML at the start of the file: {STRING_LITERAL}';
- } elseif ($i >= $n - 1) {
- $issue = self::InlineHTMLTrailing;
- $message = 'Saw inline HTML at the end of the file: {STRING_LITERAL}';
- } else {
- $issue = self::InlineHTML;
- $message = 'Saw inline HTML between the first and last token: {STRING_LITERAL}';
- }
- $this->emitIssue(
- $code_base,
- (clone $context)->withLineNumberStart($token[2]),
- $issue,
- $message,
- [StringUtil::jsonEncode(self::truncate($token[1]))]
- );
- }
-
- private static function truncate(string $token): string
- {
- if (strlen($token) > 20) {
- return mb_substr($token, 0, 20) . "...";
- }
- return $token;
- }
-
- /**
- * @return string - name of PluginAwarePostAnalysisVisitor subclass
- *
- * @override
- */
- public static function getPostAnalyzeNodeVisitorClassName(): string
- {
- return InlineHTMLVisitor::class;
- }
-}
-
-/**
- * Records existence of AST_ECHO within a file, marking the file as one that should be checked.
- *
- * php-ast (and the underlying AST implementation) doesn't provide a way to distinguish inline HTML from other types of echos.
- */
-class InlineHTMLVisitor extends PluginAwarePostAnalysisVisitor
-{
- /**
- * @override
- * @param Node $node @unused-param
- * @return void
- */
- public function visitEcho(Node $node)
- {
- InlineHTMLPlugin::$file_set_to_analyze[$this->context->getFile()] = true;
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-if (!function_exists('token_get_all')) {
- throw new UnloadablePluginException("InlineHTMLPlugin requires the tokenizer extension, which is not enabled (this plugin uses token_get_all())");
-}
-return new InlineHTMLPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/InvalidVariableIssetPlugin.php b/vendor/phan/phan/.phan/plugins/InvalidVariableIssetPlugin.php
deleted file mode 100644
index 648c73eb..00000000
--- a/vendor/phan/phan/.phan/plugins/InvalidVariableIssetPlugin.php
+++ /dev/null
@@ -1,127 +0,0 @@
-children['var'];
- $variable = $argument;
-
- // get variable name from argument
- while (!isset($variable->children['name'])) {
- if (!$variable instanceof Node) {
- // e.g. 'foo' in `isset('foo'[$i])` or `isset('foo'->bar)`.
- $this->emit(
- 'PhanPluginInvalidVariableIsset',
- "Unexpected expression in isset()",
- []
- );
- return $this->context;
- }
- if (in_array($variable->kind, self::EXPRESSIONS, true)) {
- $variable = $variable->children['expr'];
- } elseif (in_array($variable->kind, self::CLASSES, true)) {
- $variable = $variable->children['class'];
- } else {
- return $this->context;
- }
- }
- if (!$variable instanceof Node) {
- $this->emit(
- 'PhanPluginUnexpectedExpressionIsset',
- "Unexpected expression in isset()",
- []
- );
- return $this->context;
- }
- $name = $variable->children['name'] ?? null;
-
- // emit issue if name is not declared
- // Check for edge cases such as isset($$var)
- if (is_string($name)) {
- if ($variable->kind !== ast\AST_VAR) {
- // e.g. ast\AST_NAME of an ast\AST_CONST
- return $this->context;
- }
- if (!Variable::isHardcodedVariableInScopeWithName($name, $this->context->isInGlobalScope())
- && !$this->context->getScope()->hasVariableWithName($name)
- && !(
- $this->context->isInGlobalScope() && Config::getValue('ignore_undeclared_variables_in_global_scope')
- )
- ) {
- $this->emit(
- 'PhanPluginUndeclaredVariableIsset',
- 'undeclared variable ${VARIABLE} in isset()',
- [$name]
- );
- }
- } elseif ($variable->kind !== ast\AST_VAR) {
- // emit issue if argument is not array access
- $this->emit(
- 'PhanPluginInvalidVariableIsset',
- "non array/property access in isset()",
- []
- );
- return $this->context;
- } elseif (!is_string($name)) {
- // emit issue if argument is not array access
- $this->emit(
- 'PhanPluginComplexVariableInIsset',
- "Unanalyzable complex variable expression in isset",
- []
- );
- }
- return $this->context;
- }
-}
-
-return new InvalidVariableIssetPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/InvokePHPNativeSyntaxCheckPlugin.php b/vendor/phan/phan/.phan/plugins/InvokePHPNativeSyntaxCheckPlugin.php
deleted file mode 100644
index 1ee07197..00000000
--- a/vendor/phan/phan/.phan/plugins/InvokePHPNativeSyntaxCheckPlugin.php
+++ /dev/null
@@ -1,448 +0,0 @@
- ['php_native_syntax_check_binaries' => ['php72', 'php70', 'php56']]
- * Note: This may cause Phan to take over twice as long. This is recommended for use with `--processes N`.
- *
- * Known issues:
- * - short_open_tags may make php --syntax-check --no-php-ini behave differently from php --syntax-check, e.g. for '
- * A list of invoked processes that this plugin created.
- * This plugin creates 0 or more processes(up to a maximum number can run at a time)
- * and then waits for the execution of those processes to finish.
- */
- private $processes = [];
-
- /**
- * @param CodeBase $code_base @phan-unused-param
- * The code base in which the node exists
- *
- * @param Context $context
- * A context with the file name for $file_contents and the scope after analyzing $node.
- *
- * @param string $file_contents the unmodified file contents @phan-unused-param
- * @param Node $node the node @phan-unused-param
- * @override
- */
- public function beforeAnalyzeFile(
- CodeBase $code_base,
- Context $context,
- string $file_contents,
- Node $node
- ): void {
- $php_binaries = (Config::getValue('plugin_config')['php_native_syntax_check_binaries'] ?? null) ?: [PHP_BINARY];
-
- foreach ($php_binaries as $binary) {
- $this->processes[] = new InvokeExecutionPromise($binary, $file_contents, $context);
- }
- }
-
- /**
- * @param CodeBase $code_base
- * The code base in which the node exists
- *
- * @param Context $context @phan-unused-param
- * A context with the file name for $file_contents and the scope after analyzing $node.
- *
- * @param string $file_contents the unmodified file contents @phan-unused-param
- * @param Node $node the node @phan-unused-param
- * @override
- * @throws Error if a process fails to shut down
- */
- public function afterAnalyzeFile(
- CodeBase $code_base,
- Context $context,
- string $file_contents,
- Node $node
- ): void {
- $configured_max_incomplete_processes = (int)(Config::getValue('plugin_config')['php_native_syntax_check_max_processes'] ?? 1) - 1;
- $max_incomplete_processes = max(0, $configured_max_incomplete_processes);
- $this->awaitIncompleteProcesses($code_base, $max_incomplete_processes);
- }
-
- /**
- * @throws Error if a syntax check process fails to shut down
- */
- private function awaitIncompleteProcesses(CodeBase $code_base, int $max_incomplete_processes): void
- {
- foreach ($this->processes as $i => $process) {
- if (!$process->read()) {
- continue;
- }
- unset($this->processes[$i]);
- self::handleError($code_base, $process);
- }
- $max_incomplete_processes = max(0, $max_incomplete_processes);
- while (count($this->processes) > $max_incomplete_processes) {
- $process = array_pop($this->processes);
- if (!$process) {
- throw new AssertionError("Process list should be non-empty");
- }
- $process->blockingRead();
- self::handleError($code_base, $process);
- }
- }
-
- /**
- * @override
- * @throws Error if a syntax check process fails to shut down.
- */
- public function finalizeProcess(CodeBase $code_base): void
- {
- $this->awaitIncompleteProcesses($code_base, 0);
- }
-
- private static function handleError(CodeBase $code_base, InvokeExecutionPromise $process): void
- {
- $check_error_message = $process->getError();
- if (!is_string($check_error_message)) {
- return;
- }
- $context = $process->getContext();
- $binary = $process->getBinary();
- $lineno = 1;
- if (preg_match(self::LINE_NUMBER_REGEX, $check_error_message, $matches)) {
- $lineno = (int)$matches[1];
- $check_error_message = trim(preg_replace(self::LINE_NUMBER_REGEX, '', $check_error_message));
- }
- $check_error_message = preg_replace(self::STDIN_FILENAME_REGEX, '', $check_error_message);
-
- self::emitIssue(
- $code_base,
- (clone $context)->withLineNumberStart($lineno),
- 'PhanNativePHPSyntaxCheckPlugin',
- 'Saw error or notice for {FILE} --syntax-check: {DETAILS}',
- [
- $binary === PHP_BINARY ? 'php' : $binary,
- json_encode($check_error_message),
-
- ],
- Issue::SEVERITY_CRITICAL
- );
- }
-}
-
-/**
- * This wraps a `php --syntax-check` process,
- * and contains methods to start the process and await the result
- * (and check for failures)
- */
-class InvokeExecutionPromise
-{
- /** @var string path to the php binary invoked */
- private $binary;
-
- /** @var bool is the process finished executing */
- private $done = false;
-
- /** @var resource the result of `proc_open()` */
- private $process;
-
- /** @var array{0:resource,1:resource,2:resource} stdin, stdout, stderr */
- private $pipes;
-
- /** @var ?string an error message */
- private $error = null;
-
- /** @var string the raw bytes from stdout with serialized data */
- private $raw_stdout = '';
-
- /** @var string */
- private $fallback_error = '';
-
- /** @var Context has the file name being analyzed */
- private $context;
-
- /** @var ?string the temporary path, if needed for Windows. */
- private $tmp_path;
-
- public function __construct(string $binary, string $file_contents, Context $context)
- {
- $this->context = clone $context;
- $new_file_contents = Parser::removeShebang($file_contents);
- // TODO: Use symfony process
- // Note: We might have invalid utf-8, ensure that the streams are opened in binary mode.
- // I'm not sure if this is necessary.
- if (DIRECTORY_SEPARATOR === "\\") {
- $cmd = escapeshellarg($binary) . ' --syntax-check --no-php-ini';
- $abs_path = $this->getAbsPathForFileContents($new_file_contents, $file_contents !== $new_file_contents);
- if (!is_string($abs_path)) {
- // The helper function has set the error and done flags
- return;
- }
-
- // Possibly https://bugs.php.net/bug.php?id=51800
- // NOTE: Work around this by writing from the original file. This may not work as expected in LSP mode
- $abs_path = str_replace("/", "\\", $abs_path);
-
- $cmd .= ' < ' . escapeshellarg($abs_path);
-
- $descriptorspec = [
- 1 => ['pipe', 'wb'],
- ];
- $this->binary = $binary;
- // https://superuser.com/questions/1213094/how-to-escape-in-cmd-exe-c-parameters/1213100#1213100
- //
- // > Otherwise, old behavior is to see if the first character is
- // > a quote character and if so, strip the leading character and
- // > remove the last quote character on the command line, preserving
- // > any text after the last quote character.
- //
- // e.g. `""C:\php 7.4.3\php.exe" --syntax-check --no-php-ini < "C:\some project\test.php""`
- // gets unescaped as `"C:\php 7.4.3\php.exe" --syntax-check --no-php-ini < "C:\some project\test.php"`
- if (PHP_VERSION_ID < 80000) {
- // In PHP 8.0.0, proc_open started always escaping arguments with additional quotes, so doing it twice would be a bug.
- $cmd = "\"$cmd\"";
- }
- $process = proc_open("$cmd", $descriptorspec, $pipes);
- if (!is_resource($process)) {
- $this->done = true;
- $this->error = "Failed to run proc_open in " . __METHOD__;
- return;
- }
- $this->process = $process;
- } else {
- $cmd = [$binary, '--syntax-check', '--no-php-ini'];
- if (PHP_VERSION_ID < 70400) {
- $cmd = implode(' ', array_map('escapeshellarg', $cmd));
- }
- $descriptorspec = [
- ['pipe', 'rb'],
- ['pipe', 'wb'],
- ];
- $this->binary = $binary;
- // @phan-suppress-next-line PhanPartialTypeMismatchArgumentInternal PHP 7.3 does not accept arrays
- $process = proc_open($cmd, $descriptorspec, $pipes);
- if (!is_resource($process)) {
- $this->done = true;
- $this->error = "Failed to run proc_open in " . __METHOD__;
- return;
- }
- $this->process = $process;
-
- error_clear_last();
- if (!self::streamPutContents($pipes[0], $new_file_contents)) {
- $this->fallback_error = \error_get_last()['message'] ?? '';
- }
- }
- $this->pipes = $pipes;
-
- if (!stream_set_blocking($pipes[1], false)) {
- $this->error = "unable to set read stdout to non-blocking";
- }
- }
-
- private function getAbsPathForFileContents(string $new_file_contents, bool $force_tmp_file): ?string
- {
- $file_name = $this->context->getFile();
- if ($force_tmp_file || CLI::isDaemonOrLanguageServer()) {
- // This is inefficient, but
- // - Windows has problems with using stdio/stdout at the same time
- // - During regular analysis, we won't need to create temporary files.
- $tmp_path = tempnam(sys_get_temp_dir(), 'phan');
- if (!is_string($tmp_path)) {
- $this->done = true;
- $this->error = "Could not create temporary path for $file_name";
- return null;
- }
- file_put_contents($tmp_path, $new_file_contents);
- $this->tmp_path = $tmp_path;
- return $tmp_path;
- }
- $abs_path = Config::projectPath($file_name);
- if (!file_exists($abs_path)) {
- $this->done = true;
- $this->error = "File does not exist";
- return null;
- }
- return $abs_path;
- }
-
- /**
- * @param resource $stream stream to write $file_contents to before fclose()
- * @param string $file_contents
- * See https://bugs.php.net/bug.php?id=39598
- */
- private static function streamPutContents($stream, string $file_contents): bool
- {
- try {
- while (strlen($file_contents) > 0) {
- $bytes_written = with_disabled_phan_error_handler(/** @return int|false */ static function () use ($stream, $file_contents) {
- return @fwrite($stream, $file_contents);
- });
- if ($bytes_written === false) {
- CLI::printWarningToStderr('failed to write in ' . __METHOD__);
- return false;
- }
- if ($bytes_written === 0) {
- $read_streams = [];
- $write_streams = [$stream];
- $except_streams = [];
- // Wait for the stream to be available for write with a timeout of 1 second.
- stream_select($read_streams, $write_streams, $except_streams, 1);
- if (!$write_streams) {
- usleep(1000); // Probably unnecessary, but leaving it in anyway
- // This is blocked?
- continue;
- }
- // $stream is ready to be written to?
- $bytes_written = fwrite($stream, $file_contents);
- if (!$bytes_written) {
- CLI::printToStderr('failed to write in ' . __METHOD__ . ' but the stream should be ready');
- return false;
- }
- }
- if ($bytes_written > 0) {
- $file_contents = \substr($file_contents, $bytes_written);
- }
- }
- } finally {
- fclose($stream);
- }
- return true;
- }
-
- /**
- * @return bool false if an error was encountered when trying to read more output from the syntax check process.
- */
- public function read(): bool
- {
- if ($this->done) {
- return true;
- }
- $stdout = $this->pipes[1];
- while (!feof($stdout)) {
- $bytes = fread($stdout, 4096);
- if ($bytes === false) {
- break;
- }
- if (strlen($bytes) === 0) {
- break;
- }
- $this->raw_stdout .= $bytes;
- }
- if (!feof($stdout)) {
- return false;
- }
- fclose($stdout);
-
- $this->done = true;
-
- $exit_code = proc_close($this->process);
- if ($exit_code === 0) {
- $this->error = null;
- return true;
- }
- $output = str_replace("\r", "", trim($this->raw_stdout));
- $first_line = explode("\n", $output)[0];
- $this->error = $first_line;
- return true;
- }
-
- /**
- * @throws Error if reading failed
- */
- public function blockingRead(): void
- {
- if ($this->done) {
- return;
- }
- if (!stream_set_blocking($this->pipes[1], true)) {
- throw new Error("Unable to make stdout blocking");
- }
- if (!$this->read()) {
- throw new Error("Failed to read");
- }
- }
-
- /**
- * @throws RangeException if this was called before the process finished
- */
- public function getError(): ?string
- {
- if (!$this->done) {
- throw new RangeException("Called " . __METHOD__ . " too early");
- }
- if ($this->error === '') {
- // There was an error running the process, but no output to stdout.
- $result = "No output was detected. Is " . var_representation($this->binary) . " a relative or absolute path to an executable PHP binary?";
- if ($this->fallback_error !== '') {
- $result .= ' Error sending file contents to syntax check: ' . $this->fallback_error;
- }
- return $result;
- }
- return $this->error;
- }
-
- /**
- * Returns the context containing the name of the file being syntax checked
- */
- public function getContext(): Context
- {
- return $this->context;
- }
-
- /**
- * @return string the path to the PHP interpreter binary. (e.g. `/usr/bin/php`)
- */
- public function getBinary(): string
- {
- return $this->binary;
- }
-
- /**
- * @return never
- */
- public function __wakeup()
- {
- $this->tmp_path = null;
- throw new RuntimeException("Cannot unserialize");
- }
-
- public function __destruct()
- {
- // We created a temporary path for Windows
- if (is_string($this->tmp_path)) {
- unlink($this->tmp_path);
- }
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new InvokePHPNativeSyntaxCheckPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/LoopVariableReusePlugin.php b/vendor/phan/phan/.phan/plugins/LoopVariableReusePlugin.php
deleted file mode 100644
index 4105f428..00000000
--- a/vendor/phan/phan/.phan/plugins/LoopVariableReusePlugin.php
+++ /dev/null
@@ -1,7 +0,0 @@
- maps function/method/closure FQSEN to function info and the set of union types they return */
- public static $method_return_types;
-
- /** @var Set the set of function/method/closure FQSENs that don't need to be more specific. */
- public static $method_blacklist;
-
- /**
- * @return class-string - name of PluginAwarePostAnalysisVisitor subclass
- */
- public static function getPostAnalyzeNodeVisitorClassName(): string
- {
- return MoreSpecificElementTypeVisitor::class;
- }
-
- /**
- * Record that $function contains a return statement which returns an expression of type $return_type.
- *
- * This may be called multiple times for the same return statement (Phan recursively analyzes functions with underspecified param types by default)
- */
- public static function recordType(FunctionInterface $function, UnionType $return_type): void
- {
- $fqsen = $function->getFQSEN();
- if (self::$method_blacklist->offsetExists($fqsen)) {
- return;
- }
- if ($return_type->isEmpty()) {
- self::$method_blacklist->attach($fqsen);
- self::$method_return_types->offsetUnset($fqsen);
- return;
- }
- if (self::$method_return_types->offsetExists($fqsen)) {
- self::$method_return_types->offsetGet($fqsen)->types->attach($return_type);
- } else {
- self::$method_return_types->offsetSet($fqsen, new ElementTypeInfo($function, [$return_type]));
- }
- }
-
- private static function shouldWarnAboutMoreSpecificType(CodeBase $code_base, UnionType $actual_type, UnionType $declared_return_type): bool
- {
- if ($declared_return_type->isEmpty()) {
- // There was no phpdoc type declaration, so let UnknownElementTypePlugin warn about that instead of this.
- // This plugin warns about `@return mixed` but not the absence of a declaration because the former normally prevents phan from inferring something more specific.
- return false;
- }
- if ($declared_return_type->containsNullable() && !$actual_type->containsNullable()) {
- // Warn about `Subclass1|Subclass2` being the real return type of `?BaseClass`
- // because the actual returned type is non-null
- return true;
- }
- if ($declared_return_type->typeCount() === 1) {
- if ($declared_return_type->getTypeSet()[0]->hasObjectWithKnownFQSEN()) {
- if ($actual_type->typeCount() >= 2) {
- // Don't warn about Subclass1|Subclass2 being more specific than BaseClass
- return false;
- }
- }
- }
- if ($declared_return_type->isStrictSubtypeOf($code_base, $actual_type)) {
- return false;
- }
- if (!$actual_type->isStrictSubtypeOf($code_base, $declared_return_type)) {
- return false;
- }
- if (!$actual_type->canCastToUnionType($declared_return_type, $code_base)) {
- // Don't warn here about type mismatches such as int->string or object->array, but do warn about SubClass->BaseClass.
- // Phan should warn elsewhere about those mismatches
- return false;
- }
- if ($declared_return_type->hasTopLevelArrayShapeTypeInstances()) {
- return false;
- }
- $real_actual_type = $actual_type->getRealUnionType();
- if (!$real_actual_type->isEmpty() && $declared_return_type->isStrictSubtypeOf($code_base, $real_actual_type)) {
- // TODO: Provide a way to disable this heuristic.
- return false;
- }
- return true;
- }
-
- private static function containsObjectWithKnownFQSEN(UnionType $union_type): bool
- {
- foreach ($union_type->getTypesRecursively() as $type) {
- if ($type->hasObjectWithKnownFQSEN()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * After all return statements are gathered, suggest a more specific type for the various functions.
- */
- public function finalizeProcess(CodeBase $code_base): void
- {
- foreach (self::$method_return_types as $type_info) {
- $function = $type_info->function;
- $function_context = $function->getContext();
- // TODO: Do a better job for Traversable and iterable
- $actual_type = UnionType::merge($type_info->types->toArray())->withStaticResolvedInContext($function_context)->eraseTemplatesRecursive()->asNormalizedTypes();
- $declared_return_type = $function->getOriginalReturnType()->withStaticResolvedInContext($function_context)->eraseTemplatesRecursive()->asNormalizedTypes();
- if (!self::shouldWarnAboutMoreSpecificType($code_base, $actual_type, $declared_return_type)) {
- continue;
- }
- if (self::containsObjectWithKnownFQSEN($actual_type) && !self::containsObjectWithKnownFQSEN($declared_return_type)) {
- $issue_type = 'PhanPluginMoreSpecificActualReturnTypeContainsFQSEN';
- $issue_message = 'Phan inferred that {FUNCTION} documented to have return type {TYPE} (without an FQSEN) returns the more specific type {TYPE} (with an FQSEN)';
- } else {
- $issue_type = 'PhanPluginMoreSpecificActualReturnType';
- $issue_message = 'Phan inferred that {FUNCTION} documented to have return type {TYPE} returns the more specific type {TYPE}';
- }
-
- $this->emitIssue(
- $code_base,
- $function->getContext(),
- $issue_type,
- $issue_message,
- [
- $function->getRepresentationForIssue(),
- $declared_return_type,
- $actual_type->getDebugRepresentation()
- ]
- );
- }
- }
-}
-
-/**
- * Represents the actual return types seen during analysis
- * (including recursive analysis)
- */
-class ElementTypeInfo
-{
- /** @var FunctionInterface the function with the return values*/
- public $function;
- /** @var Set the set of observed return types */
- public $types;
- /**
- * @param list $return_types
- */
- public function __construct(FunctionInterface $function, array $return_types)
- {
- $this->function = $function;
- $this->types = new Set($return_types);
- }
-}
-MoreSpecificElementTypePlugin::$method_blacklist = new Set();
-MoreSpecificElementTypePlugin::$method_return_types = new Map();
-
-/**
- * This visitor analyzes node kinds that can be the root of expressions
- * containing duplicate expressions, and is called on nodes in post-order.
- */
-class MoreSpecificElementTypeVisitor extends PluginAwarePostAnalysisVisitor
-{
- /**
- * @param Node $node a node of kind ast\AST_RETURN, representing a return statement.
- */
- public function visitReturn(Node $node): void
- {
- if (!$this->context->isInFunctionLikeScope()) {
- return;
- }
- try {
- $function = $this->context->getFunctionLikeInScope($this->code_base);
- } catch (Exception $_) {
- return;
- }
- if ($function->hasYield()) {
- // TODO: Support analyzing yield key/value types of generators?
- return;
- }
- if ($function instanceof Method) {
- // Skip functions that are overrides or are overridden.
- // They may be documenting a less specific return type to deal with the inheritance hierarchy.
- if ($function->isOverride() || $function->isOverriddenByAnother()) {
- return;
- }
- }
- try {
- // Fetch the list of valid classes, and warn about any undefined classes.
- // (We have more specific issue types such as PhanNonClassMethodCall below, don't emit PhanTypeExpected*)
- $union_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $node->children['expr']);
- } catch (Exception $_) {
- // Phan should already throw for this
- return;
- }
- MoreSpecificElementTypePlugin::recordType($function, $union_type->withFlattenedArrayShapeOrLiteralTypeInstances());
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new MoreSpecificElementTypePlugin();
diff --git a/vendor/phan/phan/.phan/plugins/NoAssertPlugin.php b/vendor/phan/phan/.phan/plugins/NoAssertPlugin.php
deleted file mode 100644
index 212e8564..00000000
--- a/vendor/phan/phan/.phan/plugins/NoAssertPlugin.php
+++ /dev/null
@@ -1,85 +0,0 @@
-children['expr']->children['name'] ?? null;
- if (!is_string($name)) {
- return;
- }
- if (strcasecmp($name, 'assert') !== 0) {
- return;
- }
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginNoAssert',
- // phpcs:ignore Generic.Files.LineLength.MaxExceeded
- 'assert() is discouraged. Although phan supports using assert() for type annotations, PHP\'s documentation recommends assertions only for debugging, and assert() has surprising behaviors.',
- []
- );
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new NoAssertPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/NonBoolBranchPlugin.php b/vendor/phan/phan/.phan/plugins/NonBoolBranchPlugin.php
deleted file mode 100644
index 7ee39e40..00000000
--- a/vendor/phan/phan/.phan/plugins/NonBoolBranchPlugin.php
+++ /dev/null
@@ -1,88 +0,0 @@
-children as $if_node) {
- if (!$if_node instanceof Node) {
- throw new AssertionError("Expected if statement to be a node");
- }
- $condition = $if_node->children['cond'];
-
- // dig nodes to avoid the NOT('!') operation converting its value to a boolean type.
- // Also, use right-hand side of assignments such as `$x = (expr)`
- while (($condition instanceof Node) && (
- ($condition->flags === ast\flags\UNARY_BOOL_NOT && $condition->kind === ast\AST_UNARY_OP)
- || (\in_array($condition->kind, [\ast\AST_ASSIGN, \ast\AST_ASSIGN_REF], true)))
- ) {
- $condition = $condition->children['expr'];
- }
-
- if ($condition === null) {
- // $condition === null will be appeared in else-clause, then avoid them
- continue;
- }
-
- if ($condition instanceof Node) {
- $this->context = $this->context->withLineNumberStart($condition->lineno);
- }
- // evaluate the type of conditional expression
- try {
- $union_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $condition);
- } catch (IssueException $_) {
- return $this->context;
- }
- if (!$union_type->isEmpty() && !$union_type->isExclusivelyBoolTypes()) {
- $this->emit(
- 'PhanPluginNonBoolBranch',
- 'Non bool value of type {TYPE} evaluated in if clause',
- [(string)$union_type]
- );
- }
- }
- return $this->context;
- }
-}
-
-return new NonBoolBranchPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/NonBoolInLogicalArithPlugin.php b/vendor/phan/phan/.phan/plugins/NonBoolInLogicalArithPlugin.php
deleted file mode 100644
index 40a8e4ab..00000000
--- a/vendor/phan/phan/.phan/plugins/NonBoolInLogicalArithPlugin.php
+++ /dev/null
@@ -1,95 +0,0 @@
-flags, self::BINARY_BOOL_OPERATORS, true)) {
- // get left node and parse it
- // (dig nodes to avoid NOT('!') operator's converting its value to boolean type)
- $left_node = $node->children['left'];
- while (isset($left_node->flags) && $left_node->flags === ast\flags\UNARY_BOOL_NOT) {
- $left_node = $left_node->children['expr'];
- }
-
- // get right node and parse it
- $right_node = $node->children['right'];
- while (isset($right_node->flags) && $right_node->flags === ast\flags\UNARY_BOOL_NOT) {
- $right_node = $right_node->children['expr'];
- }
-
- // get the type of two nodes
- $left_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $left_node);
- $right_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $right_node);
-
- // if left or right type is NOT boolean, emit issue
- if (!$left_type->isExclusivelyBoolTypes()) {
- if ($left_node instanceof Node) {
- $this->context = $this->context->withLineNumberStart($left_node->lineno);
- }
- $this->emit(
- 'PhanPluginNonBoolInLogicalArith',
- 'Non bool value of type {TYPE} in logical arithmetic',
- [(string)$left_type]
- );
- }
- if (!$right_type->isExclusivelyBoolTypes()) {
- if ($right_node instanceof Node) {
- $this->context = $this->context->withLineNumberStart($right_node->lineno);
- }
- $this->emit(
- 'PhanPluginNonBoolInLogicalArith',
- 'Non bool value of type {TYPE} in logical arithmetic',
- [(string)$right_type]
- );
- }
- }
- return $this->context;
- }
-}
-
-return new NonBoolInLogicalArithPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin.php b/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin.php
deleted file mode 100644
index a5324937..00000000
--- a/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin.php
+++ /dev/null
@@ -1,202 +0,0 @@
- - Set after the constructor is called if an instance property with this name is declared
- // protected $parent_node_list;
-
- // A plugin's visitors should NOT implement visit(), unless they need to.
-
- // phpcs:disable Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
- public const NotFullyQualifiedFunctionCall = 'PhanPluginNotFullyQualifiedFunctionCall';
- public const NotFullyQualifiedOptimizableFunctionCall = 'PhanPluginNotFullyQualifiedOptimizableFunctionCall';
- public const NotFullyQualifiedGlobalConstant = 'PhanPluginNotFullyQualifiedGlobalConstant';
- // phpcs:enable Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
-
- /**
- * Source of functions: `zend_try_compile_special_func` from https://github.com/php/php-src/blob/master/Zend/zend_compile.c
- */
- private const OPTIMIZABLE_FUNCTIONS = [
- 'array_key_exists' => true,
- 'array_slice' => true,
- 'boolval' => true,
- 'call_user_func' => true,
- 'call_user_func_array' => true,
- 'chr' => true,
- 'count' => true,
- 'defined' => true,
- 'doubleval' => true,
- 'floatval' => true,
- 'func_get_args' => true,
- 'func_num_args' => true,
- 'get_called_class' => true,
- 'get_class' => true,
- 'gettype' => true,
- 'in_array' => true,
- 'intval' => true,
- 'is_array' => true,
- 'is_bool' => true,
- 'is_double' => true,
- 'is_float' => true,
- 'is_int' => true,
- 'is_integer' => true,
- 'is_long' => true,
- 'is_null' => true,
- 'is_object' => true,
- 'is_real' => true,
- 'is_resource' => true,
- 'is_string' => true,
- 'ord' => true,
- 'strlen' => true,
- 'strval' => true,
- ];
-
- /**
- * @param Node $node
- * A node to analyze of type ast\AST_CALL (call to a global function)
- * @override
- */
- public function visitCall(Node $node): void
- {
- $expression = $node->children['expr'];
- if (!($expression instanceof Node) || $expression->kind !== ast\AST_NAME) {
- return;
- }
- if (($expression->flags & ast\flags\NAME_NOT_FQ) !== ast\flags\NAME_NOT_FQ) {
- // This is namespace\foo() or \NS\foo()
- return;
- }
- if ($this->context->getNamespace() === '\\') {
- // This is in the global namespace and is always fully qualified
- return;
- }
- $function_name = $expression->children['name'];
- if (!is_string($function_name)) {
- // Possibly redundant.
- return;
- }
- // TODO: Probably wrong for ast\parse_code - should check namespace map of USE_NORMAL for 'ast' there.
- // Same for ContextNode->getFunction()
- if ($this->context->hasNamespaceMapFor(\ast\flags\USE_FUNCTION, $function_name)) {
- return;
- }
- $this->warnNotFullyQualifiedFunctionCall($function_name, $expression);
- }
-
- private function warnNotFullyQualifiedFunctionCall(string $function_name, Node $expression): void
- {
- if (array_key_exists(strtolower($function_name), self::OPTIMIZABLE_FUNCTIONS)) {
- $issue_type = self::NotFullyQualifiedOptimizableFunctionCall;
- $issue_msg = 'Expected function call to {FUNCTION}() to be fully qualified or have a use statement but none were found in namespace {NAMESPACE}'
- . ' (opcache can optimize fully qualified calls to this function in recent php versions)';
- } else {
- $issue_type = self::NotFullyQualifiedFunctionCall;
- $issue_msg = 'Expected function call to {FUNCTION}() to be fully qualified or have a use statement but none were found in namespace {NAMESPACE}';
- }
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($expression->lineno),
- $issue_type,
- $issue_msg,
- [$function_name, $this->context->getNamespace()]
- );
- }
-
- /**
- * @param Node $node
- * A node to analyze of type ast\AST_CONST (reference to a constant)
- * @override
- */
- public function visitConst(Node $node): void
- {
- $expression = $node->children['name'];
- if (!($expression instanceof Node) || $expression->kind !== ast\AST_NAME) {
- return;
- }
- if (($expression->flags & ast\flags\NAME_NOT_FQ) !== ast\flags\NAME_NOT_FQ) {
- // This is namespace\SOME_CONST or \NS\SOME_CONST
- return;
- }
- if ($this->context->getNamespace() === '\\') {
- // This is in the global namespace and is always fully qualified
- return;
- }
- $constant_name = $expression->children['name'];
- if (!is_string($constant_name)) {
- // Possibly redundant.
- return;
- }
- $constant_name_lower = strtolower($constant_name);
- if ($constant_name_lower === 'true' || $constant_name_lower === 'false' || $constant_name_lower === 'null') {
- // These are treated similarly to keywords and are either
- // 1. the same in any namespace
- // 2. `use somethingelse\true [as false];`
- return;
- }
-
- // TODO: Probably wrong for ast\AST_NAME - should check namespace map of USE_NORMAL for 'ast' there.
- // Same for ContextNode->getConst()
- if ($this->context->hasNamespaceMapFor(\ast\flags\USE_CONST, $constant_name)) {
- return;
- }
- $this->warnNotFullyQualifiedConstantUsage($constant_name, $expression);
- }
-
- private function warnNotFullyQualifiedConstantUsage(string $constant_name, Node $expression): void
- {
- $this->emitPluginIssue(
- $this->code_base,
- (clone $this->context)->withLineNumberStart($expression->lineno),
- self::NotFullyQualifiedGlobalConstant,
- 'Expected usage of {CONST} to be fully qualified or have a use statement but none were found in namespace {NAMESPACE}',
- [$constant_name, $this->context->getNamespace()]
- );
- }
-}
-
-if (Config::isIssueFixingPluginEnabled()) {
- require_once __DIR__ . '/NotFullyQualifiedUsagePlugin/fixers.php';
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new NotFullyQualifiedUsagePlugin();
diff --git a/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin/fixers.php b/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin/fixers.php
deleted file mode 100644
index 764d7e99..00000000
--- a/vendor/phan/phan/.phan/plugins/NotFullyQualifiedUsagePlugin/fixers.php
+++ /dev/null
@@ -1,108 +0,0 @@
-getLine();
- $expected_name = $instance->getTemplateParameters()[0];
- $edits = [];
- foreach ($contents->getNodesAtLine($line) as $node) {
- if ($node instanceof QualifiedName) {
- if ($node->globalSpecifier || $node->relativeSpecifier) {
- IssueFixer::debug("skip already globally or relatively specified\n");
- // This is already qualified
- continue;
- }
- $actual_name = (new NodeUtils($contents->getContents()))->phpParserNameToString($node);
- } elseif ($node instanceof ReservedWord) {
- // A reserved word in other contexts such as 'float'
- $token = $node->children;
- if (!$token instanceof Token) {
- continue;
- }
- $actual_name = (new NodeUtils($contents->getContents()))->tokenToString($token);
- } else {
- IssueFixer::debug("skip wrong node kind " . get_class($node) . "\n");
- continue;
- }
- if ($actual_name !== $expected_name) {
- IssueFixer::debug("skip '$actual_name' !== '$expected_name'\n");
- continue;
- }
- $is_actual_call = $node->parent instanceof CallExpression;
- $is_expected_call = $instance->getIssue()->getType() !== NotFullyQualifiedUsageVisitor::NotFullyQualifiedGlobalConstant;
- if ($is_actual_call !== $is_expected_call) {
- IssueFixer::debug("skip check mismatch actual expected are call vs constants\n");
- // don't warn about constants with the same names as functions or vice-versa
- continue;
- }
- try {
- if ($is_expected_call) {
- // Don't do this if the global function this refers to doesn't exist.
- // TODO: Support namespaced functions
- if (!$code_base->hasFunctionWithFQSEN(FullyQualifiedFunctionName::fromFullyQualifiedString($actual_name))) {
- IssueFixer::debug("skip attempt to fix $actual_name() because function was not found in the global scope\n");
- return null;
- }
- } else {
- // Don't do this if the global function this refers to doesn't exist.
- // TODO: Support namespaced functions
- if (!$code_base->hasGlobalConstantWithFQSEN(FullyQualifiedGlobalConstantName::fromFullyQualifiedString($actual_name)) &&
- !in_array(strtolower($actual_name), ['null', 'true', 'false'], true)) {
- IssueFixer::debug("skip attempt to fix $actual_name because the constant was not found in the global scope\n");
- return null;
- }
- }
- } catch (Exception $_) {
- continue;
- }
- //fwrite(STDERR, "name is: " . get_class($node->parent) . "\n");
-
- // They are case-sensitively identical.
- // Generate a fix.
- // @phan-suppress-next-line PhanThrowTypeAbsentForCall
- $start = $node->getStartPosition();
- $edits[] = new FileEdit($start, $start, '\\');
- }
- if ($edits) {
- return new FileEditSet($edits);
- }
- return null;
- };
- IssueFixer::registerFixerClosure(
- NotFullyQualifiedUsageVisitor::NotFullyQualifiedGlobalConstant,
- $fix
- );
- IssueFixer::registerFixerClosure(
- NotFullyQualifiedUsageVisitor::NotFullyQualifiedFunctionCall,
- $fix
- );
- IssueFixer::registerFixerClosure(
- NotFullyQualifiedUsageVisitor::NotFullyQualifiedOptimizableFunctionCall,
- $fix
- );
-});
diff --git a/vendor/phan/phan/.phan/plugins/NumericalComparisonPlugin.php b/vendor/phan/phan/.phan/plugins/NumericalComparisonPlugin.php
deleted file mode 100644
index db1acce3..00000000
--- a/vendor/phan/phan/.phan/plugins/NumericalComparisonPlugin.php
+++ /dev/null
@@ -1,86 +0,0 @@
-children['left'];
- $left_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $left_node);
- $right_node = $node->children['right'];
- $right_type = UnionTypeVisitor::unionTypeFromNode($this->code_base, $this->context, $right_node);
-
- // non numerical values are not allowed in the operator equal(==, !=)
- if (in_array($node->flags, self::BINARY_EQUAL_OPERATORS, true)) {
- if (!$left_type->isNonNullNumberType() && !$right_type->isNonNullNumberType()) {
- $this->emit(
- 'PhanPluginNumericalComparison',
- "non numerical values compared by the operators '==' or '!='",
- []
- );
- }
- // numerical values are not allowed in the operator identical('===', '!==')
- } elseif (in_array($node->flags, self::BINARY_IDENTICAL_OPERATORS, true)) {
- if ($left_type->isNonNullNumberType() || $right_type->isNonNullNumberType()) {
- // TODO: different name for this issue type?
- $this->emit(
- 'PhanPluginNumericalComparison',
- "numerical values compared by the operators '===' or '!=='",
- []
- );
- }
- }
- return $this->context;
- }
-}
-
-return new NumericalComparisonPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHP53CompatibilityPlugin.php b/vendor/phan/phan/.phan/plugins/PHP53CompatibilityPlugin.php
deleted file mode 100644
index 6a66e4c6..00000000
--- a/vendor/phan/phan/.phan/plugins/PHP53CompatibilityPlugin.php
+++ /dev/null
@@ -1,116 +0,0 @@
-flags === ast\flags\ARRAY_SYNTAX_SHORT) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginCompatibilityShortArray',
- "Short arrays ({CODE}) require support for php 5.4+",
- [ASTReverter::toShortString($node)]
- );
- }
- }
-
- /**
- * @param Node $node
- * A node to analyze of kind ast\AST_ARG_LIST
- * @override
- */
- public function visitArgList(Node $node): void
- {
- $lastArg = end($node->children);
- if ($lastArg instanceof Node && $lastArg->kind === ast\AST_UNPACK) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginCompatibilityArgumentUnpacking',
- "Argument unpacking ({CODE}) requires support for php 5.6+",
- [ASTReverter::toShortString($lastArg)]
- );
- }
- }
-
- /**
- * @param Node $node
- * A node to analyze of kind ast\AST_PARAM
- * @override
- */
- public function visitParam(Node $node): void
- {
- if ($node->flags & ast\flags\PARAM_VARIADIC) {
- $this->emitPluginIssue(
- $this->code_base,
- $this->context,
- 'PhanPluginCompatibilityVariadicParam',
- "Variadic functions ({CODE}) require support for php 5.6+",
- [ASTReverter::toShortString($node)]
- );
- }
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new PHP53CompatibilityPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHPDocInWrongCommentPlugin.php b/vendor/phan/phan/.phan/plugins/PHPDocInWrongCommentPlugin.php
deleted file mode 100644
index 45f7f8ce..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPDocInWrongCommentPlugin.php
+++ /dev/null
@@ -1,98 +0,0 @@
-emitIssue(
- $code_base,
- (clone $context)->withLineNumberStart($token[2]),
- 'PhanPluginPHPDocHashComment',
- 'Saw comment starting with {COMMENT} in {COMMENT} - consider using {COMMENT} instead to avoid confusion with php 8.0 {COMMENT} attributes',
- ['#', StringUtil::jsonEncode(self::truncate(trim($comment_string))), '//', '#[']
- );
- }
- continue;
- }
- if (strpos($comment_string, '@') === false) {
- continue;
- }
- $lineno = $token[2];
-
- // @phan-suppress-next-line PhanAccessClassConstantInternal
- $comment = Comment::fromStringInContext("/**" . $comment_string, $code_base, $context, $lineno, Comment::ON_ANY);
-
- if ($comment instanceof NullComment) {
- continue;
- }
- $this->emitIssue(
- $code_base,
- (clone $context)->withLineNumberStart($token[2]),
- 'PhanPluginPHPDocInWrongComment',
- 'Saw possible phpdoc annotation in ordinary block comment {COMMENT}. PHPDoc comments should start with "/**" (followed by whitespace), not "/*"',
- [StringUtil::jsonEncode(self::truncate($comment_string))]
- );
- }
- }
-
- private static function truncate(string $token): string
- {
- if (strlen($token) > 200) {
- return mb_substr($token, 0, 200) . "...";
- }
- return $token;
- }
-}
-if (!function_exists('token_get_all')) {
- throw new UnloadablePluginException("PHPDocInWrongCommentPlugin requires the tokenizer extension, which is not enabled (this plugin uses token_get_all())");
-}
-return new PHPDocInWrongCommentPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin.php b/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin.php
deleted file mode 100644
index c22f55c5..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin.php
+++ /dev/null
@@ -1,235 +0,0 @@
-isMagic() || $method->isPHPInternal()) {
- return;
- }
- if ($method->getFQSEN() !== $method->getDefiningFQSEN()) {
- return;
- }
- self::analyzeFunctionLike($code_base, $method);
- }
-
- /**
- * @suppress PhanAccessClassConstantInternal
- */
- private static function isRedundantFunctionComment(FunctionInterface $method, string $doc_comment): bool
- {
- $lines = explode("\n", $doc_comment);
- foreach ($lines as $line) {
- $line = trim($line, " \r\n\t*/");
- if ($line === '') {
- continue;
- }
- if ($line[0] !== '@') {
- return false;
- }
- if (!preg_match('/^@(phan-)?(param|return)\s/', $line)) {
- return false;
- }
- if (preg_match(Builder::PARAM_COMMENT_REGEX, $line, $matches)) {
- if ($matches[0] !== $line) {
- // There's a description after the (at)param annotation
- return false;
- }
- } elseif (preg_match(Builder::RETURN_COMMENT_REGEX, $line, $matches)) {
- if ($matches[0] !== $line) {
- // There's a description after the (at)return annotation
- return false;
- }
- } else {
- // This is not a valid annotation. It might be documentation.
- return false;
- }
- }
- $comment = $method->getComment();
- if (!$comment) {
- // unparseable?
- return false;
- }
- if ($comment->hasReturnUnionType()) {
- $comment_return_type = $comment->getReturnType();
- if (!$comment_return_type->isEmpty() && !$comment_return_type->asNormalizedTypes()->isEqualTo($method->getRealReturnType())) {
- return false;
- }
- }
- if (count($comment->getParameterList()) > 0) {
- return false;
- }
- foreach ($comment->getParameterMap() as $comment_param_name => $param) {
- $comment_param_type = $param->getUnionType()->asNormalizedTypes();
- if ($comment_param_type->isEmpty()) {
- return false;
- }
- foreach ($method->getRealParameterList() as $real_param) {
- if ($real_param->getName() === $comment_param_name) {
- if ($real_param->getUnionType()->isEqualTo($comment_param_type)) {
- // This is redundant, check remaining parameters.
- continue 2;
- }
- }
- }
- // could not find that comment param, Phan warns elsewhere.
- // Assume this is not redundant.
- return false;
- }
- return true;
- }
-
- private static function analyzeFunctionLike(CodeBase $code_base, FunctionInterface $method): void
- {
- if (Phan::isExcludedAnalysisFile($method->getContext()->getFile())) {
- // This has no side effects, so we can skip files that don't need to be analyzed
- return;
- }
- $comment = $method->getDocComment();
- if (!StringUtil::isNonZeroLengthString($comment)) {
- return;
- }
- if (!self::isRedundantFunctionComment($method, $comment)) {
- self::checkIsRedundantReturn($code_base, $method, $comment);
- return;
- }
- $encoded_comment = StringUtil::encodeValue($comment);
- if ($method instanceof Method) {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- self::RedundantMethodComment,
- 'Redundant doc comment on method {METHOD}(). Either add a description or remove the comment: {COMMENT}',
- [$method->getName(), $encoded_comment]
- );
- } elseif ($method instanceof Func && $method->isClosure()) {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- self::RedundantClosureComment,
- 'Redundant doc comment on closure {FUNCTION}. Either add a description or remove the comment: {COMMENT}',
- [$method->getNameForIssue(), $encoded_comment]
- );
- } else {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- self::RedundantFunctionComment,
- 'Redundant doc comment on function {FUNCTION}(). Either add a description or remove the comment: {COMMENT}',
- [$method->getName(), $encoded_comment]
- );
- }
- }
-
- private static function checkIsRedundantReturn(CodeBase $code_base, FunctionInterface $method, string $doc_comment): void
- {
- if (strpos($doc_comment, '@return') === false) {
- return;
- }
- $comment = $method->getComment();
- if (!$comment) {
- // unparseable?
- return;
- }
- if ($method->getRealReturnType()->isEmpty()) {
- return;
- }
- if (!$comment->hasReturnUnionType()) {
- return;
- }
- $comment_return_type = $comment->getReturnType();
- if (!$comment_return_type->asNormalizedTypes()->isEqualTo($method->getRealReturnType())) {
- return;
- }
- $lines = explode("\n", $doc_comment);
- for ($i = count($lines) - 1; $i >= 0; $i--) {
- $line = $lines[$i];
- $line = trim($line, " \r\n\t*/");
- if ($line === '') {
- continue;
- }
- if ($line[0] !== '@') {
- return;
- }
- if (!preg_match('/^@(phan-)?return\s/', $line)) {
- continue;
- }
- // @phan-suppress-next-line PhanAccessClassConstantInternal
- if (!preg_match(Builder::RETURN_COMMENT_REGEX, $line, $matches)) {
- return;
- }
- if ($matches[0] !== $line) {
- // There's a description after the (at)return annotation
- return;
- }
- self::emitIssue(
- $code_base,
- $method->getContext()->withLineNumberStart($comment->getReturnLineno()),
- self::RedundantReturnComment,
- 'Redundant @return {TYPE} on function {FUNCTION}. Either add a description or remove the @return annotation: {COMMENT}',
- [$comment_return_type, $method->getNameForIssue(), $line]
- );
- return;
- }
- }
-
- /**
- * @return array
- */
- public function getAutomaticFixers(): array
- {
- require_once __DIR__ . '/PHPDocRedundantPlugin/Fixers.php';
- $function_like_fixer = Closure::fromCallable([Fixers::class, 'fixRedundantFunctionLikeComment']);
- return [
- self::RedundantFunctionComment => $function_like_fixer,
- self::RedundantMethodComment => $function_like_fixer,
- self::RedundantClosureComment => $function_like_fixer,
- self::RedundantReturnComment => Closure::fromCallable([Fixers::class, 'fixRedundantReturnComment']),
- ];
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new PHPDocRedundantPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin/Fixers.php b/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin/Fixers.php
deleted file mode 100644
index d861fd91..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPDocRedundantPlugin/Fixers.php
+++ /dev/null
@@ -1,187 +0,0 @@
-getTemplateParameters();
- $name = $params[0];
- $encoded_comment = $params[1];
- // @phan-suppress-next-line PhanPartialTypeMismatchArgument
- $declaration = self::findFunctionLikeDeclaration($contents, $instance->getLine(), $name);
- if (!$declaration) {
- return null;
- }
- return self::computeEditsToRemoveFunctionLikeComment($contents, $declaration, (string)$encoded_comment);
- }
-
- private static function computeEditsToRemoveFunctionLikeComment(FileCacheEntry $contents, FunctionLike $declaration, string $encoded_comment): ?FileEditSet
- {
- if (!$declaration instanceof PhpParser\Node) {
- // impossible
- return null;
- }
- $comment_token = self::getDocCommentToken($declaration);
- if (!$comment_token) {
- return null;
- }
- $file_contents = $contents->getContents();
- $comment = $comment_token->getText($file_contents);
- $actual_encoded_comment = StringUtil::encodeValue($comment);
- if ($actual_encoded_comment !== $encoded_comment) {
- return null;
- }
- return self::computeEditSetToDeleteComment($file_contents, $comment_token);
- }
-
- private static function computeEditSetToDeleteComment(string $file_contents, Token $comment_token): FileEditSet
- {
- // get the byte where the `)` of the argument list ends
- $last_byte_index = $comment_token->getEndPosition();
- $first_byte_index = $comment_token->start;
- // Skip leading whitespace and the previous newline, if those were found
- for (; $first_byte_index > 0; $first_byte_index--) {
- $prev_byte = $file_contents[$first_byte_index - 1];
- switch ($prev_byte) {
- case " ":
- case "\t":
- // keep skipping previous bytes of whitespace
- break;
- case "\n":
- $first_byte_index--;
- if ($first_byte_index > 0 && $file_contents[$first_byte_index - 1] === "\r") {
- $first_byte_index--;
- }
- break 2;
- case "\r":
- $first_byte_index--;
- break 2;
- default:
- // This is not whitespace, so stop.
- break 2;
- }
- }
- $file_edit = new FileEdit($first_byte_index, $last_byte_index, '');
- return new FileEditSet([$file_edit]);
- }
-
- /**
- * Add a missing return type to the real signature
- * @param CodeBase $code_base @unused-param
- */
- public static function fixRedundantReturnComment(
- CodeBase $code_base,
- FileCacheEntry $contents,
- IssueInstance $instance
- ): ?FileEditSet {
- $lineno = $instance->getLine();
- $file_lines = $contents->getLines();
-
- $line = \trim($file_lines[$lineno]);
- // @phan-suppress-next-line PhanAccessClassConstantInternal
- if (!\preg_match(Builder::RETURN_COMMENT_REGEX, $line)) {
- return null;
- }
- $first_deleted_line = $lineno;
- $last_deleted_line = $lineno;
- $is_blank_comment_line = static function (int $i) use ($file_lines): bool {
- return \trim($file_lines[$i] ?? '') === '*';
- };
- while ($is_blank_comment_line($first_deleted_line - 1)) {
- $first_deleted_line--;
- }
- while ($is_blank_comment_line($last_deleted_line + 1)) {
- $last_deleted_line++;
- }
- $start_offset = $contents->getLineOffset($first_deleted_line);
- $end_offset = $contents->getLineOffset($last_deleted_line + 1);
- if (!$start_offset || !$end_offset) {
- return null;
- }
- // Return an edit to delete the `(at)return RedundantType` and the surrounding blank comment lines
- return new FileEditSet([new FileEdit($start_offset, $end_offset, '')]);
- }
-
- /**
- * @suppress PhanThrowTypeAbsentForCall
- * @suppress PhanUndeclaredClassMethod
- * @suppress UnusedSuppression false positive for PhpTokenizer with polyfill due to https://github.com/Microsoft/tolerant-php-parser/issues/292
- */
- private static function getDocCommentToken(PhpParser\Node $node): ?Token
- {
- $leadingTriviaText = $node->getLeadingCommentAndWhitespaceText();
- $leadingTriviaTokens = PhpTokenizer::getTokensArrayFromContent(
- $leadingTriviaText,
- ParseContext::SourceElements,
- $node->getFullStartPosition(),
- false
- );
- for ($i = \count($leadingTriviaTokens) - 1; $i >= 0; $i--) {
- $token = $leadingTriviaTokens[$i];
- if ($token->kind === TokenKind::DocCommentToken) {
- return $token;
- }
- }
- return null;
- }
-
- private static function findFunctionLikeDeclaration(
- FileCacheEntry $contents,
- int $line,
- string $name
- ): ?FunctionLike {
- $candidates = [];
- foreach ($contents->getNodesAtLine($line) as $node) {
- if ($node instanceof FunctionDeclaration || $node instanceof MethodDeclaration) {
- $name_node = $node->name;
- if (!$name_node) {
- continue;
- }
- $declaration_name = (new NodeUtils($contents->getContents()))->tokenToString($name_node);
- if ($declaration_name === $name) {
- $candidates[] = $node;
- }
- } elseif ($node instanceof AnonymousFunctionCreationExpression) {
- if (\preg_match('/^Closure\(/', $name)) {
- $candidates[] = $node;
- }
- }
- }
- if (\count($candidates) === 1) {
- return $candidates[0];
- }
- return null;
- }
-}
diff --git a/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin.php b/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin.php
deleted file mode 100644
index e1d3b21a..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin.php
+++ /dev/null
@@ -1,161 +0,0 @@
- */
- private $deferred_analysis_methods = [];
-
- /**
- * @return array
- */
- public function getAutomaticFixers(): array
- {
- require_once __DIR__ . '/PHPDocToRealTypesPlugin/Fixers.php';
- $param_closure = Closure::fromCallable([Fixers::class, 'fixParamType']);
- $return_closure = Closure::fromCallable([Fixers::class, 'fixReturnType']);
- return [
- self::CanUsePHP71Void => $return_closure,
- self::CanUseReturnType => $return_closure,
- self::CanUseNullableReturnType => $return_closure,
- self::CanUseNullableParamType => $param_closure,
- self::CanUseParamType => $param_closure,
- ];
- }
-
- public function analyzeFunction(CodeBase $code_base, Func $function): void
- {
- self::analyzeFunctionLike($code_base, $function);
- }
-
- /**
- * @param CodeBase $code_base @unused-param
- */
- public function analyzeMethod(CodeBase $code_base, Method $method): void
- {
- if ($method->isFromPHPDoc() || $method->isMagic() || $method->isPHPInternal()) {
- return;
- }
- if ($method->getFQSEN() !== $method->getDefiningFQSEN()) {
- return;
- }
- $this->deferred_analysis_methods[$method->getFQSEN()->__toString()] = $method;
- }
-
- public function beforeAnalyzePhase(CodeBase $code_base): void
- {
- $ignore_overrides = (bool)getenv('PHPDOC_TO_REAL_TYPES_IGNORE_INHERITANCE');
- foreach ($this->deferred_analysis_methods as $method) {
- if ($method->isOverride() || $method->isOverriddenByAnother()) {
- if (!$ignore_overrides) {
- continue;
- }
- }
- self::analyzeFunctionLike($code_base, $method);
- }
- }
-
- private static function analyzeFunctionLike(CodeBase $code_base, FunctionInterface $method): void
- {
- if (Phan::isExcludedAnalysisFile($method->getContext()->getFile())) {
- // This has no side effects, so we can skip files that don't need to be analyzed
- return;
- }
- if ($method->getRealReturnType()->isEmpty()) {
- self::analyzeReturnTypeOfFunctionLike($code_base, $method);
- }
- $phpdoc_param_list = $method->getParameterList();
- foreach ($method->getRealParameterList() as $i => $parameter) {
- if (!$parameter->getNonVariadicUnionType()->isEmpty()) {
- continue;
- }
- $phpdoc_param = $phpdoc_param_list[$i];
- if (!$phpdoc_param) {
- continue;
- }
- $union_type = $phpdoc_param->getNonVariadicUnionType()->asNormalizedTypes();
- if ($union_type->typeCount() !== 1) {
- continue;
- }
- $type = $union_type->getTypeSet()[0];
- if (!$type->canUseInRealSignature()) {
- continue;
- }
- self::emitIssue(
- $code_base,
- $method->getContext(),
- $type->isNullable() ? self::CanUseNullableParamType : self::CanUseParamType,
- 'Can use {TYPE} as the type of parameter ${PARAMETER} of {METHOD}',
- [$type->asSignatureType(), $parameter->getName(), $method->getName()]
- );
- }
- }
-
- private static function analyzeReturnTypeOfFunctionLike(CodeBase $code_base, FunctionInterface $method): void
- {
- $union_type = $method->getUnionType();
- if ($union_type->isVoidType()) {
- self::emitIssue(
- $code_base,
- $method->getContext(),
- self::CanUsePHP71Void,
- 'Can use php 7.1\'s {TYPE} as a return type of {METHOD}',
- ['void', $method->getName()]
- );
- return;
- }
- $union_type = $union_type->asNormalizedTypes();
- if ($union_type->typeCount() !== 1) {
- return;
- }
- $type = $union_type->getTypeSet()[0];
- if (!$type->canUseInRealSignature()) {
- return;
- }
- self::emitIssue(
- $code_base,
- $method->getContext(),
- $type->isNullable() ? self::CanUseNullableReturnType : self::CanUseReturnType,
- 'Can use {TYPE} as a return type of {METHOD}',
- [$type->asSignatureType(), $method->getName()]
- );
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new PHPDocToRealTypesPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin/Fixers.php b/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin/Fixers.php
deleted file mode 100644
index 6d74c90f..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPDocToRealTypesPlugin/Fixers.php
+++ /dev/null
@@ -1,133 +0,0 @@
-getTemplateParameters();
- $return_type = $params[0];
- $name = $params[1];
- // @phan-suppress-next-line PhanPartialTypeMismatchArgument
- $declaration = self::findFunctionLikeDeclaration($contents, $instance->getLine(), $name);
- if (!$declaration) {
- return null;
- }
- return self::computeEditsForReturnTypeDeclaration($declaration, (string)$return_type);
- }
-
- /**
- * Add a missing param type to the real signature
- * @unused-param $code_base
- */
- public static function fixParamType(
- CodeBase $code_base,
- FileCacheEntry $contents,
- IssueInstance $instance
- ): ?FileEditSet {
- $params = $instance->getTemplateParameters();
- $param_type = $params[0];
- $param_name = $params[1];
- $method_name = $params[2];
- // @phan-suppress-next-line PhanPartialTypeMismatchArgument
- $declaration = self::findFunctionLikeDeclaration($contents, $instance->getLine(), $method_name);
- if (!$declaration) {
- return null;
- }
- return self::computeEditsForParamTypeDeclaration($contents, $declaration, (string)$param_name, (string)$param_type);
- }
-
- private static function computeEditsForReturnTypeDeclaration(FunctionLike $declaration, string $return_type): ?FileEditSet
- {
- if ($return_type === '') {
- return null;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- $close_bracket = $declaration->anonymousFunctionUseClause->closeParen ?? $declaration->closeParen;
- if (!$close_bracket instanceof Token) {
- return null;
- }
- // get the byte where the `)` of the argument list ends
- $last_byte_index = $close_bracket->getEndPosition();
- $file_edit = new FileEdit($last_byte_index, $last_byte_index, " : $return_type");
- return new FileEditSet([$file_edit]);
- }
-
- private static function computeEditsForParamTypeDeclaration(FileCacheEntry $contents, FunctionLike $declaration, string $param_name, string $param_type): ?FileEditSet
- {
- if ($param_type === '') {
- return null;
- }
- // @phan-suppress-next-line PhanUndeclaredProperty
- $parameter_node_list = $declaration->parameters->children ?? [];
- foreach ($parameter_node_list as $param) {
- if (!$param instanceof PhpParser\Node\Parameter) {
- continue;
- }
- $declaration_name = (new NodeUtils($contents->getContents()))->tokenToString($param->variableName);
- if ($declaration_name !== $param_name) {
- continue;
- }
- $token = $param->byRefToken ?? $param->dotDotDotToken ?? $param->variableName;
- $token_start_index = $token->start;
- $file_edit = new FileEdit($token_start_index, $token_start_index, "$param_type ");
- return new FileEditSet([$file_edit]);
- }
- return null;
- }
-
- private static function findFunctionLikeDeclaration(
- FileCacheEntry $contents,
- int $line,
- string $name
- ): ?FunctionLike {
- $candidates = [];
- foreach ($contents->getNodesAtLine($line) as $node) {
- if ($node instanceof FunctionDeclaration || $node instanceof MethodDeclaration) {
- $name_node = $node->name;
- if (!$name_node) {
- continue;
- }
- $declaration_name = (new NodeUtils($contents->getContents()))->tokenToString($name_node);
- if ($declaration_name === $name) {
- $candidates[] = $node;
- }
- } elseif ($node instanceof AnonymousFunctionCreationExpression) {
- if ($name === '{closure}') {
- $candidates[] = $node;
- }
- }
- }
- if (\count($candidates) === 1) {
- return $candidates[0];
- }
- return null;
- }
-}
diff --git a/vendor/phan/phan/.phan/plugins/PHPUnitAssertionPlugin.php b/vendor/phan/phan/.phan/plugins/PHPUnitAssertionPlugin.php
deleted file mode 100644
index 4b1c88d4..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPUnitAssertionPlugin.php
+++ /dev/null
@@ -1,241 +0,0 @@
-hasClassWithFQSEN($assert_class_fqsen)) {
- if (!getenv('PHAN_PHPUNIT_ASSERTION_PLUGIN_QUIET')) {
- // @phan-suppress-next-line PhanPluginRemoveDebugCall
- fwrite(STDERR, "PHPUnitAssertionPlugin failed to find class PHPUnit\Framework\Assert, giving up (set environment variable PHAN_PHPUNIT_ASSERTION_PLUGIN_QUIET=1 to ignore this)\n");
- }
- return [];
- }
- $result = [];
- foreach ($code_base->getClassByFQSEN($assert_class_fqsen)->getMethodMap($code_base) as $method) {
- $closure = $this->createClosureForMethod($code_base, $method, $method->getName());
- if (!$closure) {
- continue;
- }
- $result[(string)$method->getFQSEN()] = $closure;
- }
- return $result;
- }
-
- /**
- * @return ?Closure(CodeBase, Context, FunctionInterface, array, ?Node):void
- * @suppress PhanAccessClassConstantInternal, PhanAccessMethodInternal
- */
- private function createClosureForMethod(CodeBase $code_base, Method $method, string $name): ?Closure
- {
- // TODO: Add a helper method which will convert a doc comment and a stub php function source code to a closure for a param index (or indices)
- switch (\strtolower($name)) {
- case 'asserttrue':
- case 'assertnotfalse':
- return $method->createClosureForAssertion(
- $code_base,
- new Assertion(UnionType::empty(), 'unusedParamName', Assertion::IS_TRUE),
- 0
- );
- case 'assertfalse':
- case 'assertnottrue':
- return $method->createClosureForAssertion(
- $code_base,
- new Assertion(UnionType::empty(), 'unusedParamName', Assertion::IS_FALSE),
- 0
- );
- // TODO: Rest of https://github.com/sebastianbergmann/phpunit/issues/3368
- case 'assertisstring':
- // TODO: Could convert to real types?
- return $method->createClosureForAssertion(
- $code_base,
- new Assertion(UnionType::fromFullyQualifiedPHPDocString('string'), 'unusedParamName', Assertion::IS_OF_TYPE),
- 0
- );
- case 'assertnull':
- return $method->createClosureForAssertion(
- $code_base,
- new Assertion(UnionType::fromFullyQualifiedPHPDocString('null'), 'unusedParamName', Assertion::IS_OF_TYPE),
- 0
- );
- case 'assertnotnull':
- return $method->createClosureForAssertion(
- $code_base,
- new Assertion(UnionType::fromFullyQualifiedPHPDocString('null'), 'unusedParamName', Assertion::IS_NOT_OF_TYPE),
- 0
- );
- case 'assertsame':
- // Sets the type of $actual to $expected
- //
- // This is equivalent to the side effects of the below doc comment.
- // Note that the doc comment would make phan emit warnings about invalid classes, etc.
- // TODO: Reuse the code for templates here
- //
- // (at)template T
- // (at)param T $expected
- // (at)param mixed $actual
- // (at)phan-assert T $actual
- return $method->createClosureForUnionTypeExtractorAndAssertionType(
- /**
- * @param list $args
- */
- static function (CodeBase $code_base, Context $context, array $args): UnionType {
- if (\count($args) < 2) {
- return UnionType::empty();
- }
- return UnionTypeVisitor::unionTypeFromNode($code_base, $context, $args[0]);
- },
- Assertion::IS_OF_TYPE,
- 1
- );
- case 'assertinternaltype':
- return $method->createClosureForUnionTypeExtractorAndAssertionType(
- /**
- * @param list $args
- */
- function (CodeBase $code_base, Context $context, array $args): UnionType {
- if (\count($args) < 2) {
- return UnionType::empty();
- }
- $string = $args[0];
- if ($string instanceof ast\Node) {
- $string = (UnionTypeVisitor::unionTypeFromNode($code_base, $context, $string))->asSingleScalarValueOrNull();
- }
- if (!is_string($string)) {
- return UnionType::empty();
- }
- $original_type = (UnionTypeVisitor::unionTypeFromNode($code_base, $context, $args[1]));
- switch ($string) {
- case 'numeric':
- return UnionType::fromFullyQualifiedPHPDocString('int|float|string');
- case 'integer':
- case 'int':
- return UnionType::fromFullyQualifiedPHPDocString('int');
-
- case 'double':
- case 'float':
- case 'real':
- return UnionType::fromFullyQualifiedPHPDocString('float');
-
- case 'string':
- return UnionType::fromFullyQualifiedPHPDocString('string');
-
- case 'boolean':
- case 'bool':
- return UnionType::fromFullyQualifiedPHPDocString('bool');
-
- case 'null':
- return UnionType::fromFullyQualifiedPHPDocString('null');
-
- case 'array':
- $result = $original_type->arrayTypes();
- if ($result->isEmpty()) {
- return UnionType::fromFullyQualifiedPHPDocString('array');
- }
- return $result;
- case 'object':
- $result = $original_type->objectTypes();
- if ($result->isEmpty()) {
- return UnionType::fromFullyQualifiedPHPDocString('object');
- }
- return $result;
- case 'resource':
- return UnionType::fromFullyQualifiedPHPDocString('resource');
- case 'scalar':
- $result = $original_type->scalarTypes();
- if ($result->isEmpty()) {
- return UnionType::fromFullyQualifiedPHPDocString('int|string|float|bool');
- }
- return $result;
-
- case 'callable':
- $result = $original_type->callableTypes($code_base);
- if ($result->isEmpty()) {
- return UnionType::fromFullyQualifiedPHPDocString('callable');
- }
- return $result;
- }
- // Warn about possibly invalid assertion
- // NOTE: This is only emitted for variables
- $this->emitPluginIssue(
- $code_base,
- $context,
- 'PhanPluginPHPUnitAssertionInvalidInternalType',
- 'Unknown type {STRING_LITERAL} in call to assertInternalType',
- [$string]
- );
-
- return UnionType::empty();
- },
- Assertion::IS_OF_TYPE,
- 1
- );
- case 'assertinstanceof':
- // This is equivalent to the side effects of the below doc comment.
- // Note that the doc comment would make phan emit warnings about invalid classes, etc.
- // TODO: Reuse the code for class-string here.
- //
- // (at)template T
- // (at)param class-string $expected
- // (at)param mixed $actual
- // (at)phan-assert T $actual
- return $method->createClosureForUnionTypeExtractorAndAssertionType(
- /**
- * @param list $args
- */
- static function (CodeBase $code_base, Context $context, array $args): UnionType {
- if (\count($args) < 2) {
- return UnionType::empty();
- }
- $string = (UnionTypeVisitor::unionTypeFromNode($code_base, $context, $args[0]))->asSingleScalarValueOrNull();
- if (!is_string($string)) {
- return UnionType::empty();
- }
- try {
- return FullyQualifiedClassName::fromFullyQualifiedString($string)->asType()->asPHPDocUnionType();
- } catch (\Exception $_) {
- return UnionType::empty();
- }
- },
- Assertion::IS_OF_TYPE,
- 1
- );
- }
- return null;
- }
-}
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new PHPUnitAssertionPlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PHPUnitNotDeadCodePlugin.php b/vendor/phan/phan/.phan/plugins/PHPUnitNotDeadCodePlugin.php
deleted file mode 100644
index a7858fa6..00000000
--- a/vendor/phan/phan/.phan/plugins/PHPUnitNotDeadCodePlugin.php
+++ /dev/null
@@ -1,154 +0,0 @@
-code_base contains all class definitions
- * @override
- * @unused-param $node
- */
- public function visitClass(Node $node): void
- {
- if (!Config::get_track_references()) {
- return;
- }
- $code_base = $this->code_base;
- if (!$code_base->hasClassWithFQSEN(self::$phpunit_test_case_fqsen)) {
- if (!self::$did_warn_missing_class) {
- // @phan-suppress-next-line PhanPluginRemoveDebugCall
- fprintf(STDERR, "Using plugin %s but could not find PHPUnit\Framework\TestCase\n", self::class);
- self::$did_warn_missing_class = true;
- }
- return;
- }
- // This assumes PreOrderAnalysisVisitor->visitClass is called first.
- $context = $this->context;
- $class = $context->getClassInScope($code_base);
- if (!$class->getFQSEN()->asType()->asExpandedTypes($code_base)->hasType(self::$phpunit_test_case_type)) {
- // This isn't a phpunit test case.
- return;
- }
-
- // Mark subclasses of TestCase as referenced
- $class->addReference($context);
- // Mark all test cases as referenced
- foreach ($class->getMethodMap($code_base) as $method) {
- if (static::isTestCase($method)) {
- // TODO: Parse @dataProvider methodName, check for method existence,
- // then mark method for dataProvider as referenced.
- $method->addReference($context);
- $this->markDataProvidersAsReferenced($class, $method);
- }
- }
- // https://phpunit.de/manual/current/en/fixtures.html (PHPUnit framework checks for this override)
- if ($class->hasPropertyWithName($code_base, 'backupStaticAttributesBlacklist')) {
- $property = $class->getPropertyByName($code_base, 'backupStaticAttributesBlacklist');
- $property->addReference($context);
- $property->setHasReadReference();
- }
- }
-
- /**
- * This regex contains a single pattern, which matches a valid PHP identifier.
- * (e.g. for variable names, magic property names, etc.
- * This does not allow backslashes.
- */
- private const WORD_REGEX = '([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)';
-
- /**
- * Marks all data provider methods as being referenced
- *
- * @param Method $method the Method representing a unit test in a test case subclass
- */
- private function markDataProvidersAsReferenced(Clazz $class, Method $method): void
- {
- if (preg_match('/@dataProvider\s+' . self::WORD_REGEX . '/', $method->getNode()->children['docComment'] ?? '', $match)) {
- $data_provider_name = $match[1];
- if ($class->hasMethodWithName($this->code_base, $data_provider_name, true)) {
- $class->getMethodByName($this->code_base, $data_provider_name)->addReference($this->context);
- }
- }
- }
-
- /**
- * @return bool true if $method is a PHPUnit test case
- */
- protected static function isTestCase(Method $method): bool
- {
- if (!$method->isPublic()) {
- return false;
- }
- if (preg_match('@^test@i', $method->getName())) {
- return true;
- }
- if (preg_match('/@test\b/', $method->getNode()->children['docComment'] ?? '')) {
- return true;
- }
- return false;
- }
-
- /**
- * Static initializer for this plugin - Gets called below before any methods can be used
- * @suppress PhanThrowTypeAbsentForCall this FQSEN is valid
- */
- public static function init(): void
- {
- $fqsen = FullyQualifiedClassName::make('\\PHPUnit\Framework', 'TestCase');
- self::$phpunit_test_case_fqsen = $fqsen;
- self::$phpunit_test_case_type = $fqsen->asType();
- }
-}
-PHPUnitNotDeadPluginVisitor::init();
-
-// Every plugin needs to return an instance of itself at the
-// end of the file in which it's defined.
-return new PHPUnitNotDeadCodePlugin();
diff --git a/vendor/phan/phan/.phan/plugins/PhanSelfCheckPlugin.php b/vendor/phan/phan/.phan/plugins/PhanSelfCheckPlugin.php
deleted file mode 100644
index 7563d7fa..00000000
--- a/vendor/phan/phan/.phan/plugins/PhanSelfCheckPlugin.php
+++ /dev/null
@@ -1,271 +0,0 @@
-):void
- */
- $make_array_issue_callback = static function (int $fmt_index, int $arg_index): Closure {
- /**
- * @param list $args the nodes for the arguments to the invocation
- */
- return static function (
- CodeBase $code_base,
- Context $context,
- FunctionInterface $unused_function,
- array $args
- ) use (
- $fmt_index,
- $arg_index
- ): void {
- if (\count($args) <= $fmt_index) {
- return;
- }
- // TODO: Check for AST_UNPACK
- $issue_message_template = $args[$fmt_index];
- if ($issue_message_template instanceof Node) {
- $issue_message_template = (new ContextNode($code_base, $context, $issue_message_template))->getEquivalentPHPScalarValue();
- }
- if (!is_string($issue_message_template)) {
- return;
- }
- $issue_message_arg_count = self::computeArraySize($code_base, $context, $args[$arg_index] ?? null);
- if ($issue_message_arg_count === null) {
- return;
- }
- self::checkIssueTemplateUsage($code_base, $context, $issue_message_template, $issue_message_arg_count);
- };
- };
- /**
- * @param int $type_index the index of a parameter expecting an issue type (e.g. PhanParamTooMany)
- * @param int $arg_index the index of an array parameter expecting sequential arguments. This is >= $type_index.
- * @return Closure(CodeBase, Context, FunctionInterface, list):void
- */
- $make_type_and_parameters_callback = static function (int $type_index, int $arg_index): Closure {
- /**
- * @param list $args the nodes for the arguments to the invocation
- */
- return static function (
- CodeBase $code_base,
- Context $context,
- FunctionInterface $function,
- array $args
- ) use (
- $type_index,
- $arg_index
- ): void {
- if (\count($args) <= $type_index) {
- return;
- }
- // TODO: Check for AST_UNPACK
- $issue_type = $args[$type_index];
- if ($issue_type instanceof Node) {
- $issue_type = (new ContextNode($code_base, $context, $issue_type))->getEquivalentPHPScalarValue();
- }
- if (!is_string($issue_type)) {
- return;
- }
- $issue = self::getIssueOrWarn($code_base, $context, $function, $issue_type);
- if (!$issue) {
- return;
- }
- $issue_message_arg_count = self::computeArraySize($code_base, $context, $args[$arg_index] ?? null);
- if ($issue_message_arg_count === null) {
- return;
- }
- self::checkIssueTemplateUsage($code_base, $context, $issue->getTemplate(), $issue_message_arg_count);
- };
- };
- /**
- * @param int $type_index the index of a parameter expecting an issue type (e.g. PhanParamTooMany)
- * @param int $arg_index the index of an array parameter expecting variable arguments. This is >= $type_index.
- * @return Closure(CodeBase, Context, FunctionInterface, list