diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..97f9d04 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ + [ + 'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments' +]]); +$parser = (new PhpParser\ParserFactory)->create( + PhpParser\ParserFactory::PREFER_PHP7, + $lexer +); +$dumper = new PhpParser\NodeDumper([ + 'dumpComments' => true, + 'dumpPositions' => $attributes['with-positions'], +]); +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; + +$traverser = new PhpParser\NodeTraverser(); +$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); + +foreach ($files as $file) { + if (strpos($file, ' Code $code\n"); + } else { + if (!file_exists($file)) { + fwrite(STDERR, "File $file does not exist.\n"); + exit(1); + } + + $code = file_get_contents($file); + fwrite(STDERR, "====> File $file:\n"); + } + + if ($attributes['with-recovery']) { + $errorHandler = new PhpParser\ErrorHandler\Collecting; + $stmts = $parser->parse($code, $errorHandler); + foreach ($errorHandler->getErrors() as $error) { + $message = formatErrorMessage($error, $code, $attributes['with-column-info']); + fwrite(STDERR, $message . "\n"); + } + if (null === $stmts) { + continue; + } + } else { + try { + $stmts = $parser->parse($code); + } catch (PhpParser\Error $error) { + $message = formatErrorMessage($error, $code, $attributes['with-column-info']); + fwrite(STDERR, $message . "\n"); + exit(1); + } + } + + foreach ($operations as $operation) { + if ('dump' === $operation) { + fwrite(STDERR, "==> Node dump:\n"); + echo $dumper->dump($stmts, $code), "\n"; + } elseif ('pretty-print' === $operation) { + fwrite(STDERR, "==> Pretty print:\n"); + echo $prettyPrinter->prettyPrintFile($stmts), "\n"; + } elseif ('json-dump' === $operation) { + fwrite(STDERR, "==> JSON dump:\n"); + echo json_encode($stmts, JSON_PRETTY_PRINT), "\n"; + } elseif ('var-dump' === $operation) { + fwrite(STDERR, "==> var_dump():\n"); + var_dump($stmts); + } elseif ('resolve-names' === $operation) { + fwrite(STDERR, "==> Resolved names.\n"); + $stmts = $traverser->traverse($stmts); + } + } +} + +function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) { + if ($withColumnInfo && $e->hasColumnInfo()) { + return $e->getMessageWithColumnInfo($code); + } else { + return $e->getMessage(); + } +} + +function showHelp($error = '') { + if ($error) { + fwrite(STDERR, $error . "\n\n"); + } + fwrite($error ? STDERR : STDOUT, << false, + 'with-positions' => false, + 'with-recovery' => false, + ]; + + array_shift($args); + $parseOptions = true; + foreach ($args as $arg) { + if (!$parseOptions) { + $files[] = $arg; + continue; + } + + switch ($arg) { + case '--dump': + case '-d': + $operations[] = 'dump'; + break; + case '--pretty-print': + case '-p': + $operations[] = 'pretty-print'; + break; + case '--json-dump': + case '-j': + $operations[] = 'json-dump'; + break; + case '--var-dump': + $operations[] = 'var-dump'; + break; + case '--resolve-names': + case '-N'; + $operations[] = 'resolve-names'; + break; + case '--with-column-info': + case '-c'; + $attributes['with-column-info'] = true; + break; + case '--with-positions': + case '-P': + $attributes['with-positions'] = true; + break; + case '--with-recovery': + case '-r': + $attributes['with-recovery'] = true; + break; + case '--help': + case '-h'; + showHelp(); + break; + case '--': + $parseOptions = false; + break; + default: + if ($arg[0] === '-') { + showHelp("Invalid operation $arg."); + } else { + $files[] = $arg; + } + } + } + + return [$operations, $files, $attributes]; +} diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000..95f7e09 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +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/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..06962d8 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $vendorDir . '/zoujingli/ip2region/Ip2Region.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 0000000..737bc1d --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,13 @@ + $vendorDir . '/symfony/polyfill-php56/bootstrap.php', + '9b552a3cc426e3287cc811caefa3cf53' => $vendorDir . '/topthink/think-helper/src/helper.php', + 'cc56288302d9df745d97c934d6a6e5f0' => $vendorDir . '/topthink/think-queue/src/common.php', + 'ea51e7f80936725691663347d5b38bd9' => $vendorDir . '/topthink/think-swoole/src/command.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..b7fc012 --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($vendorDir . '/topthink/think-swoole/src'), + 'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'), + 'think\\' => array($vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-queue/src'), + 'app\\' => array($baseDir . '/application'), + 'XCron\\' => array($vendorDir . '/xavier/xcron-expression/src/Cron'), + 'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'), + 'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'), + 'SuperClosure\\' => array($vendorDir . '/jeremeamia/superclosure/src'), + 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..6d52f09 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitc9ce0b35e756c543477403a43e9c78e8::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInitc9ce0b35e756c543477403a43e9c78e8::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequirec9ce0b35e756c543477403a43e9c78e8($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequirec9ce0b35e756c543477403a43e9c78e8($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000..4510581 --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,96 @@ + __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', + '9b552a3cc426e3287cc811caefa3cf53' => __DIR__ . '/..' . '/topthink/think-helper/src/helper.php', + 'cc56288302d9df745d97c934d6a6e5f0' => __DIR__ . '/..' . '/topthink/think-queue/src/common.php', + 'ea51e7f80936725691663347d5b38bd9' => __DIR__ . '/..' . '/topthink/think-swoole/src/command.php', + ); + + public static $prefixLengthsPsr4 = array ( + 't' => + array ( + 'think\\swoole\\' => 13, + 'think\\composer\\' => 15, + 'think\\' => 6, + ), + 'a' => + array ( + 'app\\' => 4, + ), + 'X' => + array ( + 'XCron\\' => 6, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Util\\' => 22, + 'Symfony\\Polyfill\\Php56\\' => 23, + 'SuperClosure\\' => 13, + ), + 'P' => + array ( + 'PhpParser\\' => 10, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'think\\swoole\\' => + array ( + 0 => __DIR__ . '/..' . '/topthink/think-swoole/src', + ), + 'think\\composer\\' => + array ( + 0 => __DIR__ . '/..' . '/topthink/think-installer/src', + ), + 'think\\' => + array ( + 0 => __DIR__ . '/..' . '/topthink/think-helper/src', + 1 => __DIR__ . '/..' . '/topthink/think-queue/src', + ), + 'app\\' => + array ( + 0 => __DIR__ . '/../..' . '/application', + ), + 'XCron\\' => + array ( + 0 => __DIR__ . '/..' . '/xavier/xcron-expression/src/Cron', + ), + 'Symfony\\Polyfill\\Util\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-util', + ), + 'Symfony\\Polyfill\\Php56\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php56', + ), + 'SuperClosure\\' => + array ( + 0 => __DIR__ . '/..' . '/jeremeamia/superclosure/src', + ), + 'PhpParser\\' => + array ( + 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', + ), + ); + + public static $classMap = array ( + 'Ip2Region' => __DIR__ . '/..' . '/zoujingli/ip2region/Ip2Region.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitc9ce0b35e756c543477403a43e9c78e8::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitc9ce0b35e756c543477403a43e9c78e8::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitc9ce0b35e756c543477403a43e9c78e8::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..acc5e56 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,554 @@ +[ + { + "name": "jeremeamia/superclosure", + "version": "2.4.0", + "version_normalized": "2.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/jeremeamia/super_closure.git", + "reference": "5707d5821b30b9a07acfb4d76949784aaa0e9ce9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/5707d5821b30b9a07acfb4d76949784aaa0e9ce9", + "reference": "5707d5821b30b9a07acfb4d76949784aaa0e9ce9", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^1.2|^2.0|^3.0|^4.0", + "php": ">=5.4", + "symfony/polyfill-php56": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0|^5.0" + }, + "time": "2018-03-21T22:21:57+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "SuperClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia", + "role": "Developer" + } + ], + "description": "Serialize Closure objects, including their context and binding", + "homepage": "https://github.com/jeremeamia/super_closure", + "keywords": [ + "closure", + "function", + "lambda", + "parser", + "serializable", + "serialize", + "tokenizer" + ] + }, + { + "name": "nikic/php-parser", + "version": "v4.2.4", + "version_normalized": "4.2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "97e59c7a16464196a8b9c77c47df68e4a39a45c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/97e59c7a16464196a8b9c77c47df68e4a39a45c4", + "reference": "97e59c7a16464196a8b9c77c47df68e4a39a45c4", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + }, + "time": "2019-09-01T07:51:21+00:00", + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ] + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "0e3b212e96a51338639d8ce175c046d7729c3403" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/0e3b212e96a51338639d8ce175c046d7729c3403", + "reference": "0e3b212e96a51338639d8ce175c046d7729c3403", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/polyfill-util", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "4317de1386717b4c22caed7725350a8887ab205c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4317de1386717b4c22caed7725350a8887ab205c", + "reference": "4317de1386717b4c22caed7725350a8887ab205c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ] + }, + { + "name": "topthink/framework", + "version": "v5.1.38.1", + "version_normalized": "5.1.38.1", + "source": { + "type": "git", + "url": "https://github.com/top-think/framework.git", + "reference": "12d15c29d5d6a972fc8bfc8db005d64d4786028c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/framework/zipball/12d15c29d5d6a972fc8bfc8db005d64d4786028c", + "reference": "12d15c29d5d6a972fc8bfc8db005d64d4786028c", + "shasum": "" + }, + "require": { + "php": ">=5.6.0", + "topthink/think-installer": "2.*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.0", + "mikey179/vfsstream": "~1.6", + "phpdocumentor/reflection-docblock": "^2.0", + "phploc/phploc": "2.*", + "phpunit/phpunit": "^5.0|^6.0", + "sebastian/phpcpd": "2.*", + "squizlabs/php_codesniffer": "2.*" + }, + "time": "2019-08-12T00:58:30+00:00", + "type": "think-framework", + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + }, + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "the new thinkphp framework", + "homepage": "http://thinkphp.cn/", + "keywords": [ + "framework", + "orm", + "thinkphp" + ] + }, + { + "name": "topthink/think-helper", + "version": "v3.1.3", + "version_normalized": "3.1.3.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-helper.git", + "reference": "4d85dfd3778623bbb1de3648f1dcd0c82f4439f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-helper/zipball/4d85dfd3778623bbb1de3648f1dcd0c82f4439f4", + "reference": "4d85dfd3778623bbb1de3648f1dcd0c82f4439f4", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "time": "2019-09-30T02:36:48+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "src/helper.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "The ThinkPHP6 Helper Package" + }, + { + "name": "topthink/think-installer", + "version": "v2.0.0", + "version_normalized": "2.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-installer.git", + "reference": "f5400a12c60e513911aef41fe443fa6920952675" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-installer/zipball/f5400a12c60e513911aef41fe443fa6920952675", + "reference": "f5400a12c60e513911aef41fe443fa6920952675", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev" + }, + "time": "2018-05-11T06:45:42+00:00", + "type": "composer-plugin", + "extra": { + "class": "think\\composer\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ] + }, + { + "name": "topthink/think-queue", + "version": "v2.0.4", + "version_normalized": "2.0.4.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-queue.git", + "reference": "d9b8f38c7af8ad770257b0d7db711ce8b12a6969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-queue/zipball/d9b8f38c7af8ad770257b0d7db711ce8b12a6969", + "reference": "d9b8f38c7af8ad770257b0d7db711ce8b12a6969", + "shasum": "" + }, + "require": { + "topthink/framework": "5.1.*", + "topthink/think-helper": ">=1.0.4", + "topthink/think-installer": "^2.0" + }, + "time": "2018-05-11T06:55:55+00:00", + "type": "think-extend", + "extra": { + "think-config": { + "queue": "src/config.php" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "src/common.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "The ThinkPHP5 Queue Package" + }, + { + "name": "topthink/think-swoole", + "version": "v2.0.17", + "version_normalized": "2.0.17.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-swoole.git", + "reference": "c0e227c0f9b400cd1e8524d4cc22039d715a26e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-swoole/zipball/c0e227c0f9b400cd1e8524d4cc22039d715a26e2", + "reference": "c0e227c0f9b400cd1e8524d4cc22039d715a26e2", + "shasum": "" + }, + "require": { + "ext-swoole": ">=1.9.5", + "jeremeamia/superclosure": "^2.4", + "topthink/framework": "~5.1.20", + "topthink/think-installer": "^2.0", + "topthink/think-queue": "^2.0", + "xavier/xcron-expression": "^0.11" + }, + "time": "2019-01-23T03:34:37+00:00", + "type": "think-extend", + "extra": { + "think-config": { + "swoole": "src/config/swoole.php", + "swoole_server": "src/config/server.php", + "timer": "src/config/timer.php" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\swoole\\": "src" + }, + "files": [ + "src/command.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "description": "Swoole extend for thinkphp5.1" + }, + { + "name": "xavier/xcron-expression", + "version": "0.11", + "version_normalized": "0.11.0.0", + "source": { + "type": "git", + "url": "https://github.com/xavieryang007/xcron-expression.git", + "reference": "a7fa8108ee8ad636625425a9795aae8ba4fee1b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/xavieryang007/xcron-expression/zipball/a7fa8108ee8ad636625425a9795aae8ba4fee1b3", + "reference": "a7fa8108ee8ad636625425a9795aae8ba4fee1b3", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~5.7" + }, + "time": "2018-08-18T09:01:23+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "XCron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "base on Michael Dowling`s cron-expression", + "keywords": [ + "cron", + "schedule" + ] + }, + { + "name": "zoujingli/ip2region", + "version": "v1.0.6", + "version_normalized": "1.0.6.0", + "source": { + "type": "git", + "url": "https://github.com/zoujingli/ip2region.git", + "reference": "054b6580f4e1f441ca700939fbddd6c0b8488f7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zoujingli/ip2region/zipball/054b6580f4e1f441ca700939fbddd6c0b8488f7a", + "reference": "054b6580f4e1f441ca700939fbddd6c0b8488f7a", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "time": "2019-05-18T06:44:34+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "Ip2Region.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com", + "homepage": "http://ctolog.com" + } + ], + "description": "Ip2Region for PHP", + "homepage": "https://github.com/zoujingli/Ip2Region", + "keywords": [ + "Ip2Region" + ] + } +] diff --git a/vendor/jeremeamia/superclosure/LICENSE.md b/vendor/jeremeamia/superclosure/LICENSE.md new file mode 100644 index 0000000..1cde13d --- /dev/null +++ b/vendor/jeremeamia/superclosure/LICENSE.md @@ -0,0 +1,18 @@ +# MIT License + +Copyright (c) 2010-2015 Jeremy Lindblom + +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/jeremeamia/superclosure/composer.json b/vendor/jeremeamia/superclosure/composer.json new file mode 100644 index 0000000..2ffab22 --- /dev/null +++ b/vendor/jeremeamia/superclosure/composer.json @@ -0,0 +1,39 @@ +{ + "name": "jeremeamia/superclosure", + "type": "library", + "description": "Serialize Closure objects, including their context and binding", + "keywords": ["closure", "serialize", "serializable", "function", "parser", "tokenizer", "lambda"], + "homepage": "https://github.com/jeremeamia/super_closure", + "license": "MIT", + "authors": [ + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia", + "role": "Developer" + } + ], + "require": { + "php": ">=5.4", + "nikic/php-parser": "^1.2|^2.0|^3.0|^4.0", + "symfony/polyfill-php56": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0|^5.0" + }, + "autoload": { + "psr-4": { + "SuperClosure\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "SuperClosure\\Test\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/AstAnalyzer.php b/vendor/jeremeamia/superclosure/src/Analyzer/AstAnalyzer.php new file mode 100644 index 0000000..c15f83c --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/AstAnalyzer.php @@ -0,0 +1,148 @@ +locateClosure($data); + + // Make a second pass through the AST, but only through the closure's + // nodes, to resolve any magic constants to literal values. + $traverser = new NodeTraverser; + $traverser->addVisitor(new MagicConstantVisitor($data['location'])); + $traverser->addVisitor($thisDetector = new ThisDetectorVisitor); + $data['ast'] = $traverser->traverse([$data['ast']])[0]; + $data['hasThis'] = $thisDetector->detected; + + // Bounce the updated AST down to a string representation of the code. + $data['code'] = (new NodePrinter)->prettyPrint([$data['ast']]); + } + + /** + * Parses the closure's code and produces an abstract syntax tree (AST). + * + * @param array $data + * + * @throws ClosureAnalysisException if there is an issue finding the closure + */ + private function locateClosure(array &$data) + { + try { + $locator = new ClosureLocatorVisitor($data['reflection']); + $fileAst = $this->getFileAst($data['reflection']); + + $fileTraverser = new NodeTraverser; + $fileTraverser->addVisitor(new NameResolver); + $fileTraverser->addVisitor($locator); + $fileTraverser->traverse($fileAst); + } catch (ParserError $e) { + // @codeCoverageIgnoreStart + throw new ClosureAnalysisException( + 'There was an error analyzing the closure code.', 0, $e + ); + // @codeCoverageIgnoreEnd + } + + $data['ast'] = $locator->closureNode; + if (!$data['ast']) { + // @codeCoverageIgnoreStart + throw new ClosureAnalysisException( + 'The closure was not found within the abstract syntax tree.' + ); + // @codeCoverageIgnoreEnd + } + + $data['location'] = $locator->location; + } + + /** + * Returns the variables that in the "use" clause of the closure definition. + * These are referred to as the "used variables", "static variables", or + * "closed upon variables", "context" of the closure. + * + * @param array $data + */ + protected function determineContext(array &$data) + { + // Get the variable names defined in the AST + $refs = 0; + $vars = array_map(function ($node) use (&$refs) { + if ($node->byRef) { + $refs++; + } + if ($node->var instanceof VariableNode) { + // For PHP-Parser >=4.0 + return $node->var->name; + } else { + // For PHP-Parser <4.0 + return $node->var; + } + }, $data['ast']->uses); + $data['hasRefs'] = ($refs > 0); + + // Get the variable names and values using reflection + $values = $data['reflection']->getStaticVariables(); + + // Combine the names and values to create the canonical context. + foreach ($vars as $name) { + if (isset($values[$name])) { + $data['context'][$name] = $values[$name]; + } + } + } + + /** + * @param \ReflectionFunction $reflection + * + * @throws ClosureAnalysisException + * + * @return \PhpParser\Node[] + */ + private function getFileAst(\ReflectionFunction $reflection) + { + $fileName = $reflection->getFileName(); + if (!file_exists($fileName)) { + throw new ClosureAnalysisException( + "The file containing the closure, \"{$fileName}\" did not exist." + ); + } + + + return $this->getParser()->parse(file_get_contents($fileName)); + } + + /** + * @return CodeParser + */ + private function getParser() + { + if (class_exists('PhpParser\ParserFactory')) { + return (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + } + + return new CodeParser(new EmulativeLexer); + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/ClosureAnalyzer.php b/vendor/jeremeamia/superclosure/src/Analyzer/ClosureAnalyzer.php new file mode 100644 index 0000000..6ba92a3 --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/ClosureAnalyzer.php @@ -0,0 +1,68 @@ + new \ReflectionFunction($closure), + 'code' => null, + 'hasThis' => false, + 'context' => [], + 'hasRefs' => false, + 'binding' => null, + 'scope' => null, + 'isStatic' => $this->isClosureStatic($closure), + ]; + + $this->determineCode($data); + $this->determineContext($data); + $this->determineBinding($data); + + return $data; + } + + abstract protected function determineCode(array &$data); + + /** + * Returns the variables that are in the "use" clause of the closure. + * + * These variables are referred to as the "used variables", "static + * variables", "closed upon variables", or "context" of the closure. + * + * @param array $data + */ + abstract protected function determineContext(array &$data); + + private function determineBinding(array &$data) + { + $data['binding'] = $data['reflection']->getClosureThis(); + if ($scope = $data['reflection']->getClosureScopeClass()) { + $data['scope'] = $scope->getName(); + } + } + + private function isClosureStatic(\Closure $closure) + { + $closure = @$closure->bindTo(new \stdClass); + + if ($closure === null) { + return true; + } + + $rebound = new \ReflectionFunction($closure); + + return $rebound->getClosureThis() === null; + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/Token.php b/vendor/jeremeamia/superclosure/src/Analyzer/Token.php new file mode 100644 index 0000000..cab8efa --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/Token.php @@ -0,0 +1,70 @@ +code = $code; + $this->value = $value; + $this->line = $line; + $this->name = $value ? token_name($value) : null; + } + + /** + * Determines if the token's value/code is equal to the specified value. + * + * @param mixed $value The value to check. + * + * @return bool True if the token is equal to the value. + */ + public function is($value) + { + return ($this->code === $value || $this->value === $value); + } + + public function __toString() + { + return $this->code; + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/TokenAnalyzer.php b/vendor/jeremeamia/superclosure/src/Analyzer/TokenAnalyzer.php new file mode 100644 index 0000000..2928ba0 --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/TokenAnalyzer.php @@ -0,0 +1,118 @@ +determineTokens($data); + $data['code'] = implode('', $data['tokens']); + $data['hasThis'] = (strpos($data['code'], '$this') !== false); + } + + private function determineTokens(array &$data) + { + $potential = $this->determinePotentialTokens($data['reflection']); + $braceLevel = $index = $step = $insideUse = 0; + $data['tokens'] = $data['context'] = []; + + foreach ($potential as $token) { + $token = new Token($token); + switch ($step) { + // Handle tokens before the function declaration. + case 0: + if ($token->is(T_FUNCTION)) { + $data['tokens'][] = $token; + $step++; + } + break; + // Handle tokens inside the function signature. + case 1: + $data['tokens'][] = $token; + if ($insideUse) { + if ($token->is(T_VARIABLE)) { + $varName = trim($token, '$ '); + $data['context'][$varName] = null; + } elseif ($token->is('&')) { + $data['hasRefs'] = true; + } + } elseif ($token->is(T_USE)) { + $insideUse++; + } + if ($token->is('{')) { + $step++; + $braceLevel++; + } + break; + // Handle tokens inside the function body. + case 2: + $data['tokens'][] = $token; + if ($token->is('{')) { + $braceLevel++; + } elseif ($token->is('}')) { + $braceLevel--; + if ($braceLevel === 0) { + $step++; + } + } + break; + // Handle tokens after the function declaration. + case 3: + if ($token->is(T_FUNCTION)) { + throw new ClosureAnalysisException('Multiple closures ' + . 'were declared on the same line of code. Could not ' + . 'determine which closure was the intended target.' + ); + } + break; + } + } + } + + private function determinePotentialTokens(\ReflectionFunction $reflection) + { + // Load the file containing the code for the function. + $fileName = $reflection->getFileName(); + if (!is_readable($fileName)) { + throw new ClosureAnalysisException( + "Cannot read the file containing the closure: \"{$fileName}\"." + ); + } + + $code = ''; + $file = new \SplFileObject($fileName); + $file->seek($reflection->getStartLine() - 1); + while ($file->key() < $reflection->getEndLine()) { + $code .= $file->current(); + $file->next(); + } + + $code = trim($code); + if (strpos($code, 'getStaticVariables(); + + // Construct the context by combining the variable names and values. + foreach ($data['context'] as $name => &$value) { + if (isset($values[$name])) { + $value = $values[$name]; + } + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ClosureLocatorVisitor.php b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ClosureLocatorVisitor.php new file mode 100644 index 0000000..69f92ed --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ClosureLocatorVisitor.php @@ -0,0 +1,120 @@ +reflection = $reflection; + $this->location = [ + 'class' => null, + 'directory' => dirname($this->reflection->getFileName()), + 'file' => $this->reflection->getFileName(), + 'function' => $this->reflection->getName(), + 'line' => $this->reflection->getStartLine(), + 'method' => null, + 'namespace' => null, + 'trait' => null, + ]; + } + + public function enterNode(AstNode $node) + { + // Determine information about the closure's location + if (!$this->closureNode) { + if ($node instanceof NamespaceNode) { + $namespace = $node->name !== null + ? $node->name->toString() + : null; + $this->location['namespace'] = $namespace; + } + if ($node instanceof TraitNode) { + $this->location['trait'] = (string) $node->name; + $this->location['class'] = null; + } elseif ($node instanceof ClassNode) { + $this->location['class'] = (string) $node->name; + $this->location['trait'] = null; + } + } + + // Locate the node of the closure + if ($node instanceof ClosureNode) { + if ($node->getAttribute('startLine') == $this->location['line']) { + if ($this->closureNode) { + $line = $this->location['file'] . ':' . $node->getAttribute('startLine'); + throw new ClosureAnalysisException("Two closures were " + . "declared on the same line ({$line}) of code. Cannot " + . "determine which closure was the intended target."); + } else { + $this->closureNode = $node; + } + } + } + } + + public function leaveNode(AstNode $node) + { + // Determine information about the closure's location + if (!$this->closureNode) { + if ($node instanceof NamespaceNode) { + $this->location['namespace'] = null; + } + if ($node instanceof TraitNode) { + $this->location['trait'] = null; + } elseif ($node instanceof ClassNode) { + $this->location['class'] = null; + } + } + } + + public function afterTraverse(array $nodes) + { + if ($this->location['class']) { + $this->location['class'] = $this->location['namespace'] . '\\' . $this->location['class']; + $this->location['method'] = "{$this->location['class']}::{$this->location['function']}"; + } elseif ($this->location['trait']) { + $this->location['trait'] = $this->location['namespace'] . '\\' . $this->location['trait']; + $this->location['method'] = "{$this->location['trait']}::{$this->location['function']}"; + + // If the closure was declared in a trait, then we will do a best + // effort guess on the name of the class that used the trait. It's + // actually impossible at this point to know for sure what it is. + if ($closureScope = $this->reflection->getClosureScopeClass()) { + $this->location['class'] = $closureScope ? $closureScope->getName() : null; + } elseif ($closureThis = $this->reflection->getClosureThis()) { + $this->location['class'] = get_class($closureThis); + } + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/MagicConstantVisitor.php b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/MagicConstantVisitor.php new file mode 100644 index 0000000..219654f --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/MagicConstantVisitor.php @@ -0,0 +1,50 @@ +location = $location; + } + + public function leaveNode(AstNode $node) + { + switch ($node->getType()) { + case 'Scalar_MagicConst_Class' : + return new StringNode($this->location['class'] ?: ''); + case 'Scalar_MagicConst_Dir' : + return new StringNode($this->location['directory'] ?: ''); + case 'Scalar_MagicConst_File' : + return new StringNode($this->location['file'] ?: ''); + case 'Scalar_MagicConst_Function' : + return new StringNode($this->location['function'] ?: ''); + case 'Scalar_MagicConst_Line' : + return new NumberNode($node->getAttribute('startLine') ?: 0); + case 'Scalar_MagicConst_Method' : + return new StringNode($this->location['method'] ?: ''); + case 'Scalar_MagicConst_Namespace' : + return new StringNode($this->location['namespace'] ?: ''); + case 'Scalar_MagicConst_Trait' : + return new StringNode($this->location['trait'] ?: ''); + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ThisDetectorVisitor.php b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ThisDetectorVisitor.php new file mode 100644 index 0000000..e2997ff --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Analyzer/Visitor/ThisDetectorVisitor.php @@ -0,0 +1,27 @@ +name === 'this') { + $this->detected = true; + } + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/Exception/ClosureAnalysisException.php b/vendor/jeremeamia/superclosure/src/Exception/ClosureAnalysisException.php new file mode 100644 index 0000000..d1b0b5c --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Exception/ClosureAnalysisException.php @@ -0,0 +1,9 @@ +closure = $closure; + $this->serializer = $serializer ?: new Serializer; + } + + /** + * Return the original closure object. + * + * @return Closure + */ + public function getClosure() + { + return $this->closure; + } + + /** + * Delegates the closure invocation to the actual closure object. + * + * Important Notes: + * + * - `ReflectionFunction::invokeArgs()` should not be used here, because it + * does not work with closure bindings. + * - Args passed-by-reference lose their references when proxied through + * `__invoke()`. This is an unfortunate, but understandable, limitation + * of PHP that will probably never change. + * + * @return mixed + */ + public function __invoke() + { + return call_user_func_array($this->closure, func_get_args()); + } + + /** + * Clones the SerializableClosure with a new bound object and class scope. + * + * The method is essentially a wrapped proxy to the Closure::bindTo method. + * + * @param mixed $newthis The object to which the closure should be bound, + * or NULL for the closure to be unbound. + * @param mixed $newscope The class scope to which the closure is to be + * associated, or 'static' to keep the current one. + * If an object is given, the type of the object will + * be used instead. This determines the visibility of + * protected and private methods of the bound object. + * + * @return SerializableClosure + * @link http://www.php.net/manual/en/closure.bindto.php + */ + public function bindTo($newthis, $newscope = 'static') + { + return new self( + $this->closure->bindTo($newthis, $newscope), + $this->serializer + ); + } + + /** + * Serializes the code, context, and binding of the closure. + * + * @return string|null + * @link http://php.net/manual/en/serializable.serialize.php + */ + public function serialize() + { + try { + $this->data = $this->data ?: $this->serializer->getData($this->closure, true); + return serialize($this->data); + } catch (\Exception $e) { + trigger_error( + 'Serialization of closure failed: ' . $e->getMessage(), + E_USER_NOTICE + ); + // Note: The serialize() method of Serializable must return a string + // or null and cannot throw exceptions. + return null; + } + } + + /** + * Unserializes the closure. + * + * Unserializes the closure's data and recreates the closure using a + * simulation of its original context. The used variables (context) are + * extracted into a fresh scope prior to redefining the closure. The + * closure is also rebound to its former object and scope. + * + * @param string $serialized + * + * @throws ClosureUnserializationException + * @link http://php.net/manual/en/serializable.unserialize.php + */ + public function unserialize($serialized) + { + // Unserialize the closure data and reconstruct the closure object. + $this->data = unserialize($serialized); + $this->closure = __reconstruct_closure($this->data); + + // Throw an exception if the closure could not be reconstructed. + if (!$this->closure instanceof Closure) { + throw new ClosureUnserializationException( + 'The closure is corrupted and cannot be unserialized.' + ); + } + + // Rebind the closure to its former binding and scope. + if ($this->data['binding'] || $this->data['isStatic']) { + $this->closure = $this->closure->bindTo( + $this->data['binding'], + $this->data['scope'] + ); + } + } + + /** + * Returns closure data for `var_dump()`. + * + * @return array + */ + public function __debugInfo() + { + return $this->data ?: $this->serializer->getData($this->closure, true); + } +} + +/** + * Reconstruct a closure. + * + * HERE BE DRAGONS! + * + * The infamous `eval()` is used in this method, along with the error + * suppression operator, and variable variables (i.e., double dollar signs) to + * perform the unserialization logic. I'm sorry, world! + * + * This is also done inside a plain function instead of a method so that the + * binding and scope of the closure are null. + * + * @param array $__data Unserialized closure data. + * + * @return Closure|null + * @internal + */ +function __reconstruct_closure(array $__data) +{ + // Simulate the original context the closure was created in. + foreach ($__data['context'] as $__var_name => &$__value) { + if ($__value instanceof SerializableClosure) { + // Unbox any SerializableClosures in the context. + $__value = $__value->getClosure(); + } elseif ($__value === Serializer::RECURSION) { + // Track recursive references (there should only be one). + $__recursive_reference = $__var_name; + } + + // Import the variable into this scope. + ${$__var_name} = $__value; + } + + // Evaluate the code to recreate the closure. + try { + if (isset($__recursive_reference)) { + // Special handling for recursive closures. + @eval("\${$__recursive_reference} = {$__data['code']};"); + $__closure = ${$__recursive_reference}; + } else { + @eval("\$__closure = {$__data['code']};"); + } + } catch (\ParseError $e) { + // Discard the parse error. + } + + return isset($__closure) ? $__closure : null; +} diff --git a/vendor/jeremeamia/superclosure/src/Serializer.php b/vendor/jeremeamia/superclosure/src/Serializer.php new file mode 100644 index 0000000..732ee81 --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/Serializer.php @@ -0,0 +1,221 @@ + true, + 'context' => true, + 'binding' => true, + 'scope' => true, + 'isStatic' => true, + ]; + + /** + * The closure analyzer instance. + * + * @var ClosureAnalyzer + */ + private $analyzer; + + /** + * The HMAC key to sign serialized closures. + * + * @var string + */ + private $signingKey; + + /** + * Create a new serializer instance. + * + * @param ClosureAnalyzer|null $analyzer Closure analyzer instance. + * @param string|null $signingKey HMAC key to sign closure data. + */ + public function __construct( + ClosureAnalyzer $analyzer = null, + $signingKey = null + ) { + $this->analyzer = $analyzer ?: new DefaultAnalyzer; + $this->signingKey = $signingKey; + } + + /** + * @inheritDoc + */ + public function serialize(\Closure $closure) + { + $serialized = serialize(new SerializableClosure($closure, $this)); + + if ($serialized === null) { + throw new ClosureSerializationException( + 'The closure could not be serialized.' + ); + } + + if ($this->signingKey) { + $signature = $this->calculateSignature($serialized); + $serialized = '%' . base64_encode($signature) . $serialized; + } + + return $serialized; + } + + /** + * @inheritDoc + */ + public function unserialize($serialized) + { + // Strip off the signature from the front of the string. + $signature = null; + if ($serialized[0] === '%') { + $signature = base64_decode(substr($serialized, 1, 44)); + $serialized = substr($serialized, 45); + } + + // If a key was provided, then verify the signature. + if ($this->signingKey) { + $this->verifySignature($signature, $serialized); + } + + set_error_handler(function () {}); + $unserialized = unserialize($serialized); + restore_error_handler(); + if ($unserialized === false) { + throw new ClosureUnserializationException( + 'The closure could not be unserialized.' + ); + } elseif (!$unserialized instanceof SerializableClosure) { + throw new ClosureUnserializationException( + 'The closure did not unserialize to a SuperClosure.' + ); + } + + return $unserialized->getClosure(); + } + + /** + * @inheritDoc + */ + public function getData(\Closure $closure, $forSerialization = false) + { + // Use the closure analyzer to get data about the closure. + $data = $this->analyzer->analyze($closure); + + // If the closure data is getting retrieved solely for the purpose of + // serializing the closure, then make some modifications to the data. + if ($forSerialization) { + // If there is no reference to the binding, don't serialize it. + if (!$data['hasThis']) { + $data['binding'] = null; + } + + // Remove data about the closure that does not get serialized. + $data = array_intersect_key($data, self::$dataToKeep); + + // Wrap any other closures within the context. + foreach ($data['context'] as &$value) { + if ($value instanceof \Closure) { + $value = ($value === $closure) + ? self::RECURSION + : new SerializableClosure($value, $this); + } + } + } + + return $data; + } + + /** + * Recursively traverses and wraps all Closure objects within the value. + * + * NOTE: THIS MAY NOT WORK IN ALL USE CASES, SO USE AT YOUR OWN RISK. + * + * @param mixed $data Any variable that contains closures. + * @param SerializerInterface $serializer The serializer to use. + */ + public static function wrapClosures(&$data, SerializerInterface $serializer) + { + if ($data instanceof \Closure) { + // Handle and wrap closure objects. + $reflection = new \ReflectionFunction($data); + if ($binding = $reflection->getClosureThis()) { + self::wrapClosures($binding, $serializer); + $scope = $reflection->getClosureScopeClass(); + $scope = $scope ? $scope->getName() : 'static'; + $data = $data->bindTo($binding, $scope); + } + $data = new SerializableClosure($data, $serializer); + } elseif (is_array($data) || $data instanceof \stdClass || $data instanceof \Traversable) { + // Handle members of traversable values. + foreach ($data as &$value) { + self::wrapClosures($value, $serializer); + } + } elseif (is_object($data) && !$data instanceof \Serializable) { + // Handle objects that are not already explicitly serializable. + $reflection = new \ReflectionObject($data); + if (!$reflection->hasMethod('__sleep')) { + foreach ($reflection->getProperties() as $property) { + if ($property->isPrivate() || $property->isProtected()) { + $property->setAccessible(true); + } + $value = $property->getValue($data); + self::wrapClosures($value, $serializer); + $property->setValue($data, $value); + } + } + } + } + + /** + * Calculates a signature for a closure's serialized data. + * + * @param string $data Serialized closure data. + * + * @return string Signature of the closure's data. + */ + private function calculateSignature($data) + { + return hash_hmac('sha256', $data, $this->signingKey, true); + } + + /** + * Verifies the signature for a closure's serialized data. + * + * @param string $signature The provided signature of the data. + * @param string $data The data for which to verify the signature. + * + * @throws ClosureUnserializationException if the signature is invalid. + */ + private function verifySignature($signature, $data) + { + // Verify that the provided signature matches the calculated signature. + if (!hash_equals($signature, $this->calculateSignature($data))) { + throw new ClosureUnserializationException('The signature of the' + . ' closure\'s data is invalid, which means the serialized ' + . 'closure has been modified and is unsafe to unserialize.' + ); + } + } +} diff --git a/vendor/jeremeamia/superclosure/src/SerializerInterface.php b/vendor/jeremeamia/superclosure/src/SerializerInterface.php new file mode 100644 index 0000000..2c1a300 --- /dev/null +++ b/vendor/jeremeamia/superclosure/src/SerializerInterface.php @@ -0,0 +1,45 @@ +` (with missing property name) is now supported in error recovery mode. +* The error recovery mode is now exposed in the `php-parse` script through the `--with-recovery` + or `-r` flags. + +The following changes are also part of PHP-Parser 2.1.1: + +* The PHP 7 parser will now generate a parse error for `$var =& new Obj` assignments. +* Comments on free-standing code blocks will now be retained as comments on the first statement in + the code block. + +Version 3.0.0-alpha1 (2016-07-25) +--------------------------------- + +### Added + +* [7.1] Added support for `void` and `iterable` types. These will now be represented as strings + (instead of `Name` instances) similar to other builtin types. +* [7.1] Added support for class constant visibility. The `ClassConst` node now has a `flags` subnode + holding the visibility modifier, as well as `isPublic()`, `isProtected()` and `isPrivate()` + methods. The constructor changed to accept the additional subnode. +* [7.1] Added support for nullable types. These are represented using a new `NullableType` node + with a single `type` subnode. +* [7.1] Added support for short array destructuring syntax. This means that `Array` nodes may now + appear as the left-hand-side of assignments and foreach value targets. Additionally the array + items may now contain `null` values if elements are skipped. +* [7.1] Added support for keys in list() destructuring. The `List` subnode `vars` has been renamed + to `items` and now contains `ArrayItem`s instead of plain variables. +* [7.1] Added support for multi-catch. The `Catch` subnode `type` has been renamed to `types` and + is now an array of `Name`s. +* `Name::slice()` now supports lengths and negative offsets. This brings it in line with + `array_slice()` functionality. + +### Changed + +Due to PHP 7.1 support additions described above, the node structure changed as follows: + +* `void` and `iterable` types are now stored as strings if the PHP 7 parser is used. +* The `ClassConst` constructor changed to accept an additional `flags` subnode. +* The `Array` subnode `items` may now contain `null` elements (destructuring). +* The `List` subnode `vars` has been renamed to `items` and now contains `ArrayItem`s instead of + plain variables. +* The `Catch` subnode `type` has been renamed to `types` and is now an array of `Name`s. + +Additionally the following changes were made: + +* The `type` subnode on `Class`, `ClassMethod` and `Property` has been renamed to `flags`. The + `type` subnode has retained for backwards compatibility and is populated to the same value as + `flags`. However, writes to `type` will not update `flags`. +* The `TryCatch` subnode `finallyStmts` has been replaced with a `finally` subnode that holds an + explicit `Finally` node. This allows for more accurate attribute assignment. +* The `Trait` constructor now has the same form as the `Class` and `Interface` constructors: It + takes an array of subnodes. Unlike classes/interfaces, traits can only have a `stmts` subnode. +* The `NodeDumper` now prints class/method/property/constant modifiers, as well as the include and + use type in a textual representation, instead of only showing the number. +* All methods on `PrettyPrinter\Standard` are now protected. Previously most of them were public. + +### Removed + +* Removed support for running on PHP 5.4. It is however still possible to parse PHP 5.2-5.4 code + while running on a newer version. +* The deprecated `Comment::setLine()` and `Comment::setText()` methods have been removed. +* The deprecated `Name::set()`, `Name::setFirst()` and `Name::setLast()` methods have been removed. + +Version 2.1.1 (2016-09-16) +-------------------------- + +### Changed + +* The pretty printer will now escape all control characters in the range `\x00-\x1F` inside double + quoted strings. If no special escape sequence is available, an octal escape will be used. +* The quality of the error recovery has been improved. In particular unterminated expressions should + be handled more gracefully. +* The PHP 7 parser will now generate a parse error for `$var =& new Obj` assignments. +* Comments on free-standing code blocks will no be retained as comments on the first statement in + the code block. + +Version 2.1.0 (2016-04-19) +-------------------------- + +### Fixed + +* Properly support `B""` strings (with uppercase `B`) in a number of places. +* Fixed reformatting of indented parts in a certain non-standard comment style. + +### Added + +* Added `dumpComments` option to node dumper, to enable dumping of comments associated with nodes. +* Added `Stmt\Nop` node, that is used to collect comments located at the end of a block or at the + end of a file (without a following node with which they could otherwise be associated). +* Added `kind` attribute to `Expr\Exit` to distinguish between `exit` and `die`. +* Added `kind` attribute to `Scalar\LNumber` to distinguish between decimal, binary, octal and + hexadecimal numbers. +* Added `kind` attribute to `Expr\Array` to distinguish between `array()` and `[]`. +* Added `kind` attribute to `Scalar\String` and `Scalar\Encapsed` to distinguish between + single-quoted, double-quoted, heredoc and nowdoc string. +* Added `docLabel` attribute to `Scalar\String` and `Scalar\Encapsed`, if it is a heredoc or + nowdoc string. +* Added start file offset information to `Comment` nodes. +* Added `setReturnType()` method to function and method builders. +* Added `-h` and `--help` options to `php-parse` script. + +### Changed + +* Invalid octal literals now throw a parse error in PHP 7 mode. +* The pretty printer takes all the new attributes mentioned in the previous section into account. +* The protected `AbstractPrettyPrinter::pComments()` method no longer returns a trailing newline. +* The bundled autoloader supports library files being stored in a different directory than + `PhpParser` for easier downstream distribution. + +### Deprecated + +* The `Comment::setLine()` and `Comment::setText()` methods have been deprecated. Construct new + objects instead. + +### Removed + +* The internal (but public) method `Scalar\LNumber::parse()` has been removed. A non-internal + `LNumber::fromString()` method has been added instead. + +Version 2.0.1 (2016-02-28) +-------------------------- + +### Fixed + +* `declare() {}` and `declare();` are not semantically equivalent and will now result in different + ASTs. The format case will have an empty `stmts` array, while the latter will set `stmts` to + `null`. +* Magic constants are now supported as semi-reserved keywords. +* A shebang line like `#!/usr/bin/env php` is now allowed at the start of a namespaced file. + Previously this generated an exception. +* The `prettyPrintFile()` method will not strip a trailing `?>` from the raw data that follows a + `__halt_compiler()` statement. +* The `prettyPrintFile()` method will not strip an opening `slice()` which takes a subslice of a name. + +### Changed + +* `PhpParser\Parser` is now an interface, implemented by `Parser\Php5`, `Parser\Php7` and + `Parser\Multiple`. The `Multiple` parser will try multiple parsers, until one succeeds. +* Token constants are now defined on `PhpParser\Parser\Tokens` rather than `PhpParser\Parser`. +* The `Name->set()`, `Name->append()`, `Name->prepend()` and `Name->setFirst()` methods are + deprecated in favor of `Name::concat()` and `Name->slice()`. +* The `NodeTraverser` no longer clones nodes by default. The old behavior can be restored by + passing `true` to the constructor. +* The constructor for `Scalar` nodes no longer has a default value. E.g. `new LNumber()` should now + be written as `new LNumber(0)`. + +--- + +**This changelog only includes changes from the 2.0 series. For older changes see the +[1.x series changelog](https://github.com/nikic/PHP-Parser/blob/1.x/CHANGELOG.md) and the +[0.9 series changelog](https://github.com/nikic/PHP-Parser/blob/0.9/CHANGELOG.md).** diff --git a/vendor/nikic/php-parser/LICENSE b/vendor/nikic/php-parser/LICENSE new file mode 100644 index 0000000..920cc5b --- /dev/null +++ b/vendor/nikic/php-parser/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2011-2018 by Nikita Popov. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/nikic/php-parser/README.md b/vendor/nikic/php-parser/README.md new file mode 100644 index 0000000..f1dd929 --- /dev/null +++ b/vendor/nikic/php-parser/README.md @@ -0,0 +1,225 @@ +PHP Parser +========== + +[![Build Status](https://travis-ci.org/nikic/PHP-Parser.svg?branch=master)](https://travis-ci.org/nikic/PHP-Parser) [![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master) + +This is a PHP 5.2 to PHP 7.4 parser written in PHP. Its purpose is to simplify static code analysis and +manipulation. + +[**Documentation for version 4.x**][doc_master] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 7.4). + +[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2). + +Features +-------- + +The main features provided by this library are: + + * Parsing PHP 5 and PHP 7 code into an abstract syntax tree (AST). + * Invalid code can be parsed into a partial AST. + * The AST contains accurate location information. + * Dumping the AST in human-readable form. + * Converting an AST back to PHP code. + * Experimental: Formatting can be preserved for partially changed ASTs. + * Infrastructure to traverse and modify ASTs. + * Resolution of namespaced names. + * Evaluation of constant expressions. + * Builders to simplify AST construction for code generation. + * Converting an AST into JSON and back. + +Quick Start +----------- + +Install the library using [composer](https://getcomposer.org): + + php composer.phar require nikic/php-parser + +Parse some PHP code into an AST and dump the result in human-readable form: + +```php +create(ParserFactory::PREFER_PHP7); +try { + $ast = $parser->parse($code); +} catch (Error $error) { + echo "Parse error: {$error->getMessage()}\n"; + return; +} + +$dumper = new NodeDumper; +echo $dumper->dump($ast) . "\n"; +``` + +This dumps an AST looking something like this: + +``` +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: test + ) + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: foo + ) + default: null + ) + ) + returnType: null + stmts: array( + 0: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: var_dump + ) + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: foo + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + ) +) +``` + +Let's traverse the AST and perform some kind of modification. For example, drop all function bodies: + +```php +use PhpParser\Node; +use PhpParser\Node\Stmt\Function_; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; + +$traverser = new NodeTraverser(); +$traverser->addVisitor(new class extends NodeVisitorAbstract { + public function enterNode(Node $node) { + if ($node instanceof Function_) { + // Clean out the function body + $node->stmts = []; + } + } +}); + +$ast = $traverser->traverse($ast); +echo $dumper->dump($ast) . "\n"; +``` + +This gives us an AST where the `Function_::$stmts` are empty: + +``` +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: test + ) + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: foo + ) + default: null + ) + ) + returnType: null + stmts: array( + ) + ) +) +``` + +Finally, we can convert the new AST back to PHP code: + +```php +use PhpParser\PrettyPrinter; + +$prettyPrinter = new PrettyPrinter\Standard; +echo $prettyPrinter->prettyPrintFile($ast); +``` + +This gives us our original code, minus the `var_dump()` call inside the function: + +```php + Expr_AssignOp_BitwiseAnd +Expr_AssignBitwiseOr => Expr_AssignOp_BitwiseOr +Expr_AssignBitwiseXor => Expr_AssignOp_BitwiseXor +Expr_AssignConcat => Expr_AssignOp_Concat +Expr_AssignDiv => Expr_AssignOp_Div +Expr_AssignMinus => Expr_AssignOp_Minus +Expr_AssignMod => Expr_AssignOp_Mod +Expr_AssignMul => Expr_AssignOp_Mul +Expr_AssignPlus => Expr_AssignOp_Plus +Expr_AssignShiftLeft => Expr_AssignOp_ShiftLeft +Expr_AssignShiftRight => Expr_AssignOp_ShiftRight + +Expr_BitwiseAnd => Expr_BinaryOp_BitwiseAnd +Expr_BitwiseOr => Expr_BinaryOp_BitwiseOr +Expr_BitwiseXor => Expr_BinaryOp_BitwiseXor +Expr_BooleanAnd => Expr_BinaryOp_BooleanAnd +Expr_BooleanOr => Expr_BinaryOp_BooleanOr +Expr_Concat => Expr_BinaryOp_Concat +Expr_Div => Expr_BinaryOp_Div +Expr_Equal => Expr_BinaryOp_Equal +Expr_Greater => Expr_BinaryOp_Greater +Expr_GreaterOrEqual => Expr_BinaryOp_GreaterOrEqual +Expr_Identical => Expr_BinaryOp_Identical +Expr_LogicalAnd => Expr_BinaryOp_LogicalAnd +Expr_LogicalOr => Expr_BinaryOp_LogicalOr +Expr_LogicalXor => Expr_BinaryOp_LogicalXor +Expr_Minus => Expr_BinaryOp_Minus +Expr_Mod => Expr_BinaryOp_Mod +Expr_Mul => Expr_BinaryOp_Mul +Expr_NotEqual => Expr_BinaryOp_NotEqual +Expr_NotIdentical => Expr_BinaryOp_NotIdentical +Expr_Plus => Expr_BinaryOp_Plus +Expr_ShiftLeft => Expr_BinaryOp_ShiftLeft +Expr_ShiftRight => Expr_BinaryOp_ShiftRight +Expr_Smaller => Expr_BinaryOp_Smaller +Expr_SmallerOrEqual => Expr_BinaryOp_SmallerOrEqual + +Scalar_ClassConst => Scalar_MagicConst_Class +Scalar_DirConst => Scalar_MagicConst_Dir +Scalar_FileConst => Scalar_MagicConst_File +Scalar_FuncConst => Scalar_MagicConst_Function +Scalar_LineConst => Scalar_MagicConst_Line +Scalar_MethodConst => Scalar_MagicConst_Method +Scalar_NSConst => Scalar_MagicConst_Namespace +Scalar_TraitConst => Scalar_MagicConst_Trait +``` + +These changes may affect custom pretty printers and code comparing the return value of `Node::getType()` to specific +strings. + +### Miscellaneous + + * The classes `Template` and `TemplateLoader` have been removed. You should use some other [code generation][code_gen] + project built on top of PHP-Parser instead. + + * The `PrettyPrinterAbstract::pStmts()` method now emits a leading newline if the statement list is not empty. + Custom pretty printers should remove the explicit newline before `pStmts()` calls. + + Old: + + ```php + public function pStmt_Trait(PHPParser_Node_Stmt_Trait $node) { + return 'trait ' . $node->name + . "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}'; + } + ``` + + New: + + ```php + public function pStmt_Trait(Stmt\Trait_ $node) { + return 'trait ' . $node->name + . "\n" . '{' . $this->pStmts($node->stmts) . "\n" . '}'; + } + ``` + + [code_gen]: https://github.com/nikic/PHP-Parser/wiki/Projects-using-the-PHP-Parser#code-generation \ No newline at end of file diff --git a/vendor/nikic/php-parser/UPGRADE-2.0.md b/vendor/nikic/php-parser/UPGRADE-2.0.md new file mode 100644 index 0000000..1c61de5 --- /dev/null +++ b/vendor/nikic/php-parser/UPGRADE-2.0.md @@ -0,0 +1,74 @@ +Upgrading from PHP-Parser 1.x to 2.0 +==================================== + +### PHP version requirements + +PHP-Parser now requires PHP 5.4 or newer to run. It is however still possible to *parse* PHP 5.2 and +PHP 5.3 source code, while running on a newer version. + +### Creating a parser instance + +Parser instances should now be created through the `ParserFactory`. Old direct instantiation code +will not work, because the parser class was renamed. + +Old: + +```php +use PhpParser\Parser, PhpParser\Lexer; +$parser = new Parser(new Lexer\Emulative); +``` + +New: + +```php +use PhpParser\ParserFactory; +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +``` + +The first argument to `ParserFactory` determines how different PHP versions are handled. The +possible values are: + + * `ParserFactory::PREFER_PHP7`: Try to parse code as PHP 7. If this fails, try to parse it as PHP 5. + * `ParserFactory::PREFER_PHP5`: Try to parse code as PHP 5. If this fails, try to parse it as PHP 7. + * `ParserFactory::ONLY_PHP7`: Parse code as PHP 7. + * `ParserFactory::ONLY_PHP5`: Parse code as PHP 5. + +For most practical purposes the difference between `PREFER_PHP7` and `PREFER_PHP5` is mainly whether +a scalar type hint like `string` will be stored as `'string'` (PHP 7) or as `new Name('string')` +(PHP 5). + +To use a custom lexer, pass it as the second argument to the `create()` method: + +```php +use PhpParser\ParserFactory; +$lexer = new MyLexer; +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer); +``` + +### Rename of the `PhpParser\Parser` class + +`PhpParser\Parser` is now an interface, which is implemented by `Parser\Php5`, `Parser\Php7` and +`Parser\Multiple`. Parser tokens are now defined in `Parser\Tokens`. If you use the `ParserFactory` +described above to create your parser instance, these changes should have no further impact on you. + +### Removal of legacy aliases + +All legacy aliases for classes have been removed. This includes the old non-namespaced `PHPParser_` +classes, as well as the classes that had to be renamed for PHP 7 support. + +### Deprecations + +The `set()`, `setFirst()`, `append()` and `prepend()` methods of the `Node\Name` class have been +deprecated. Instead `Name::concat()` and `Name->slice()` should be used. + +### Miscellaneous + +* The `NodeTraverser` no longer clones nodes by default. If you want to restore the old behavior, + pass `true` to the constructor. +* The legacy node format has been removed. If you use custom nodes, they are now expected to + implement a `getSubNodeNames()` method. +* The default value for `Scalar` node constructors was removed. This means that something like + `new LNumber()` should be replaced by `new LNumber(0)`. +* String parts of encapsed strings are now represented using `Scalar\EncapsStringPart` nodes, while + previously raw strings were used. This affects the `parts` child of `Scalar\Encaps` and + `Expr\ShellExec`. \ No newline at end of file diff --git a/vendor/nikic/php-parser/UPGRADE-3.0.md b/vendor/nikic/php-parser/UPGRADE-3.0.md new file mode 100644 index 0000000..9a2895f --- /dev/null +++ b/vendor/nikic/php-parser/UPGRADE-3.0.md @@ -0,0 +1,160 @@ +Upgrading from PHP-Parser 2.x to 3.0 +==================================== + +The backwards-incompatible changes in this release may be summarized as follows: + + * The specific details of the node representation have changed in some cases, primarily to + accommodate new PHP 7.1 features. + * There have been significant changes to the error recovery implementation. This may affect you, + if you used the error recovery mode or have a custom lexer implementation. + * A number of deprecated methods were removed. + +### PHP version requirements + +PHP-Parser now requires PHP 5.5 or newer to run. It is however still possible to *parse* PHP 5.2, +5.3 and 5.4 source code, while running on a newer version. + +### Changes to the node structure + +The following changes are likely to require code changes if the respective nodes are used: + + * The `List` subnode `vars` has been renamed to `items` and now contains `ArrayItem`s instead of + plain variables. + * The `Catch` subnode `type` has been renamed to `types` and is now an array of `Name`s. + * The `TryCatch` subnode `finallyStmts` has been replaced with a `finally` subnode that holds an + explicit `Finally` node. + * The `type` subnode on `Class`, `ClassMethod` and `Property` has been renamed to `flags`. The + `type` subnode has retained for backwards compatibility and is populated to the same value as + `flags`. However, writes to `type` will not update `flags` and use of `type` is discouraged. + +The following changes are unlikely to require code changes: + + * The `ClassConst` constructor changed to accept an additional `flags` subnode. + * The `Trait` constructor now has the same form as the `Class` and `Interface` constructors: It + takes an array of subnodes. Unlike classes/interfaces, traits can only have a `stmts` subnode. + * The `Array` subnode `items` may now contain `null` elements (due to destructuring). + * `void` and `iterable` types are now stored as strings if the PHP 7 parser is used. Previously + these would have been represented as `Name` instances. + +### Changes to error recovery mode + +Previously, error recovery mode was enabled by setting the `throwOnError` option to `false` when +creating the parser, while collected errors were retrieved using the `getErrors()` method: + +```php +$lexer = ...; +$parser = (new ParserFactory)->create(ParserFactor::ONLY_PHP7, $lexer, [ + 'throwOnError' => true, +]); + +$stmts = $parser->parse($code); +$errors = $parser->getErrors(); +if ($errors) { + handleErrors($errors); +} +processAst($stmts); +``` + +Both the `throwOnError` option and the `getErrors()` method have been removed in PHP-Parser 3.0. +Instead an instance of `ErrorHandler\Collecting` should be passed to the `parse()` method: + +```php +$lexer = ...; +$parser = (new ParserFactory)->create(ParserFactor::ONLY_PHP7, $lexer); + +$errorHandler = new ErrorHandler\Collecting; +$stmts = $parser->parse($code, $errorHandler); +if ($errorHandler->hasErrors()) { + handleErrors($errorHandler->getErrors()); +} +processAst($stmts); +``` + +#### Multiple parser fallback in error recovery mode + +As a result of this change, if a `Multiple` parser is used (e.g. through the `ParserFactory` using +`PREFER_PHP7` or `PREFER_PHP5`), it will now return the result of the first *non-throwing* parse. As +parsing never throws in error recovery mode, the result from the first parser will always be +returned. + +The PHP 7 parser is a superset of the PHP 5 parser, with the exceptions that `=& new` and +`global $$foo->bar` are not supported (other differences are in representation only). The PHP 7 +parser will be able to recover from the error in both cases. For this reason, this change will +likely pass unnoticed if you do not specifically test for this syntax. + +It is possible to restore the precise previous behavior with the following code: + +```php +$lexer = ...; +$parser7 = new Parser\Php7($lexer); +$parser5 = new Parser\Php5($lexer); + +$errors7 = new ErrorHandler\Collecting(); +$stmts7 = $parser7->parse($code, $errors7); +if ($errors7->hasErrors()) { + $errors5 = new ErrorHandler\Collecting(); + $stmts5 = $parser5->parse($code, $errors5); + if (!$errors5->hasErrors()) { + // If PHP 7 parse has errors but PHP 5 parse has no errors, use PHP 5 result + return [$stmts5, $errors5]; + } +} +// If PHP 7 succeeds or both fail use PHP 7 result +return [$stmts7, $errors7]; +``` + +#### Error handling in the lexer + +In order to support recovery from lexer errors, the signature of the `startLexing()` method changed +to optionally accept an `ErrorHandler`: + +```php +// OLD +public function startLexing($code); +// NEW +public function startLexing($code, ErrorHandler $errorHandler = null); +``` + +If you use a custom lexer with overridden `startLexing()` method, it needs to be changed to accept +the extra parameter. The value should be passed on to the parent method. + +#### Error checks in node constructors + +The constructors of certain nodes used to contain additional checks for semantic errors, such as +creating a try block without either catch or finally. These checks have been moved from the node +constructors into the parser. This allows recovery from such errors, as well as representing the +resulting (invalid) AST. + +This means that certain error conditions are no longer checked for manually constructed nodes. + +### Removed methods, arguments, options + +The following methods, arguments or options have been removed: + + * `Comment::setLine()`, `Comment::setText()`: Create new `Comment` instances instead. + * `Name::set()`, `Name::setFirst()`, `Name::setLast()`, `Name::append()`, `Name::prepend()`: + Use `Name::concat()` in combination with `Name::slice()` instead. + * `Error::getRawLine()`, `Error::setRawLine()`. Use `Error::getStartLine()` and + `Error::setStartLine()` instead. + * `Parser::getErrors()`. Use `ErrorHandler\Collecting` instead. + * `$separator` argument of `Name::toString()`. Use `strtr()` instead, if you really need it. + * `$cloneNodes` argument of `NodeTraverser::__construct()`. Explicitly clone nodes in the visitor + instead. + * `throwOnError` parser option. Use `ErrorHandler\Collecting` instead. + +### Miscellaneous + + * The `NameResolver` will now resolve unqualified function and constant names in the global + namespace into fully qualified names. For example `foo()` in the global namespace resolves to + `\foo()`. For names where no static resolution is possible, a `namespacedName` attribute is + added now, containing the namespaced variant of the name. + * All methods on `PrettyPrinter\Standard` are now protected. Previously most of them were public. + The pretty printer should only be invoked using the `prettyPrint()`, `prettyPrintFile()` and + `prettyPrintExpr()` methods. + * The node dumper now prints numeric values that act as enums/flags in a string representation. + If node dumper results are used in tests, updates may be needed to account for this. + * The constants on `NameTraverserInterface` have been moved into the `NameTraverser` class. + * The emulative lexer now directly postprocesses tokens, instead of using `~__EMU__~` sequences. + This changes the protected API of the emulative lexer. + * The `Name::slice()` method now returns `null` for empty slices, previously `new Name([])` was + used. `Name::concat()` now also supports concatenation with `null`. diff --git a/vendor/nikic/php-parser/UPGRADE-4.0.md b/vendor/nikic/php-parser/UPGRADE-4.0.md new file mode 100644 index 0000000..628bdbd --- /dev/null +++ b/vendor/nikic/php-parser/UPGRADE-4.0.md @@ -0,0 +1,77 @@ +Upgrading from PHP-Parser 3.x to 4.0 +==================================== + +### PHP version requirements + +PHP-Parser now requires PHP 7.0 or newer to run. It is however still possible to *parse* PHP 5.2-5.6 +source code, while running on a newer version. + +HHVM is no longer actively supported. + +### Changes to the node structure + +* Many subnodes that previously held simple strings now store `Identifier` nodes instead (or + `VarLikeIdentifier` nodes if they have form `$ident`). The constructors of the affected nodes will + automatically convert strings to `Identifier`s and `Identifier`s implement `__toString()`. As such + some code continues to work without changes, but anything using `is_string()`, type-strict + comparisons or strict-mode may require adjustment. The following is an exhaustive list of all + affected subnodes: + + * `Const_::$name` + * `NullableType::$type` (for simple types) + * `Param::$type` (for simple types) + * `Expr\ClassConstFetch::$name` + * `Expr\Closure::$returnType` (for simple types) + * `Expr\MethodCall::$name` + * `Expr\PropertyFetch::$name` + * `Expr\StaticCall::$name` + * `Expr\StaticPropertyFetch::$name` (uses `VarLikeIdentifier`) + * `Stmt\Class_::$name` + * `Stmt\ClassMethod::$name` + * `Stmt\ClassMethod::$returnType` (for simple types) + * `Stmt\Function_::$name` + * `Stmt\Function_::$returnType` (for simple types) + * `Stmt\Goto_::$name` + * `Stmt\Interface_::$name` + * `Stmt\Label::$name` + * `Stmt\PropertyProperty::$name` (uses `VarLikeIdentifier`) + * `Stmt\TraitUseAdaptation\Alias::$method` + * `Stmt\TraitUseAdaptation\Alias::$newName` + * `Stmt\TraitUseAdaptation\Precedence::$method` + * `Stmt\Trait_::$name` + * `Stmt\UseUse::$alias` + +* Expression statements (`expr;`) are now represented using a `Stmt\Expression` node. Previously + these statements were directly represented as their constituent expression. +* The `name` subnode of `Param` has been renamed to `var` and now contains a `Variable` rather than + a plain string. +* The `name` subnode of `StaticVar` has been renamed to `var` and now contains a `Variable` rather + than a plain string. +* The `var` subnode of `ClosureUse` now contains a `Variable` rather than a plain string. +* The `var` subnode of `Catch_` now contains a `Variable` rather than a plain string. +* The `alias` subnode of `UseUse` is now `null` if no explicit alias is given. As such, + `use Foo\Bar` and `use Foo\Bar as Bar` are now represented differently. The `getAlias()` method + can be used to get the effective alias, even if it is not explicitly given. + +### Miscellaneous + +* The indentation handling in the pretty printer has been changed (this is only relevant if you + extend the pretty printer). Previously indentation was automatic, and parts were excluded using + `pNoindent()`. Now no-indent is the default and newlines that require indentation should use + `$this->nl`. + +### Removed functionality + +* Removed `type` subnode on `Class_`, `ClassMethod` and `Property` nodes. Use `flags` instead. +* The `ClassConst::isStatic()` method has been removed. Constants cannot have a static modifier. +* The `NodeTraverser` no longer accepts `false` as a return value from a `leaveNode()` method. + `NodeTraverser::REMOVE_NODE` should be returned instead. +* The `Node::setLine()` method has been removed. If you really need to, you can use `setAttribute()` + instead. +* The misspelled `Class_::VISIBILITY_MODIFER_MASK` constant has been dropped in favor of + `Class_::VISIBILITY_MODIFIER_MASK`. +* The XML serializer has been removed. As such, the classes `Serializer\XML`, and + `Unserializer\XML`, as well as the interfaces `Serializer` and `Unserializer` no longer exist. +* The `BuilderAbstract` class has been removed. It's functionality is moved into `BuilderHelpers`. + However, this is an internal class and should not be used directly. +* The `Autoloader` class has been removed in favor of relying on the Composer autoloader. diff --git a/vendor/nikic/php-parser/bin/php-parse b/vendor/nikic/php-parser/bin/php-parse new file mode 100644 index 0000000..a002f85 --- /dev/null +++ b/vendor/nikic/php-parser/bin/php-parse @@ -0,0 +1,205 @@ +#!/usr/bin/env php + [ + 'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments' +]]); +$parser = (new PhpParser\ParserFactory)->create( + PhpParser\ParserFactory::PREFER_PHP7, + $lexer +); +$dumper = new PhpParser\NodeDumper([ + 'dumpComments' => true, + 'dumpPositions' => $attributes['with-positions'], +]); +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; + +$traverser = new PhpParser\NodeTraverser(); +$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver); + +foreach ($files as $file) { + if (strpos($file, ' Code $code\n"); + } else { + if (!file_exists($file)) { + fwrite(STDERR, "File $file does not exist.\n"); + exit(1); + } + + $code = file_get_contents($file); + fwrite(STDERR, "====> File $file:\n"); + } + + if ($attributes['with-recovery']) { + $errorHandler = new PhpParser\ErrorHandler\Collecting; + $stmts = $parser->parse($code, $errorHandler); + foreach ($errorHandler->getErrors() as $error) { + $message = formatErrorMessage($error, $code, $attributes['with-column-info']); + fwrite(STDERR, $message . "\n"); + } + if (null === $stmts) { + continue; + } + } else { + try { + $stmts = $parser->parse($code); + } catch (PhpParser\Error $error) { + $message = formatErrorMessage($error, $code, $attributes['with-column-info']); + fwrite(STDERR, $message . "\n"); + exit(1); + } + } + + foreach ($operations as $operation) { + if ('dump' === $operation) { + fwrite(STDERR, "==> Node dump:\n"); + echo $dumper->dump($stmts, $code), "\n"; + } elseif ('pretty-print' === $operation) { + fwrite(STDERR, "==> Pretty print:\n"); + echo $prettyPrinter->prettyPrintFile($stmts), "\n"; + } elseif ('json-dump' === $operation) { + fwrite(STDERR, "==> JSON dump:\n"); + echo json_encode($stmts, JSON_PRETTY_PRINT), "\n"; + } elseif ('var-dump' === $operation) { + fwrite(STDERR, "==> var_dump():\n"); + var_dump($stmts); + } elseif ('resolve-names' === $operation) { + fwrite(STDERR, "==> Resolved names.\n"); + $stmts = $traverser->traverse($stmts); + } + } +} + +function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) { + if ($withColumnInfo && $e->hasColumnInfo()) { + return $e->getMessageWithColumnInfo($code); + } else { + return $e->getMessage(); + } +} + +function showHelp($error = '') { + if ($error) { + fwrite(STDERR, $error . "\n\n"); + } + fwrite($error ? STDERR : STDOUT, << false, + 'with-positions' => false, + 'with-recovery' => false, + ]; + + array_shift($args); + $parseOptions = true; + foreach ($args as $arg) { + if (!$parseOptions) { + $files[] = $arg; + continue; + } + + switch ($arg) { + case '--dump': + case '-d': + $operations[] = 'dump'; + break; + case '--pretty-print': + case '-p': + $operations[] = 'pretty-print'; + break; + case '--json-dump': + case '-j': + $operations[] = 'json-dump'; + break; + case '--var-dump': + $operations[] = 'var-dump'; + break; + case '--resolve-names': + case '-N'; + $operations[] = 'resolve-names'; + break; + case '--with-column-info': + case '-c'; + $attributes['with-column-info'] = true; + break; + case '--with-positions': + case '-P': + $attributes['with-positions'] = true; + break; + case '--with-recovery': + case '-r': + $attributes['with-recovery'] = true; + break; + case '--help': + case '-h'; + showHelp(); + break; + case '--': + $parseOptions = false; + break; + default: + if ($arg[0] === '-') { + showHelp("Invalid operation $arg."); + } else { + $files[] = $arg; + } + } + } + + return [$operations, $files, $attributes]; +} diff --git a/vendor/nikic/php-parser/composer.json b/vendor/nikic/php-parser/composer.json new file mode 100644 index 0000000..b99182e --- /dev/null +++ b/vendor/nikic/php-parser/composer.json @@ -0,0 +1,40 @@ +{ + "name": "nikic/php-parser", + "type": "library", + "description": "A PHP parser written in PHP", + "keywords": [ + "php", + "parser" + ], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Nikita Popov" + } + ], + "require": { + "php": ">=7.0", + "ext-tokenizer": "*" + }, + "require-dev": { + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + }, + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "autoload-dev": { + "psr-4": { + "PhpParser\\": "test/PhpParser/" + } + }, + "bin": [ + "bin/php-parse" + ] +} diff --git a/vendor/nikic/php-parser/doc/0_Introduction.markdown b/vendor/nikic/php-parser/doc/0_Introduction.markdown new file mode 100644 index 0000000..433cf10 --- /dev/null +++ b/vendor/nikic/php-parser/doc/0_Introduction.markdown @@ -0,0 +1,80 @@ +Introduction +============ + +This project is a PHP 5.2 to PHP 7.4 parser **written in PHP itself**. + +What is this for? +----------------- + +A parser is useful for [static analysis][0], manipulation of code and basically any other +application dealing with code programmatically. A parser constructs an [Abstract Syntax Tree][1] +(AST) of the code and thus allows dealing with it in an abstract and robust way. + +There are other ways of processing source code. One that PHP supports natively is using the +token stream generated by [`token_get_all`][2]. The token stream is much more low level than +the AST and thus has different applications: It allows to also analyze the exact formatting of +a file. On the other hand the token stream is much harder to deal with for more complex analysis. +For example, an AST abstracts away the fact that, in PHP, variables can be written as `$foo`, but also +as `$$bar`, `${'foobar'}` or even `${!${''}=barfoo()}`. You don't have to worry about recognizing +all the different syntaxes from a stream of tokens. + +Another question is: Why would I want to have a PHP parser *written in PHP*? Well, PHP might not be +a language especially suited for fast parsing, but processing the AST is much easier in PHP than it +would be in other, faster languages like C. Furthermore the people most probably wanting to do +programmatic PHP code analysis are incidentally PHP developers, not C developers. + +What can it parse? +------------------ + +The parser supports parsing PHP 5.2-7.4. + +As the parser is based on the tokens returned by `token_get_all` (which is only able to lex the PHP +version it runs on), additionally a wrapper for emulating tokens from newer versions is provided. +This allows to parse PHP 7.4 source code running on PHP 7.0, for example. This emulation is somewhat +hacky and not perfect, but it should work well on any sane code. + +What output does it produce? +---------------------------- + +The parser produces an [Abstract Syntax Tree][1] (AST) also known as a node tree. How this looks +can best be seen in an example. The program `create(ParserFactory::PREFER_PHP7); +``` + +The factory accepts a kind argument, that determines how different PHP versions are treated: + +Kind | Behavior +-----|--------- +`ParserFactory::PREFER_PHP7` | Try to parse code as PHP 7. If this fails, try to parse it as PHP 5. +`ParserFactory::PREFER_PHP5` | Try to parse code as PHP 5. If this fails, try to parse it as PHP 7. +`ParserFactory::ONLY_PHP7` | Parse code as PHP 7. +`ParserFactory::ONLY_PHP5` | Parse code as PHP 5. + +Unless you have a strong reason to use something else, `PREFER_PHP7` is a reasonable default. + +The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases +that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown). + +Subsequently you can pass PHP code (including the opening `create(ParserFactory::PREFER_PHP7); + +try { + $stmts = $parser->parse($code); + // $stmts is an array of statement nodes +} catch (Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +A parser instance can be reused to parse multiple files. + +Node dumping +------------ + +To dump the abstract syntax tree in human readable form, a `NodeDumper` can be used: + +```php +dump($stmts), "\n"; +``` + +For the sample code from the previous section, this will produce the following output: + +``` +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: printLine + ) + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: msg + ) + default: null + ) + ) + returnType: null + stmts: array( + 0: Stmt_Echo( + exprs: array( + 0: Expr_Variable( + name: msg + ) + 1: Scalar_String( + value: + + ) + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: printLine + ) + ) + args: array( + 0: Arg( + value: Scalar_String( + value: Hello World!!! + ) + byRef: false + unpack: false + ) + ) + ) + ) +) +``` + +You can also use the `php-parse` script to obtain such a node dump by calling it either with a file +name or code string: + +```sh +vendor/bin/php-parse file.php +vendor/bin/php-parse " PhpParser\Node\Stmt\Function_` + * `Stmt_Expression -> PhpParser\Node\Stmt\Expression` + +The additional `_` at the end of the first class name is necessary, because `Function` is a +reserved keyword. Many node class names in this library have a trailing `_` to avoid clashing with +a keyword. + +As PHP is a large language there are approximately 140 different nodes. In order to make working +with them easier they are grouped into three categories: + + * `PhpParser\Node\Stmt`s are statement nodes, i.e. language constructs that do not return + a value and can not occur in an expression. For example a class definition is a statement. + It doesn't return a value and you can't write something like `func(class A {});`. + * `PhpParser\Node\Expr`s are expression nodes, i.e. language constructs that return a value + and thus can occur in other expressions. Examples of expressions are `$var` + (`PhpParser\Node\Expr\Variable`) and `func()` (`PhpParser\Node\Expr\FuncCall`). + * `PhpParser\Node\Scalar`s are nodes representing scalar values, like `'string'` + (`PhpParser\Node\Scalar\String_`), `0` (`PhpParser\Node\Scalar\LNumber`) or magic constants + like `__FILE__` (`PhpParser\Node\Scalar\MagicConst\File`). All `PhpParser\Node\Scalar`s extend + `PhpParser\Node\Expr`, as scalars are expressions, too. + * There are some nodes not in either of these groups, for example names (`PhpParser\Node\Name`) + and call arguments (`PhpParser\Node\Arg`). + +The `Node\Stmt\Expression` node is somewhat confusing in that it contains both the terms "statement" +and "expression". This node distinguishes `expr`, which is a `Node\Expr`, from `expr;`, which is +an "expression statement" represented by `Node\Stmt\Expression` and containing `expr` as a sub-node. + +Every node has a (possibly zero) number of subnodes. You can access subnodes by writing +`$node->subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it +in the above example you would write `$stmts[0]->exprs`. If you wanted to access the name of the function +call, you would write `$stmts[0]->exprs[1]->name`. + +All nodes also define a `getType()` method that returns the node type. The type is the class name +without the `PhpParser\Node\` prefix and `\` replaced with `_`. It also does not contain a trailing +`_` for reserved-keyword class names. + +It is possible to associate custom metadata with a node using the `setAttribute()` method. This data +can then be retrieved using `hasAttribute()`, `getAttribute()` and `getAttributes()`. + +By default the lexer adds the `startLine`, `endLine` and `comments` attributes. `comments` is an array +of `PhpParser\Comment[\Doc]` instances. + +The start line can also be accessed using `getLine()`/`setLine()` (instead of `getAttribute('startLine')`). +The last doc comment from the `comments` attribute can be obtained using `getDocComment()`. + +Pretty printer +-------------- + +The pretty printer component compiles the AST back to PHP code. As the parser does not retain formatting +information the formatting is done using a specified scheme. Currently there is only one scheme available, +namely `PhpParser\PrettyPrinter\Standard`. + +```php +use PhpParser\Error; +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter; + +$code = "create(ParserFactory::PREFER_PHP7); +$prettyPrinter = new PrettyPrinter\Standard; + +try { + // parse + $stmts = $parser->parse($code); + + // change + $stmts[0] // the echo statement + ->exprs // sub expressions + [0] // the first of them (the string node) + ->value // it's value, i.e. 'Hi ' + = 'Hello '; // change to 'Hello ' + + // pretty print + $code = $prettyPrinter->prettyPrint($stmts); + + echo $code; +} catch (Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The above code will output: + + echo 'Hello ', hi\getTarget(); + +As you can see the source code was first parsed using `PhpParser\Parser->parse()`, then changed and then +again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`. + +The `prettyPrint()` method pretty prints a statements array. It is also possible to pretty print only a +single expression using `prettyPrintExpr()`. + +The `prettyPrintFile()` method can be used to print an entire file. This will include the opening ` Read more: [Pretty printing documentation](component/Pretty_printing.markdown) + +Node traversation +----------------- + +The above pretty printing example used the fact that the source code was known and thus it was easy to +write code that accesses a certain part of a node tree and changes it. Normally this is not the case. +Usually you want to change / analyze code in a generic way, where you don't know how the node tree is +going to look like. + +For this purpose the parser provides a component for traversing and visiting the node tree. The basic +structure of a program using this `PhpParser\NodeTraverser` looks like this: + +```php +use PhpParser\NodeTraverser; +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter; + +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +$traverser = new NodeTraverser; +$prettyPrinter = new PrettyPrinter\Standard; + +// add your visitor +$traverser->addVisitor(new MyNodeVisitor); + +try { + $code = file_get_contents($fileName); + + // parse + $stmts = $parser->parse($code); + + // traverse + $stmts = $traverser->traverse($stmts); + + // pretty print + $code = $prettyPrinter->prettyPrintFile($stmts); + + echo $code; +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The corresponding node visitor might look like this: + +```php +use PhpParser\Node; +use PhpParser\NodeVisitorAbstract; + +class MyNodeVisitor extends NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Scalar\String_) { + $node->value = 'foo'; + } + } +} +``` + +The above node visitor would change all string literals in the program to `'foo'`. + +All visitors must implement the `PhpParser\NodeVisitor` interface, which defines the following four +methods: + +```php +public function beforeTraverse(array $nodes); +public function enterNode(\PhpParser\Node $node); +public function leaveNode(\PhpParser\Node $node); +public function afterTraverse(array $nodes); +``` + +The `beforeTraverse()` method is called once before the traversal begins and is passed the nodes the +traverser was called with. This method can be used for resetting values before traversation or +preparing the tree for traversal. + +The `afterTraverse()` method is similar to the `beforeTraverse()` method, with the only difference that +it is called once after the traversal. + +The `enterNode()` and `leaveNode()` methods are called on every node, the former when it is entered, +i.e. before its subnodes are traversed, the latter when it is left. + +All four methods can either return the changed node or not return at all (i.e. `null`) in which +case the current node is not changed. + +The `enterNode()` method can additionally return the value `NodeTraverser::DONT_TRAVERSE_CHILDREN`, +which instructs the traverser to skip all children of the current node. To furthermore prevent subsequent +visitors from visiting the current node, `NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN` can be used instead. + +The `leaveNode()` method can additionally return the value `NodeTraverser::REMOVE_NODE`, in which +case the current node will be removed from the parent array. Furthermore it is possible to return +an array of nodes, which will be merged into the parent array at the offset of the current node. +I.e. if in `array(A, B, C)` the node `B` should be replaced with `array(X, Y, Z)` the result will +be `array(A, X, Y, Z, C)`. + +Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract` +class, which will define empty default implementations for all the above methods. + +> Read more: [Walking the AST](component/Walking_the_AST.markdown) + +The NameResolver node visitor +----------------------------- + +One visitor that is already bundled with the package is `PhpParser\NodeVisitor\NameResolver`. This visitor +helps you work with namespaced code by trying to resolve most names to fully qualified ones. + +For example, consider the following code: + + use A as B; + new B\C(); + +In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself. +The `NameResolver` takes care of that and resolves names as far as possible. + +After running it, most names will be fully qualified. The only names that will stay unqualified are +unqualified function and constant names. These are resolved at runtime and thus the visitor can't +know which function they are referring to. In most cases this is a non-issue as the global functions +are meant. + +Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations +that contains the namespaced name instead of only the shortname that is available via `name`. + +> Read more: [Name resolution documentation](component/Name_resolution.markdown) + +Example: Converting namespaced code to pseudo namespaces +-------------------------------------------------------- + +A small example to understand the concept: We want to convert namespaced code to pseudo namespaces +so it works on 5.2, i.e. names like `A\\B` should be converted to `A_B`. Note that such conversions +are fairly complicated if you take PHP's dynamic features into account, so our conversion will +assume that no dynamic features are used. + +We start off with the following base code: + +```php +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor\NameResolver; + +$inDir = '/some/path'; +$outDir = '/some/other/path'; + +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +$traverser = new NodeTraverser; +$prettyPrinter = new PrettyPrinter\Standard; + +$traverser->addVisitor(new NameResolver); // we will need resolved names +$traverser->addVisitor(new NamespaceConverter); // our own node visitor + +// iterate over all .php files in the directory +$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($inDir)); +$files = new \RegexIterator($files, '/\.php$/'); + +foreach ($files as $file) { + try { + // read the file that should be converted + $code = file_get_contents($file->getPathName()); + + // parse + $stmts = $parser->parse($code); + + // traverse + $stmts = $traverser->traverse($stmts); + + // pretty print + $code = $prettyPrinter->prettyPrintFile($stmts); + + // write the converted file to the target directory + file_put_contents( + substr_replace($file->getPathname(), $outDir, 0, strlen($inDir)), + $code + ); + } catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); + } +} +``` + +Now lets start with the main code, the `NodeVisitor\NamespaceConverter`. One thing it needs to do +is convert `A\\B` style names to `A_B` style ones. + +```php +use PhpParser\Node; + +class NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name(str_replace('\\', '_', $node->toString())); + } + } +} +``` + +The above code profits from the fact that the `NameResolver` already resolved all names as far as +possible, so we don't need to do that. We only need to create a string with the name parts separated +by underscores instead of backslashes. This is what `str_replace('\\', '_', $node->toString())` does. (If you want to +create a name with backslashes either write `$node->toString()` or `(string) $node`.) Then we create +a new name from the string and return it. Returning a new node replaces the old node. + +Another thing we need to do is change the class/function/const declarations. Currently they contain +only the shortname (i.e. the last part of the name), but they need to contain the complete name including +the namespace prefix: + +```php +use PhpParser\Node; +use PhpParser\Node\Stmt; + +class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name(str_replace('\\', '_', $node->toString())); + } elseif ($node instanceof Stmt\Class_ + || $node instanceof Stmt\Interface_ + || $node instanceof Stmt\Function_) { + $node->name = str_replace('\\', '_', $node->namespacedName->toString()); + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $const->name = str_replace('\\', '_', $const->namespacedName->toString()); + } + } + } +} +``` + +There is not much more to it than converting the namespaced name to string with `_` as separator. + +The last thing we need to do is remove the `namespace` and `use` statements: + +```php +use PhpParser\Node; +use PhpParser\Node\Stmt; +use PhpParser\NodeTraverser; + +class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract +{ + public function leaveNode(Node $node) { + if ($node instanceof Node\Name) { + return new Node\Name(str_replace('\\', '_', $node->toString())); + } elseif ($node instanceof Stmt\Class_ + || $node instanceof Stmt\Interface_ + || $node instanceof Stmt\Function_) { + $node->name = str_replace('\\', '_', $node->namespacedName->toString(); + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $const->name = str_replace('\\', '_', $const->namespacedName->toString()); + } + } elseif ($node instanceof Stmt\Namespace_) { + // returning an array merges is into the parent array + return $node->stmts; + } elseif ($node instanceof Stmt\Use_) { + // remove use nodes altogether + return NodeTraverser::REMOVE_NODE; + } + } +} +``` + +That's all. diff --git a/vendor/nikic/php-parser/doc/README.md b/vendor/nikic/php-parser/doc/README.md new file mode 100644 index 0000000..3b8cd76 --- /dev/null +++ b/vendor/nikic/php-parser/doc/README.md @@ -0,0 +1,46 @@ +Table of Contents +================= + +Guide +----- + + 1. [Introduction](0_Introduction.markdown) + 2. [Usage of basic components](2_Usage_of_basic_components.markdown) + +Component documentation +----------------------- + + * [Walking the AST](component/Walking_the_AST.markdown) + * Node visitors + * Modifying the AST from a visitor + * Short-circuiting traversals + * Interleaved visitors + * Simple node finding API + * Parent and sibling references + * [Name resolution](component/Name_resolution.markdown) + * Name resolver options + * Name resolution context + * [Pretty printing](component/Pretty_printing.markdown) + * Converting AST back to PHP code + * Customizing formatting + * Formatting-preserving code transformations + * [AST builders](component/AST_builders.markdown) + * Fluent builders for AST nodes + * [Lexer](component/Lexer.markdown) + * Lexer options + * Token and file positions for nodes + * Custom attributes + * [Error handling](component/Error_handling.markdown) + * Column information for errors + * Error recovery (parsing of syntactically incorrect code) + * [Constant expression evaluation](component/Constant_expression_evaluation.markdown) + * Evaluating constant/property/etc initializers + * Handling errors and unsupported expressions + * [JSON representation](component/JSON_representation.markdown) + * JSON encoding and decoding of ASTs + * [Performance](component/Performance.markdown) + * Disabling XDebug + * Reusing objects + * Garbage collection impact + * [Frequently asked questions](component/FAQ.markdown) + * Parent and sibling references diff --git a/vendor/nikic/php-parser/doc/component/AST_builders.markdown b/vendor/nikic/php-parser/doc/component/AST_builders.markdown new file mode 100644 index 0000000..60ae019 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/AST_builders.markdown @@ -0,0 +1,138 @@ +AST builders +============ + +When PHP-Parser is used to generate (or modify) code by first creating an Abstract Syntax Tree and +then using the [pretty printer](Pretty_printing.markdown) to convert it to PHP code, it can often +be tedious to manually construct AST nodes. The project provides a number of utilities to simplify +the construction of common AST nodes. + +Fluent builders +--------------- + +The library comes with a number of builders, which allow creating node trees using a fluent +interface. Builders are created using the `BuilderFactory` and the final constructed node is +accessed through `getNode()`. Fluent builders are available for +the following syntactic elements: + + * namespaces and use statements + * classes, interfaces and traits + * methods, functions and parameters + * properties + +Here is an example: + +```php +use PhpParser\BuilderFactory; +use PhpParser\PrettyPrinter; +use PhpParser\Node; + +$factory = new BuilderFactory; +$node = $factory->namespace('Name\Space') + ->addStmt($factory->use('Some\Other\Thingy')->as('SomeClass')) + ->addStmt($factory->useFunction('strlen')) + ->addStmt($factory->useConst('PHP_VERSION')) + ->addStmt($factory->class('SomeOtherClass') + ->extend('SomeClass') + ->implement('A\Few', '\Interfaces') + ->makeAbstract() // ->makeFinal() + + ->addStmt($factory->useTrait('FirstTrait')) + + ->addStmt($factory->useTrait('SecondTrait', 'ThirdTrait') + ->and('AnotherTrait') + ->with($factory->traitUseAdaptation('foo')->as('bar')) + ->with($factory->traitUseAdaptation('AnotherTrait', 'baz')->as('test')) + ->with($factory->traitUseAdaptation('AnotherTrait', 'func')->insteadof('SecondTrait'))) + + ->addStmt($factory->method('someMethod') + ->makePublic() + ->makeAbstract() // ->makeFinal() + ->setReturnType('bool') // ->makeReturnByRef() + ->addParam($factory->param('someParam')->setType('SomeClass')) + ->setDocComment('/** + * This method does something. + * + * @param SomeClass And takes a parameter + */') + ) + + ->addStmt($factory->method('anotherMethod') + ->makeProtected() // ->makePublic() [default], ->makePrivate() + ->addParam($factory->param('someParam')->setDefault('test')) + // it is possible to add manually created nodes + ->addStmt(new Node\Expr\Print_(new Node\Expr\Variable('someParam'))) + ) + + // properties will be correctly reordered above the methods + ->addStmt($factory->property('someProperty')->makeProtected()) + ->addStmt($factory->property('anotherProperty')->makePrivate()->setDefault(array(1, 2, 3))) + ) + + ->getNode() +; + +$stmts = array($node); +$prettyPrinter = new PrettyPrinter\Standard(); +echo $prettyPrinter->prettyPrintFile($stmts); +``` + +This will produce the following output with the standard pretty printer: + +```php +evaluateSilently($someExpr); +} catch (ConstExprEvaluationException $e) { + // Either the expression contains unsupported expression types, + // or an error occurred during evaluation +} +``` + +Error handling +-------------- + +The constant evaluator provides two methods, `evaluateDirectly()` and `evaluateSilently()`, which +differ in error behavior. `evaluateDirectly()` will evaluate the expression as PHP would, including +any generated warnings or Errors. `evaluateSilently()` will instead convert warnings and Errors into +a `ConstExprEvaluationException`. For example: + +```php +evaluateDirectly($expr)); // float(INF) +// Warning: Division by zero + +try { + $evaluator->evaluateSilently($expr); +} catch (ConstExprEvaluationException $e) { + var_dump($e->getPrevious()->getMessage()); // Division by zero +} +``` + +For the purposes of static analysis, you will likely want to use `evaluateSilently()` and leave +erroring expressions unevaluated. + +Unsupported expressions and evaluator fallback +---------------------------------------------- + +The constant expression evaluator supports all expression types that are permitted in constant +expressions, apart from the following: + + * `Scalar\MagicConst\*` + * `Expr\ConstFetch` (only null/false/true are handled) + * `Expr\ClassConstFetch` + +Handling these expression types requires non-local information, such as which global constants are +defined. By default, the evaluator will throw a `ConstExprEvaluationException` when it encounters +an unsupported expression type. + +It is possible to override this behavior and support resolution for these expression types by +specifying an evaluation fallback function: + +```php +getType()} cannot be evaluated"); +}); + +try { + $evalutator->evaluateSilently($someExpr); +} catch (ConstExprEvaluationException $e) { + // Handle exception +} +``` + +Implementers are advised to ensure that evaluation of indirect constant references cannot lead to +infinite recursion. For example, the following code could lead to infinite recursion if constant +lookup is implemented naively. + +```php + array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'), +)); +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer); + +try { + $stmts = $parser->parse($code); + // ... +} catch (PhpParser\Error $e) { + // ... +} +``` + +Before using column information, its availability needs to be checked with `$e->hasColumnInfo()`, as the precise +location of an error cannot always be determined. The methods for retrieving column information also have to be passed +the source code of the parsed file. An example for printing an error: + +```php +if ($e->hasColumnInfo()) { + echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code) + . ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code); + // or: + echo $e->getMessageWithColumnInfo(); +} else { + echo $e->getMessage(); +} +``` + +Both line numbers and column numbers are 1-based. EOF errors will be located at the position one past the end of the +file. + +Error recovery +-------------- + +The error behavior of the parser (and other components) is controlled by an `ErrorHandler`. Whenever an error is +encountered, `ErrorHandler::handleError()` is invoked. The default error handling strategy is `ErrorHandler\Throwing`, +which will immediately throw when an error is encountered. + +To instead collect all encountered errors into an array, while trying to continue parsing the rest of the source code, +an instance of `ErrorHandler\Collecting` can be passed to the `Parser::parse()` method. A usage example: + +```php +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::ONLY_PHP7); +$errorHandler = new PhpParser\ErrorHandler\Collecting; + +$stmts = $parser->parse($code, $errorHandler); + +if ($errorHandler->hasErrors()) { + foreach ($errorHandler->getErrors() as $error) { + // $error is an ordinary PhpParser\Error + } +} + +if (null !== $stmts) { + // $stmts is a best-effort partial AST +} +``` + +The `NameResolver` visitor also accepts an `ErrorHandler` as a constructor argument. \ No newline at end of file diff --git a/vendor/nikic/php-parser/doc/component/FAQ.markdown b/vendor/nikic/php-parser/doc/component/FAQ.markdown new file mode 100644 index 0000000..b8bf834 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/FAQ.markdown @@ -0,0 +1,68 @@ +Frequently Asked Questions +========================== + + * [How can the parent of a node be obtained?](#how-can-the-parent-of-a-node-be-obtained) + * [How can the next/previous sibling of a node be obtained?](#how-can-the-nextprevious-sibling-of-a-node-be-obtained) + +How can the parent of a node be obtained? +----- + +The AST does not store parent nodes by default. However, it is easy to add a custom parent node +attribute using a custom node visitor: + +```php +use PhpParser\Node; +use PhpParser\NodeVisitorAbstract; + +class ParentConnector extends NodeVisitorAbstract { + private $stack; + public function beforeTraverse(array $nodes) { + $this->stack = []; + } + public function enterNode(Node $node) { + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[count($this->stack)-1]); + } + $this->stack[] = $node; + } + public function leaveNode(Node $node) { + array_pop($this->stack); + } +} +``` + +After running this visitor, the parent node can be obtained through `$node->getAttribute('parent')`. + +How can the next/previous sibling of a node be obtained? +----- + +Again, siblings are not stored by default, but the visitor from the previous entry can be easily +extended to store the previous / next node with a common parent as well: + +```php +use PhpParser\Node; +use PhpParser\NodeVisitorAbstract; + +class NodeConnector extends NodeVisitorAbstract { + private $stack; + private $prev; + public function beforeTraverse(array $nodes) { + $this->stack = []; + $this->prev = null; + } + public function enterNode(Node $node) { + if (!empty($this->stack)) { + $node->setAttribute('parent', $this->stack[count($this->stack)-1]); + } + if ($this->prev && $this->prev->getAttribute('parent') == $node->getAttribute('parent')) { + $node->setAttribute('prev', $this->prev); + $this->prev->setAttribute('next', $node); + } + $this->stack[] = $node; + } + public function leaveNode(Node $node) { + $this->prev = $node; + array_pop($this->stack); + } +} +``` diff --git a/vendor/nikic/php-parser/doc/component/JSON_representation.markdown b/vendor/nikic/php-parser/doc/component/JSON_representation.markdown new file mode 100644 index 0000000..47c3429 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/JSON_representation.markdown @@ -0,0 +1,131 @@ +JSON representation +=================== + +Nodes (and comments) implement the `JsonSerializable` interface. As such, it is possible to JSON +encode the AST directly using `json_encode()`: + +```php +create(ParserFactory::PREFER_PHP7); + +try { + $stmts = $parser->parse($code); + + echo json_encode($stmts, JSON_PRETTY_PRINT), "\n"; +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +This will result in the following output (which includes attributes): + +```json +[ + { + "nodeType": "Stmt_Function", + "byRef": false, + "name": { + "nodeType": "Identifier", + "name": "printLine", + "attributes": { + "startLine": 4, + "endLine": 4 + } + }, + "params": [ + { + "nodeType": "Param", + "type": null, + "byRef": false, + "variadic": false, + "var": { + "nodeType": "Expr_Variable", + "name": "msg", + "attributes": { + "startLine": 4, + "endLine": 4 + } + }, + "default": null, + "attributes": { + "startLine": 4, + "endLine": 4 + } + } + ], + "returnType": null, + "stmts": [ + { + "nodeType": "Stmt_Echo", + "exprs": [ + { + "nodeType": "Expr_Variable", + "name": "msg", + "attributes": { + "startLine": 5, + "endLine": 5 + } + }, + { + "nodeType": "Scalar_String", + "value": "\n", + "attributes": { + "startLine": 5, + "endLine": 5, + "kind": 2 + } + } + ], + "attributes": { + "startLine": 5, + "endLine": 5 + } + } + ], + "attributes": { + "startLine": 4, + "comments": [ + { + "nodeType": "Comment_Doc", + "text": "\/** @param string $msg *\/", + "line": 3, + "filePos": 9, + "tokenPos": 2 + } + ], + "endLine": 6 + } + } +] +``` + +The JSON representation may be converted back into an AST using the `JsonDecoder`: + +```php +decode($json); +``` + +Note that not all ASTs can be represented using JSON. In particular: + + * JSON only supports UTF-8 strings. + * JSON does not support non-finite floating-point numbers. This can occur if the original source + code contains non-representable floating-pointing literals such as `1e1000`. + +If the node tree is not representable in JSON, the initial `json_encode()` call will fail. + +From the command line, a JSON dump can be obtained using `vendor/bin/php-parse -j file.php`. diff --git a/vendor/nikic/php-parser/doc/component/Lexer.markdown b/vendor/nikic/php-parser/doc/component/Lexer.markdown new file mode 100644 index 0000000..be26e38 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/Lexer.markdown @@ -0,0 +1,159 @@ +Lexer component documentation +============================= + +The lexer is responsible for providing tokens to the parser. The project comes with two lexers: `PhpParser\Lexer` and +`PhpParser\Lexer\Emulative`. The latter is an extension of the former, which adds the ability to emulate tokens of +newer PHP versions and thus allows parsing of new code on older versions. + +This documentation discusses options available for the default lexers and explains how lexers can be extended. + +Lexer options +------------- + +The two default lexers accept an `$options` array in the constructor. Currently only the `'usedAttributes'` option is +supported, which allows you to specify which attributes will be added to the AST nodes. The attributes can then be +accessed using `$node->getAttribute()`, `$node->setAttribute()`, `$node->hasAttribute()` and `$node->getAttributes()` +methods. A sample options array: + +```php +$lexer = new PhpParser\Lexer(array( + 'usedAttributes' => array( + 'comments', 'startLine', 'endLine' + ) +)); +``` + +The attributes used in this example match the default behavior of the lexer. The following attributes are supported: + + * `comments`: Array of `PhpParser\Comment` or `PhpParser\Comment\Doc` instances, representing all comments that occurred + between the previous non-discarded token and the current one. Use of this attribute is required for the + `$node->getComments()` and `$node->getDocComment()` methods to work. The attribute is also needed if you wish the pretty + printer to retain comments present in the original code. + * `startLine`: Line in which the node starts. This attribute is required for the `$node->getLine()` to work. It is also + required if syntax errors should contain line number information. + * `endLine`: Line in which the node ends. Required for `$node->getEndLine()`. + * `startTokenPos`: Offset into the token array of the first token in the node. Required for `$node->getStartTokenPos()`. + * `endTokenPos`: Offset into the token array of the last token in the node. Required for `$node->getEndTokenPos()`. + * `startFilePos`: Offset into the code string of the first character that is part of the node. Required for `$node->getStartFilePos()`. + * `endFilePos`: Offset into the code string of the last character that is part of the node. Required for `$node->getEndFilePos()`. + +### Using token positions + +> **Note:** The example in this section is outdated in that this information is directly available in the AST: While +> `$property->isPublic()` does not distinguish between `public` and `var`, directly checking `$property->flags` for +> the `$property->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0` allows making this distinction without resorting to +> tokens. However the general idea behind the example still applies in other cases. + +The token offset information is useful if you wish to examine the exact formatting used for a node. For example the AST +does not distinguish whether a property was declared using `public` or using `var`, but you can retrieve this +information based on the token position: + +```php +function isDeclaredUsingVar(array $tokens, PhpParser\Node\Stmt\Property $prop) { + $i = $prop->getAttribute('startTokenPos'); + return $tokens[$i][0] === T_VAR; +} +``` + +In order to make use of this function, you will have to provide the tokens from the lexer to your node visitor using +code similar to the following: + +```php +class MyNodeVisitor extends PhpParser\NodeVisitorAbstract { + private $tokens; + public function setTokens(array $tokens) { + $this->tokens = $tokens; + } + + public function leaveNode(PhpParser\Node $node) { + if ($node instanceof PhpParser\Node\Stmt\Property) { + var_dump(isDeclaredUsingVar($this->tokens, $node)); + } + } +} + +$lexer = new PhpParser\Lexer(array( + 'usedAttributes' => array( + 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos' + ) +)); +$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::ONLY_PHP7, $lexer); + +$visitor = new MyNodeVisitor(); +$traverser = new PhpParser\NodeTraverser(); +$traverser->addVisitor($visitor); + +try { + $stmts = $parser->parse($code); + $visitor->setTokens($lexer->getTokens()); + $stmts = $traverser->traverse($stmts); +} catch (PhpParser\Error $e) { + echo 'Parse Error: ', $e->getMessage(); +} +``` + +The same approach can also be used to perform specific modifications in the code, without changing the formatting in +other places (which is the case when using the pretty printer). + +Lexer extension +--------------- + +A lexer has to define the following public interface: + +```php +function startLexing(string $code, ErrorHandler $errorHandler = null): void; +function getTokens(): array; +function handleHaltCompiler(): string; +function getNextToken(string &$value = null, array &$startAttributes = null, array &$endAttributes = null): int; +``` + +The `startLexing()` method is invoked whenever the `parse()` method of the parser is called and is passed the source +code that is to be lexed (including the opening tag). It can be used to reset state or preprocess the source code or tokens. The +passed `ErrorHandler` should be used to report lexing errors. + +The `getTokens()` method returns the current token array, in the usual `token_get_all()` format. This method is not +used by the parser (which uses `getNextToken()`), but is useful in combination with the token position attributes. + +The `handleHaltCompiler()` method is called whenever a `T_HALT_COMPILER` token is encountered. It has to return the +remaining string after the construct (not including `();`). + +The `getNextToken()` method returns the ID of the next token (as defined by the `Parser::T_*` constants). If no more +tokens are available it must return `0`, which is the ID of the `EOF` token. Furthermore the string content of the +token should be written into the by-reference `$value` parameter (which will then be available as `$n` in the parser). + +### Attribute handling + +The other two by-ref variables `$startAttributes` and `$endAttributes` define which attributes will eventually be +assigned to the generated nodes: The parser will take the `$startAttributes` from the first token which is part of the +node and the `$endAttributes` from the last token that is part of the node. + +E.g. if the tokens `T_FUNCTION T_STRING ... '{' ... '}'` constitute a node, then the `$startAttributes` from the +`T_FUNCTION` token will be taken and the `$endAttributes` from the `'}'` token. + +An application of custom attributes is storing the exact original formatting of literals: While the parser does retain +some information about the formatting of integers (like decimal vs. hexadecimal) or strings (like used quote type), it +does not preserve the exact original formatting (e.g. leading zeros for integers or escape sequences in strings). This +can be remedied by storing the original value in an attribute: + +```php +use PhpParser\Lexer; +use PhpParser\Parser\Tokens; + +class KeepOriginalValueLexer extends Lexer // or Lexer\Emulative +{ + public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) { + $tokenId = parent::getNextToken($value, $startAttributes, $endAttributes); + + if ($tokenId == Tokens::T_CONSTANT_ENCAPSED_STRING // non-interpolated string + || $tokenId == Tokens::T_ENCAPSED_AND_WHITESPACE // interpolated string + || $tokenId == Tokens::T_LNUMBER // integer + || $tokenId == Tokens::T_DNUMBER // floating point number + ) { + // could also use $startAttributes, doesn't really matter here + $endAttributes['originalValue'] = $value; + } + + return $tokenId; + } +} +``` diff --git a/vendor/nikic/php-parser/doc/component/Name_resolution.markdown b/vendor/nikic/php-parser/doc/component/Name_resolution.markdown new file mode 100644 index 0000000..2a7eb60 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/Name_resolution.markdown @@ -0,0 +1,87 @@ +Name resolution +=============== + +Since the introduction of namespaces in PHP 5.3, literal names in PHP code are subject to a +relatively complex name resolution process, which is based on the current namespace, the current +import table state, as well the type of the referenced symbol. PHP-Parser implements name +resolution and related functionality, both as reusable logic (NameContext), as well as a node +visitor (NameResolver) based on it. + +The NameResolver visitor +------------------------ + +The `NameResolver` visitor can (and for nearly all uses of the AST, is) be applied to resolve names +to their fully-qualified form, to the degree that this is possible. + +```php +$nameResolver = new PhpParser\NodeVisitor\NameResolver; +$nodeTraverser = new PhpParser\NodeTraverser; +$nodeTraverser->addVisitor($nameResolver); + +// Resolve names +$stmts = $nodeTraverser->traverse($stmts); +``` + +In the default configuration, the name resolver will perform three actions: + + * Declarations of functions, classes, interfaces, traits and global constants will have a + `namespacedName` property added, which contains the function/class/etc name including the + namespace prefix. For historic reasons this is a **property** rather than an attribute. + * Names will be replaced by fully qualified resolved names, which are instances of + `Node\Name\FullyQualified`. + * Unqualified function and constant names inside a namespace cannot be statically resolved. Inside + a namespace `Foo`, a call to `strlen()` may either refer to the namespaced `\Foo\strlen()`, or + the global `\strlen()`. Because PHP-Parser does not have the necessary context to decide this, + such names are left unresolved. Additionally a `namespacedName` **attribute** is added to the + name node. + +The name resolver accepts an option array as the second argument, with the following default values: + +```php +$nameResolver = new PhpParser\NodeVisitor\NameResolver(null, [ + 'preserveOriginalNames' => false, + 'replaceNodes' => true, +]); +``` + +If the `preserveOriginalNames` option is enabled, then the resolved (fully qualified) name will have +an `originalName` attribute, which contains the unresolved name. + +If the `replaceNodes` option is disabled, then names will no longer be resolved in-place. Instead a +`resolvedName` attribute will be added to each name, which contains the resolved (fully qualified) +name. Once again, if an unqualified function or constant name cannot be resolved, then the +`resolvedName` attribute will not be present, and instead a `namespacedName` attribute is added. + +The `replaceNodes` attribute is useful if you wish to perform modifications on the AST, as you +probably do not wish the resoluting code to have fully resolved names as a side-effect. + +The NameContext +--------------- + +The actual name resolution logic is implemented in the `NameContext` class, which has the following +public API: + +```php +class NameContext { + public function __construct(ErrorHandler $errorHandler); + public function startNamespace(Name $namespace = null); + public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []); + + public function getNamespace(); + public function getResolvedName(Name $name, int $type); + public function getResolvedClassName(Name $name) : Name; + public function getPossibleNames(string $name, int $type) : array; + public function getShortName(string $name, int $type) : Name; +} +``` + +The `$type` parameters accept on of the `Stmt\Use_::TYPE_*` constants, which represent the three +basic symbol types in PHP (functions, constants and everything else). + +Next to name resolution, the `NameContext` also supports the reverse operation of finding a short +representation of a name given the current name resolution environment. + +The name context is intended to be used for name resolution operations outside the AST itself, such +as class names inside doc comments. A visitor running in parallel with the name resolver can access +the name context using `$nameResolver->getNameContext()`. Alternatively a visitor can use an +independent context and explicitly feed `Namespace` and `Use` nodes to it. \ No newline at end of file diff --git a/vendor/nikic/php-parser/doc/component/Performance.markdown b/vendor/nikic/php-parser/doc/component/Performance.markdown new file mode 100644 index 0000000..4281ce8 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/Performance.markdown @@ -0,0 +1,65 @@ +Performance +=========== + +Parsing is computationally expensive task, to which the PHP language is not very well suited. +Nonetheless, there are a few things you can do to improve the performance of this library, which are +described in the following. + +Xdebug +------ + +Running PHP with XDebug adds a lot of overhead, especially for code that performs many method calls. +Just by loading XDebug (without enabling profiling or other more intrusive XDebug features), you +can expect that code using PHP-Parser will be approximately *five times slower*. + +As such, you should make sure that XDebug is not loaded when using this library. Note that setting +the `xdebug.default_enable=0` ini option does *not* disable XDebug. The *only* way to disable +XDebug is to not load the extension in the first place. + +If you are building a command-line utility for use by developers (who often have XDebug enabled), +you may want to consider automatically restarting PHP with XDebug unloaded. The +[composer/xdebug-handler](https://github.com/composer/xdebug-handler) package can be used to do +this. + +If you do run with XDebug, you may need to increase the `xdebug.max_nesting_level` option to a +higher level, such as 3000. While the parser itself is recursion free, most other code working on +the AST uses recursion and will generate an error if the value of this option is too low. + +Assertions +---------- + +Assertions should be disabled in a production context by setting `zend.assertions=-1` (or +`zend.assertions=0` if set at runtime). The library currently doesn't make heavy use of assertions, +but they are used in an increasing number of places. + +Object reuse +------------ + +Many objects in this project are designed for reuse. For example, one `Parser` object can be used to +parse multiple files. + +When possible, objects should be reused rather than being newly instantiated for every use. Some +objects have expensive initialization procedures, which will be unnecessarily repeated if the object +is not reused. (Currently two objects with particularly expensive setup are lexers and pretty +printers, though the details might change between versions of this library.) + +Garbage collection +------------------ + +A limitation in PHP's cyclic garbage collector may lead to major performance degradation when the +active working set exceeds 10000 objects (or arrays). Especially when parsing very large files this +limit is significantly exceeded and PHP will spend the majority of time performing unnecessary +garbage collection attempts. + +Without GC, parsing time is roughly linear in the input size. With GC, this degenerates to quadratic +runtime for large files. While the specifics may differ, as a rough guideline you may expect a 2.5x +GC overhead for 500KB files and a 5x overhead for 1MB files. + +Because this a limitation in PHP's implementation, there is no easy way to work around this. If +possible, you should avoid parsing very large files, as they will impact overall execution time +disproportionally (and are usually generated anyway). + +Of course, you can also try to (temporarily) disable GC. By design the AST generated by PHP-Parser +is cycle-free, so the AST itself will never cause leaks with GC disabled. However, other code +(including for example the parser object itself) may hold cycles, so disabling of GC should be +approached with care. \ No newline at end of file diff --git a/vendor/nikic/php-parser/doc/component/Pretty_printing.markdown b/vendor/nikic/php-parser/doc/component/Pretty_printing.markdown new file mode 100644 index 0000000..d6198e3 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/Pretty_printing.markdown @@ -0,0 +1,96 @@ +Pretty printing +=============== + +Pretty printing is the process of converting a syntax tree back to PHP code. In its basic mode of +operation the pretty printer provided by this library will print the AST using a certain predefined +code style and will discard (nearly) all formatting of the original code. Because programmers tend +to be rather picky about their code formatting, this mode of operation is not very suitable for +refactoring code, but can be used for automatically generated code, which is usually only read for +debugging purposes. + +Basic usage +----------- + +```php +$stmts = $parser->parse($code); + +// MODIFY $stmts here + +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; +$newCode = $prettyPrinter->prettyPrintFile($stmts); +``` + +The pretty printer has three basic printing methods: `prettyPrint()`, `prettyPrintFile()` and +`prettyPrintExpr()`. The one that is most commonly useful is `prettyPrintFile()`, which takes an +array of statements and produces a full PHP file, including opening ` **Note:** This functionality is **experimental** and not yet complete. + +For automated code refactoring, migration and similar, you will usually only want to modify a small +portion of the code and leave the remainder alone. The basic pretty printer is not suitable for +this, because it will also reformat parts of the code which have not been modified. + +Since PHP-Parser 4.0, an experimental formatting-preserving pretty-printing mode is available, which +attempts to preserve the formatting of code (those AST nodes that have not changed) and only reformat +code which has been modified or newly inserted. + +Use of the formatting-preservation functionality requires some additional preparatory steps: + +```php +use PhpParser\{Lexer, NodeTraverser, NodeVisitor, Parser, PrettyPrinter}; + +$lexer = new Lexer\Emulative([ + 'usedAttributes' => [ + 'comments', + 'startLine', 'endLine', + 'startTokenPos', 'endTokenPos', + ], +]); +$parser = new Parser\Php7($lexer); + +$traverser = new NodeTraverser(); +$traverser->addVisitor(new NodeVisitor\CloningVisitor()); + +$printer = new PrettyPrinter\Standard(); + +$oldStmts = $parser->parse($code); +$oldTokens = $lexer->getTokens(); + +$newStmts = $traverser->traverse($oldStmts); + +// MODIFY $newStmts HERE + +$newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens); +``` + +If you make use of the name resolution functionality, you will likely want to disable the +`replaceNodes` option. This will add resolved names as attributes, instead of directlying modifying +the AST and causing spurious changes to the pretty printed code. For more information, see the +[name resolution documentation](Name_resolution.markdown). + +This functionality is experimental and not yet fully implemented. It should not provide incorrect +code, but it may sometimes reformat more code than necessary. Open issues are tracked in +[issue #344](https://github.com/nikic/PHP-Parser/issues/344). If you encounter problems while using +this functionality, please open an issue, so we know what to prioritize. diff --git a/vendor/nikic/php-parser/doc/component/Walking_the_AST.markdown b/vendor/nikic/php-parser/doc/component/Walking_the_AST.markdown new file mode 100644 index 0000000..efa0000 --- /dev/null +++ b/vendor/nikic/php-parser/doc/component/Walking_the_AST.markdown @@ -0,0 +1,337 @@ +Walking the AST +=============== + +The most common way to work with the AST is by using a node traverser and one or more node visitors. +As a basic example, the following code changes all literal integers in the AST into strings (e.g., +`42` becomes `'42'`.) + +```php +use PhpParser\{Node, NodeTraverser, NodeVisitorAbstract}; + +$traverser = new NodeTraverser; +$traverser->addVisitor(new class extends NodeVisitorAbstract { + public function leaveNode(Node $node) { + if ($node instanceof Node\Scalar\LNumber) { + return new Node\Scalar\String_((string) $node->value); + } + } +}); + +$stmts = ...; +$modifiedStmts = $traverser->traverse($stmts); +``` + +Node visitors +------------- + +Each node visitor implements an interface with following four methods: + +```php +interface NodeVisitor { + public function beforeTraverse(array $nodes); + public function enterNode(Node $node); + public function leaveNode(Node $node); + public function afterTraverse(array $nodes); +} +``` + +The `beforeTraverse()` and `afterTraverse()` methods are called before and after the traversal +respectively, and are passed the entire AST. They can be used to perform any necessary state +setup or cleanup. + +The `enterNode()` method is called when a node is first encountered, before its children are +processed ("preorder"). The `leaveNode()` method is called after all children have been visited +("postorder"). + +For example, if we have the following excerpt of an AST + +``` +Expr_FuncCall( + name: Name( + parts: array( + 0: printLine + ) + ) + args: array( + 0: Arg( + value: Scalar_String( + value: Hello World!!! + ) + byRef: false + unpack: false + ) + ) +) +``` + +then the enter/leave methods will be called in the following order: + +``` +enterNode(Expr_FuncCall) +enterNode(Name) +leaveNode(Name) +enterNode(Arg) +enterNode(Scalar_String) +leaveNode(Scalar_String) +leaveNode(Arg) +leaveNode(Expr_FuncCall) +``` + +A common pattern is that `enterNode` is used to collect some information and then `leaveNode` +performs modifications based on that. At the time when `leaveNode` is called, all the code inside +the node will have already been visited and necessary information collected. + +As you usually do not want to implement all four methods, it is recommended that you extend +`NodeVisitorAbstract` instead of implementing the interface directly. The abstract class provides +empty default implementations. + +Modifying the AST +----------------- + +There are a number of ways in which the AST can be modified from inside a node visitor. The first +and simplest is to simply change AST properties inside the visitor: + +```php +public function leaveNode(Node $node) { + if ($node instanceof Node\Scalar\LNumber) { + // increment all integer literals + $node->value++; + } +} +``` + +The second is to replace a node entirely by returning a new node: + +```php +public function leaveNode(Node $node) { + if ($node instanceof Node\Expr\BinaryOp\BooleanAnd) { + // Convert all $a && $b expressions into !($a && $b) + return new Node\Expr\BooleanNot($node); + } +} +``` + +Doing this is supported both inside enterNode and leaveNode. However, you have to be mindful about +where you perform the replacement: If a node is replaced in enterNode, then the recursive traversal +will also consider the children of the new node. If you aren't careful, this can lead to infinite +recursion. For example, let's take the previous code sample and use enterNode instead: + +```php +public function enterNode(Node $node) { + if ($node instanceof Node\Expr\BinaryOp\BooleanAnd) { + // Convert all $a && $b expressions into !($a && $b) + return new Node\Expr\BooleanNot($node); + } +} +``` + +Now `$a && $b` will be replaced by `!($a && $b)`. Then the traverser will go into the first (and +only) child of `!($a && $b)`, which is `$a && $b`. The transformation applies again and we end up +with `!!($a && $b)`. This will continue until PHP hits the memory limit. + +Finally, two special replacement types are supported only by leaveNode. The first is removal of a +node: + +```php +public function leaveNode(Node $node) { + if ($node instanceof Node\Stmt\Return_) { + // Remove all return statements + return NodeTraverser::REMOVE_NODE; + } +} +``` + +Node removal only works if the parent structure is an array. This means that usually it only makes +sense to remove nodes of type `Node\Stmt`, as they always occur inside statement lists (and a few +more node types like `Arg` or `Expr\ArrayItem`, which are also always part of lists). + +On the other hand, removing a `Node\Expr` does not make sense: If you have `$a * $b`, there is no +meaningful way in which the `$a` part could be removed. If you want to remove an expression, you +generally want to remove it together with a surrounding expression statement: + +```php +public function leaveNode(Node $node) { + if ($node instanceof Node\Stmt\Expression + && $node->expr instanceof Node\Expr\FuncCall + && $node->expr->name instanceof Node\Name + && $node->expr->name->toString() === 'var_dump' + ) { + return NodeTraverser::REMOVE_NODE; + } +} +``` + +This example will remove all calls to `var_dump()` which occur as expression statements. This means +that `var_dump($a);` will be removed, but `if (var_dump($a))` will not be removed (and there is no +obvious way in which it can be removed). + +Next to removing nodes, it is also possible to replace one node with multiple nodes. Again, this +only works inside leaveNode and only if the parent structure is an array. + +```php +public function leaveNode(Node $node) { + if ($node instanceof Node\Stmt\Return_ && $node->expr !== null) { + // Convert "return foo();" into "$retval = foo(); return $retval;" + $var = new Node\Expr\Variable('retval'); + return [ + new Node\Stmt\Expression(new Node\Expr\Assign($var, $node->expr)), + new Node\Stmt\Return_($var), + ]; + } +} +``` + +Short-circuiting traversal +-------------------------- + +An AST can easily contain thousands of nodes, and traversing over all of them may be slow, +especially if you have more than one visitor. In some cases, it is possible to avoid a full +traversal. + +If you are looking for all class declarations in a file (and assuming you're not interested in +anonymous classes), you know that once you've seen a class declaration, there is no point in also +checking all it's child nodes, because PHP does not allow nesting classes. In this case, you can +instruct the traverser to not recurse into the class node: + +``` +private $classes = []; +public function enterNode(Node $node) { + if ($node instanceof Node\Stmt\Class_) { + $this->classes[] = $node; + return NodeTraverser::DONT_TRAVERSE_CHILDREN; + } +} +``` + +Of course, this option is only available in enterNode, because it's already too late by the time +leaveNode is reached. + +If you are only looking for one specific node, it is also possible to abort the traversal entirely +after finding it. For example, if you are looking for the node of a class with a certain name (and +discounting exotic cases like conditionally defining a class two times), you can stop traversal +once you found it: + +``` +private $class = null; +public function enterNode(Node $node) { + if ($node instanceof Node\Stmt\Class_ && + $node->namespacedName->toString() === 'Foo\Bar\Baz' + ) { + $this->class = $node; + return NodeTraverser::STOP_TRAVERSAL; + } +} +``` + +This works both in enterNode and leaveNode. Note that this particular case can also be more easily +handled using a NodeFinder, which will be introduced below. + +Multiple visitors +----------------- + +A single traverser can be used with multiple visitors: + +```php +$traverser = new NodeTraverser; +$traverser->addVisitor($visitorA); +$traverser->addVisitor($visitorB); +$stmts = $traverser->traverse($stmts); +``` + +It is important to understand that if a traverser is run with multiple visitors, the visitors will +be interleaved. Given the following AST excerpt + +``` +Stmt_Return( + expr: Expr_Variable( + name: foobar + ) +) +``` + +the following method calls will be performed: + +``` +$visitorA->enterNode(Stmt_Return) +$visitorB->enterNode(Stmt_Return) +$visitorA->enterNode(Expr_Variable) +$visitorB->enterNode(Expr_Variable) +$visitorA->leaveNode(Expr_Variable) +$visitorB->leaveNode(Expr_Variable) +$visitorA->leaveNode(Stmt_Return) +$visitorB->leaveNode(Stmt_Return) +``` + +That is, when visiting a node, enterNode and leaveNode will always be called for all visitors. +Running multiple visitors in parallel improves performance, as the AST only has to be traversed +once. However, it is not always possible to write visitors in a way that allows interleaved +execution. In this case, you can always fall back to performing multiple traversals: + +```php +$traverserA = new NodeTraverser; +$traverserA->addVisitor($visitorA); +$traverserB = new NodeTraverser; +$traverserB->addVisitor($visitorB); +$stmts = $traverserA->traverser($stmts); +$stmts = $traverserB->traverser($stmts); +``` + +When using multiple visitors, it is important to understand how they interact with the various +special enterNode/leaveNode return values: + + * If *any* visitor returns `DONT_TRAVERSE_CHILDREN`, the children will be skipped for *all* + visitors. + * If *any* visitor returns `DONT_TRAVERSE_CURRENT_AND_CHILDREN`, the children will be skipped for *all* + visitors, and all *subsequent* visitors will not visit the current node. + * If *any* visitor returns `STOP_TRAVERSAL`, traversal is stopped for *all* visitors. + * If a visitor returns a replacement node, subsequent visitors will be passed the replacement node, + not the original one. + * If a visitor returns `REMOVE_NODE`, subsequent visitors will not see this node. + * If a visitor returns an array of replacement nodes, subsequent visitors will see neither the node + that was replaced, nor the replacement nodes. + +Simple node finding +------------------- + +While the node visitor mechanism is very flexible, creating a node visitor can be overly cumbersome +for minor tasks. For this reason a `NodeFinder` is provided, which can find AST nodes that either +satisfy a certain callback, or which are instanced of a certain node type. A couple of examples are +shown in the following: + +```php +use PhpParser\{Node, NodeFinder}; + +$nodeFinder = new NodeFinder; + +// Find all class nodes. +$classes = $nodeFinder->findInstanceOf($stmts, Node\Stmt\Class_::class); + +// Find all classes that extend another class +$extendingClasses = $nodeFinder->find($stmts, function(Node $node) { + return $node instanceof Node\Stmt\Class_ + && $node->extends !== null; +}); + +// Find first class occurring in the AST. Returns null if no class exists. +$class = $nodeFinder->findFirstInstanceOf($stmts, Node\Stmt\Class_::class); + +// Find first class that has name $name +$class = $nodeFinder->findFirst($stmts, function(Node $node) use ($name) { + return $node instanceof Node\Stmt\Class_ + && $node->resolvedName->toString() === $name; +}); +``` + +Internally, the `NodeFinder` also uses a node traverser. It only simplifies the interface for a +common use case. + +Parent and sibling references +----------------------------- + +The node visitor mechanism is somewhat rigid, in that it prescribes an order in which nodes should +be accessed: From parents to children. However, it can often be convenient to operate in the +reverse direction: When working on a node, you might want to check if the parent node satisfies a +certain property. + +PHP-Parser does not add parent (or sibling) references to nodes by itself, but you can easily +emulate this with a visitor. See the [FAQ](FAQ.markdown) for more information. diff --git a/vendor/nikic/php-parser/grammar/README.md b/vendor/nikic/php-parser/grammar/README.md new file mode 100644 index 0000000..90988cf --- /dev/null +++ b/vendor/nikic/php-parser/grammar/README.md @@ -0,0 +1,28 @@ +What do all those files mean? +============================= + + * `php5.y`: PHP 5 grammar written in a pseudo language + * `php7.y`: PHP 7 grammar written in a pseudo language + * `tokens.y`: Tokens definition shared between PHP 5 and PHP 7 grammars + * `parser.template`: A `kmyacc` parser prototype file for PHP + * `tokens.template`: A `kmyacc` prototype file for the `Tokens` class + * `rebuildParsers.php`: Preprocesses the grammar and builds the parser using `kmyacc` + +.phpy pseudo language +===================== + +The `.y` file is a normal grammar in `kmyacc` (`yacc`) style, with some transformations +applied to it: + + * Nodes are created using the syntax `Name[..., ...]`. This is transformed into + `new Name(..., ..., attributes())` + * Some function-like constructs are resolved (see `rebuildParsers.php` for a list) + +Building the parser +=================== + +In order to rebuild the parser, you need [moriyoshi's fork of kmyacc](https://github.com/moriyoshi/kmyacc-forked). +After you compiled/installed it, run the `rebuildParsers.php` script. + +By default only the `Parser.php` is built. If you want to additionally emit debug symbols and create `y.output`, run the +script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`. diff --git a/vendor/nikic/php-parser/grammar/parser.template b/vendor/nikic/php-parser/grammar/parser.template new file mode 100644 index 0000000..6166607 --- /dev/null +++ b/vendor/nikic/php-parser/grammar/parser.template @@ -0,0 +1,106 @@ +semValue +#semval($,%t) $this->semValue +#semval(%n) $stackPos-(%l-%n) +#semval(%n,%t) $stackPos-(%l-%n) + +namespace PhpParser\Parser; + +use PhpParser\Error; +use PhpParser\Node; +use PhpParser\Node\Expr; +use PhpParser\Node\Name; +use PhpParser\Node\Scalar; +use PhpParser\Node\Stmt; +#include; + +/* This is an automatically GENERATED file, which should not be manually edited. + * Instead edit one of the following: + * * the grammar files grammar/php5.y or grammar/php7.y + * * the skeleton file grammar/parser.template + * * the preprocessing script grammar/rebuildParsers.php + */ +class #(-p) extends \PhpParser\ParserAbstract +{ + protected $tokenToSymbolMapSize = #(YYMAXLEX); + protected $actionTableSize = #(YYLAST); + protected $gotoTableSize = #(YYGLAST); + + protected $invalidSymbol = #(YYBADCH); + protected $errorSymbol = #(YYINTERRTOK); + protected $defaultAction = #(YYDEFAULT); + protected $unexpectedTokenRule = #(YYUNEXPECTED); + + protected $YY2TBLSTATE = #(YY2TBLSTATE); + protected $numNonLeafStates = #(YYNLSTATES); + + protected $symbolToName = array( + #listvar terminals + ); + + protected $tokenToSymbol = array( + #listvar yytranslate + ); + + protected $action = array( + #listvar yyaction + ); + + protected $actionCheck = array( + #listvar yycheck + ); + + protected $actionBase = array( + #listvar yybase + ); + + protected $actionDefault = array( + #listvar yydefault + ); + + protected $goto = array( + #listvar yygoto + ); + + protected $gotoCheck = array( + #listvar yygcheck + ); + + protected $gotoBase = array( + #listvar yygbase + ); + + protected $gotoDefault = array( + #listvar yygdefault + ); + + protected $ruleToNonTerminal = array( + #listvar yylhs + ); + + protected $ruleToLength = array( + #listvar yylen + ); +#if -t + + protected $productions = array( + #production-strings; + ); +#endif + + protected function initReduceCallbacks() { + $this->reduceCallbacks = [ +#reduce + %n => function ($stackPos) { + %b + }, +#noact + %n => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, +#endreduce + ]; + } +} +#tailcode; diff --git a/vendor/nikic/php-parser/grammar/php5.y b/vendor/nikic/php-parser/grammar/php5.y new file mode 100644 index 0000000..e775f2c --- /dev/null +++ b/vendor/nikic/php-parser/grammar/php5.y @@ -0,0 +1,1026 @@ +%pure_parser +%expect 6 + +%tokens + +%% + +start: + top_statement_list { $$ = $this->handleNamespaces($1); } +; + +top_statement_list_ex: + top_statement_list_ex top_statement { pushNormalizing($1, $2); } + | /* empty */ { init(); } +; + +top_statement_list: + top_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +reserved_non_modifiers: + T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND + | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE + | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH + | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO + | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT + | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS + | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN +; + +semi_reserved: + reserved_non_modifiers + | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC +; + +identifier_ex: + T_STRING { $$ = Node\Identifier[$1]; } + | semi_reserved { $$ = Node\Identifier[$1]; } +; + +identifier: + T_STRING { $$ = Node\Identifier[$1]; } +; + +reserved_non_modifiers_identifier: + reserved_non_modifiers { $$ = Node\Identifier[$1]; } +; + +namespace_name_parts: + T_STRING { init($1); } + | namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); } +; + +namespace_name: + namespace_name_parts { $$ = Name[$1]; } +; + +plain_variable: + T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; } +; + +top_statement: + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | T_HALT_COMPILER + { $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; } + | T_NAMESPACE namespace_name ';' + { $$ = Stmt\Namespace_[$2, null]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($$); } + | T_NAMESPACE namespace_name '{' top_statement_list '}' + { $$ = Stmt\Namespace_[$2, $4]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($$); } + | T_NAMESPACE '{' top_statement_list '}' + { $$ = Stmt\Namespace_[null, $3]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($$); } + | T_USE use_declarations ';' { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; } + | T_USE use_type use_declarations ';' { $$ = Stmt\Use_[$3, $2]; } + | group_use_declaration ';' { $$ = $1; } + | T_CONST constant_declaration_list ';' { $$ = Stmt\Const_[$2]; } +; + +use_type: + T_FUNCTION { $$ = Stmt\Use_::TYPE_FUNCTION; } + | T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; } +; + +/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */ +group_use_declaration: + T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; } + | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; } + | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; } + | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; } +; + +unprefixed_use_declarations: + unprefixed_use_declarations ',' unprefixed_use_declaration + { push($1, $3); } + | unprefixed_use_declaration { init($1); } +; + +use_declarations: + use_declarations ',' use_declaration { push($1, $3); } + | use_declaration { init($1); } +; + +inline_use_declarations: + inline_use_declarations ',' inline_use_declaration { push($1, $3); } + | inline_use_declaration { init($1); } +; + +unprefixed_use_declaration: + namespace_name + { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); } + | namespace_name T_AS identifier + { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); } +; + +use_declaration: + unprefixed_use_declaration { $$ = $1; } + | T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; } +; + +inline_use_declaration: + unprefixed_use_declaration { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; } + | use_type unprefixed_use_declaration { $$ = $2; $$->type = $1; } +; + +constant_declaration_list: + constant_declaration_list ',' constant_declaration { push($1, $3); } + | constant_declaration { init($1); } +; + +constant_declaration: + identifier '=' static_scalar { $$ = Node\Const_[$1, $3]; } +; + +class_const_list: + class_const_list ',' class_const { push($1, $3); } + | class_const { init($1); } +; + +class_const: + identifier_ex '=' static_scalar { $$ = Node\Const_[$1, $3]; } +; + +inner_statement_list_ex: + inner_statement_list_ex inner_statement { pushNormalizing($1, $2); } + | /* empty */ { init(); } +; + +inner_statement_list: + inner_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +inner_statement: + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | T_HALT_COMPILER + { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); } +; + +non_empty_statement: + '{' inner_statement_list '}' + { + if ($2) { + $$ = $2; prependLeadingComments($$); + } else { + makeNop($$, $this->startAttributeStack[#1], $this->endAttributes); + if (null === $$) { $$ = array(); } + } + } + | T_IF parentheses_expr statement elseif_list else_single + { $$ = Stmt\If_[$2, ['stmts' => toArray($3), 'elseifs' => $4, 'else' => $5]]; } + | T_IF parentheses_expr ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';' + { $$ = Stmt\If_[$2, ['stmts' => $4, 'elseifs' => $5, 'else' => $6]]; } + | T_WHILE parentheses_expr while_statement { $$ = Stmt\While_[$2, $3]; } + | T_DO statement T_WHILE parentheses_expr ';' { $$ = Stmt\Do_ [$4, toArray($2)]; } + | T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement + { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; } + | T_SWITCH parentheses_expr switch_case_list { $$ = Stmt\Switch_[$2, $3]; } + | T_BREAK ';' { $$ = Stmt\Break_[null]; } + | T_BREAK expr ';' { $$ = Stmt\Break_[$2]; } + | T_CONTINUE ';' { $$ = Stmt\Continue_[null]; } + | T_CONTINUE expr ';' { $$ = Stmt\Continue_[$2]; } + | T_RETURN ';' { $$ = Stmt\Return_[null]; } + | T_RETURN expr ';' { $$ = Stmt\Return_[$2]; } + | T_GLOBAL global_var_list ';' { $$ = Stmt\Global_[$2]; } + | T_STATIC static_var_list ';' { $$ = Stmt\Static_[$2]; } + | T_ECHO expr_list ';' { $$ = Stmt\Echo_[$2]; } + | T_INLINE_HTML { $$ = Stmt\InlineHTML[$1]; } + | yield_expr ';' { $$ = Stmt\Expression[$1]; } + | expr ';' { $$ = Stmt\Expression[$1]; } + | T_UNSET '(' variables_list ')' ';' { $$ = Stmt\Unset_[$3]; } + | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement + { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; } + | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement + { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; } + | T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; } + | T_TRY '{' inner_statement_list '}' catches optional_finally + { $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); } + | T_THROW expr ';' { $$ = Stmt\Throw_[$2]; } + | T_GOTO identifier ';' { $$ = Stmt\Goto_[$2]; } + | identifier ':' { $$ = Stmt\Label[$1]; } + | expr error { $$ = Stmt\Expression[$1]; } + | error { $$ = array(); /* means: no statement */ } +; + +statement: + non_empty_statement { $$ = $1; } + | ';' + { makeNop($$, $this->startAttributeStack[#1], $this->endAttributes); + if ($$ === null) $$ = array(); /* means: no statement */ } +; + +catches: + /* empty */ { init(); } + | catches catch { push($1, $2); } +; + +catch: + T_CATCH '(' name plain_variable ')' '{' inner_statement_list '}' + { $$ = Stmt\Catch_[array($3), $4, $7]; } +; + +optional_finally: + /* empty */ { $$ = null; } + | T_FINALLY '{' inner_statement_list '}' { $$ = Stmt\Finally_[$3]; } +; + +variables_list: + variable { init($1); } + | variables_list ',' variable { push($1, $3); } +; + +optional_ref: + /* empty */ { $$ = false; } + | '&' { $$ = true; } +; + +optional_ellipsis: + /* empty */ { $$ = false; } + | T_ELLIPSIS { $$ = true; } +; + +function_declaration_statement: + T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}' + { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $9]]; } +; + +class_declaration_statement: + class_entry_type identifier extends_from implements_list '{' class_statement_list '}' + { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; + $this->checkClass($$, #2); } + | T_INTERFACE identifier interface_extends_list '{' class_statement_list '}' + { $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; + $this->checkInterface($$, #2); } + | T_TRAIT identifier '{' class_statement_list '}' + { $$ = Stmt\Trait_[$2, ['stmts' => $4]]; } +; + +class_entry_type: + T_CLASS { $$ = 0; } + | T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; } + | T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; } +; + +extends_from: + /* empty */ { $$ = null; } + | T_EXTENDS class_name { $$ = $2; } +; + +interface_extends_list: + /* empty */ { $$ = array(); } + | T_EXTENDS class_name_list { $$ = $2; } +; + +implements_list: + /* empty */ { $$ = array(); } + | T_IMPLEMENTS class_name_list { $$ = $2; } +; + +class_name_list: + class_name { init($1); } + | class_name_list ',' class_name { push($1, $3); } +; + +for_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDFOR ';' { $$ = $2; } +; + +foreach_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDFOREACH ';' { $$ = $2; } +; + +declare_statement: + non_empty_statement { $$ = toArray($1); } + | ';' { $$ = null; } + | ':' inner_statement_list T_ENDDECLARE ';' { $$ = $2; } +; + +declare_list: + declare_list_element { init($1); } + | declare_list ',' declare_list_element { push($1, $3); } +; + +declare_list_element: + identifier '=' static_scalar { $$ = Stmt\DeclareDeclare[$1, $3]; } +; + +switch_case_list: + '{' case_list '}' { $$ = $2; } + | '{' ';' case_list '}' { $$ = $3; } + | ':' case_list T_ENDSWITCH ';' { $$ = $2; } + | ':' ';' case_list T_ENDSWITCH ';' { $$ = $3; } +; + +case_list: + /* empty */ { init(); } + | case_list case { push($1, $2); } +; + +case: + T_CASE expr case_separator inner_statement_list_ex { $$ = Stmt\Case_[$2, $4]; } + | T_DEFAULT case_separator inner_statement_list_ex { $$ = Stmt\Case_[null, $3]; } +; + +case_separator: + ':' + | ';' +; + +while_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; } +; + +elseif_list: + /* empty */ { init(); } + | elseif_list elseif { push($1, $2); } +; + +elseif: + T_ELSEIF parentheses_expr statement { $$ = Stmt\ElseIf_[$2, toArray($3)]; } +; + +new_elseif_list: + /* empty */ { init(); } + | new_elseif_list new_elseif { push($1, $2); } +; + +new_elseif: + T_ELSEIF parentheses_expr ':' inner_statement_list { $$ = Stmt\ElseIf_[$2, $4]; } +; + +else_single: + /* empty */ { $$ = null; } + | T_ELSE statement { $$ = Stmt\Else_[toArray($2)]; } +; + +new_else_single: + /* empty */ { $$ = null; } + | T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; } +; + +foreach_variable: + variable { $$ = array($1, false); } + | '&' variable { $$ = array($2, true); } + | list_expr { $$ = array($1, false); } +; + +parameter_list: + non_empty_parameter_list { $$ = $1; } + | /* empty */ { $$ = array(); } +; + +non_empty_parameter_list: + parameter { init($1); } + | non_empty_parameter_list ',' parameter { push($1, $3); } +; + +parameter: + optional_param_type optional_ref optional_ellipsis plain_variable + { $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); } + | optional_param_type optional_ref optional_ellipsis plain_variable '=' static_scalar + { $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); } +; + +type: + name { $$ = $1; } + | T_ARRAY { $$ = Node\Identifier['array']; } + | T_CALLABLE { $$ = Node\Identifier['callable']; } +; + +optional_param_type: + /* empty */ { $$ = null; } + | type { $$ = $1; } +; + +optional_return_type: + /* empty */ { $$ = null; } + | ':' type { $$ = $2; } +; + +argument_list: + '(' ')' { $$ = array(); } + | '(' non_empty_argument_list ')' { $$ = $2; } + | '(' yield_expr ')' { $$ = array(Node\Arg[$2, false, false]); } +; + +non_empty_argument_list: + argument { init($1); } + | non_empty_argument_list ',' argument { push($1, $3); } +; + +argument: + expr { $$ = Node\Arg[$1, false, false]; } + | '&' variable { $$ = Node\Arg[$2, true, false]; } + | T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; } +; + +global_var_list: + global_var_list ',' global_var { push($1, $3); } + | global_var { init($1); } +; + +global_var: + plain_variable { $$ = $1; } + | '$' variable { $$ = Expr\Variable[$2]; } + | '$' '{' expr '}' { $$ = Expr\Variable[$3]; } +; + +static_var_list: + static_var_list ',' static_var { push($1, $3); } + | static_var { init($1); } +; + +static_var: + plain_variable { $$ = Stmt\StaticVar[$1, null]; } + | plain_variable '=' static_scalar { $$ = Stmt\StaticVar[$1, $3]; } +; + +class_statement_list_ex: + class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } } + | /* empty */ { init(); } +; + +class_statement_list: + class_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +class_statement: + variable_modifiers property_declaration_list ';' + { $$ = Stmt\Property[$1, $2]; $this->checkProperty($$, #1); } + | T_CONST class_const_list ';' { $$ = Stmt\ClassConst[$2, 0]; } + | method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body + { $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; + $this->checkClassMethod($$, #1); } + | T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; } +; + +trait_adaptations: + ';' { $$ = array(); } + | '{' trait_adaptation_list '}' { $$ = $2; } +; + +trait_adaptation_list: + /* empty */ { init(); } + | trait_adaptation_list trait_adaptation { push($1, $2); } +; + +trait_adaptation: + trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';' + { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; } + | trait_method_reference T_AS member_modifier identifier_ex ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; } + | trait_method_reference T_AS member_modifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; } + | trait_method_reference T_AS identifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; } + | trait_method_reference T_AS reserved_non_modifiers_identifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; } +; + +trait_method_reference_fully_qualified: + name T_PAAMAYIM_NEKUDOTAYIM identifier_ex { $$ = array($1, $3); } +; +trait_method_reference: + trait_method_reference_fully_qualified { $$ = $1; } + | identifier_ex { $$ = array(null, $1); } +; + +method_body: + ';' /* abstract method */ { $$ = null; } + | '{' inner_statement_list '}' { $$ = $2; } +; + +variable_modifiers: + non_empty_member_modifiers { $$ = $1; } + | T_VAR { $$ = 0; } +; + +method_modifiers: + /* empty */ { $$ = 0; } + | non_empty_member_modifiers { $$ = $1; } +; + +non_empty_member_modifiers: + member_modifier { $$ = $1; } + | non_empty_member_modifiers member_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; } +; + +member_modifier: + T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; } + | T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; } + | T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; } + | T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; } + | T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; } + | T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; } +; + +property_declaration_list: + property_declaration { init($1); } + | property_declaration_list ',' property_declaration { push($1, $3); } +; + +property_decl_name: + T_VARIABLE { $$ = Node\VarLikeIdentifier[parseVar($1)]; } +; + +property_declaration: + property_decl_name { $$ = Stmt\PropertyProperty[$1, null]; } + | property_decl_name '=' static_scalar { $$ = Stmt\PropertyProperty[$1, $3]; } +; + +expr_list: + expr_list ',' expr { push($1, $3); } + | expr { init($1); } +; + +for_expr: + /* empty */ { $$ = array(); } + | expr_list { $$ = $1; } +; + +expr: + variable { $$ = $1; } + | list_expr '=' expr { $$ = Expr\Assign[$1, $3]; } + | variable '=' expr { $$ = Expr\Assign[$1, $3]; } + | variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; } + | variable '=' '&' new_expr { $$ = Expr\AssignRef[$1, $4]; } + | new_expr { $$ = $1; } + | T_CLONE expr { $$ = Expr\Clone_[$2]; } + | variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; } + | variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; } + | variable T_MUL_EQUAL expr { $$ = Expr\AssignOp\Mul [$1, $3]; } + | variable T_DIV_EQUAL expr { $$ = Expr\AssignOp\Div [$1, $3]; } + | variable T_CONCAT_EQUAL expr { $$ = Expr\AssignOp\Concat [$1, $3]; } + | variable T_MOD_EQUAL expr { $$ = Expr\AssignOp\Mod [$1, $3]; } + | variable T_AND_EQUAL expr { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; } + | variable T_OR_EQUAL expr { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; } + | variable T_XOR_EQUAL expr { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; } + | variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; } + | variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; } + | variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; } + | variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; } + | variable T_INC { $$ = Expr\PostInc[$1]; } + | T_INC variable { $$ = Expr\PreInc [$2]; } + | variable T_DEC { $$ = Expr\PostDec[$1]; } + | T_DEC variable { $$ = Expr\PreDec [$2]; } + | expr T_BOOLEAN_OR expr { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; } + | expr T_BOOLEAN_AND expr { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; } + | expr T_LOGICAL_OR expr { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; } + | expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; } + | expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; } + | expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; } + | expr '&' expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; } + | expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; } + | expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; } + | expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; } + | expr '-' expr { $$ = Expr\BinaryOp\Minus [$1, $3]; } + | expr '*' expr { $$ = Expr\BinaryOp\Mul [$1, $3]; } + | expr '/' expr { $$ = Expr\BinaryOp\Div [$1, $3]; } + | expr '%' expr { $$ = Expr\BinaryOp\Mod [$1, $3]; } + | expr T_SL expr { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; } + | expr T_SR expr { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; } + | expr T_POW expr { $$ = Expr\BinaryOp\Pow [$1, $3]; } + | '+' expr %prec T_INC { $$ = Expr\UnaryPlus [$2]; } + | '-' expr %prec T_INC { $$ = Expr\UnaryMinus[$2]; } + | '!' expr { $$ = Expr\BooleanNot[$2]; } + | '~' expr { $$ = Expr\BitwiseNot[$2]; } + | expr T_IS_IDENTICAL expr { $$ = Expr\BinaryOp\Identical [$1, $3]; } + | expr T_IS_NOT_IDENTICAL expr { $$ = Expr\BinaryOp\NotIdentical [$1, $3]; } + | expr T_IS_EQUAL expr { $$ = Expr\BinaryOp\Equal [$1, $3]; } + | expr T_IS_NOT_EQUAL expr { $$ = Expr\BinaryOp\NotEqual [$1, $3]; } + | expr T_SPACESHIP expr { $$ = Expr\BinaryOp\Spaceship [$1, $3]; } + | expr '<' expr { $$ = Expr\BinaryOp\Smaller [$1, $3]; } + | expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; } + | expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; } + | expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; } + | expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; } + | parentheses_expr { $$ = $1; } + /* we need a separate '(' new_expr ')' rule to avoid problems caused by a s/r conflict */ + | '(' new_expr ')' { $$ = $2; } + | expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; } + | expr '?' ':' expr { $$ = Expr\Ternary[$1, null, $4]; } + | expr T_COALESCE expr { $$ = Expr\BinaryOp\Coalesce[$1, $3]; } + | T_ISSET '(' variables_list ')' { $$ = Expr\Isset_[$3]; } + | T_EMPTY '(' expr ')' { $$ = Expr\Empty_[$3]; } + | T_INCLUDE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; } + | T_INCLUDE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; } + | T_EVAL parentheses_expr { $$ = Expr\Eval_[$2]; } + | T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; } + | T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; } + | T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; } + | T_DOUBLE_CAST expr + { $attrs = attributes(); + $attrs['kind'] = $this->getFloatCastKind($1); + $$ = new Expr\Cast\Double($2, $attrs); } + | T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; } + | T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; } + | T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; } + | T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; } + | T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; } + | T_EXIT exit_expr + { $attrs = attributes(); + $attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $$ = new Expr\Exit_($2, $attrs); } + | '@' expr { $$ = Expr\ErrorSuppress[$2]; } + | scalar { $$ = $1; } + | array_expr { $$ = $1; } + | scalar_dereference { $$ = $1; } + | '`' backticks_expr '`' { $$ = Expr\ShellExec[$2]; } + | T_PRINT expr { $$ = Expr\Print_[$2]; } + | T_YIELD { $$ = Expr\Yield_[null, null]; } + | T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; } + | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type + '{' inner_statement_list '}' + { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $9]]; } + | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type + '{' inner_statement_list '}' + { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $10]]; } +; + +parentheses_expr: + '(' expr ')' { $$ = $2; } + | '(' yield_expr ')' { $$ = $2; } +; + +yield_expr: + T_YIELD expr { $$ = Expr\Yield_[$2, null]; } + | T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; } +; + +array_expr: + T_ARRAY '(' array_pair_list ')' + { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG; + $$ = new Expr\Array_($3, $attrs); } + | '[' array_pair_list ']' + { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT; + $$ = new Expr\Array_($2, $attrs); } +; + +scalar_dereference: + array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' + { $attrs = attributes(); $attrs['kind'] = strKind($1); + $$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; } + | constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + /* alternative array syntax missing intentionally */ +; + +anonymous_class: + T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}' + { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); + $this->checkClass($$[0], -1); } +; + +new_expr: + T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; } + | T_NEW anonymous_class + { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; } +; + +lexical_vars: + /* empty */ { $$ = array(); } + | T_USE '(' lexical_var_list ')' { $$ = $3; } +; + +lexical_var_list: + lexical_var { init($1); } + | lexical_var_list ',' lexical_var { push($1, $3); } +; + +lexical_var: + optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; } +; + +function_call: + name argument_list { $$ = Expr\FuncCall[$1, $2]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex argument_list + { $$ = Expr\StaticCall[$1, $3, $4]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list + { $$ = Expr\StaticCall[$1, $4, $6]; } + | static_property argument_list + { $$ = $this->fixupPhp5StaticPropCall($1, $2, attributes()); } + | variable_without_objects argument_list + { $$ = Expr\FuncCall[$1, $2]; } + | function_call '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + /* alternative array syntax missing intentionally */ +; + +class_name: + T_STATIC { $$ = Name[$1]; } + | name { $$ = $1; } +; + +name: + namespace_name_parts { $$ = Name[$1]; } + | T_NS_SEPARATOR namespace_name_parts { $$ = Name\FullyQualified[$2]; } + | T_NAMESPACE T_NS_SEPARATOR namespace_name_parts { $$ = Name\Relative[$3]; } +; + +class_name_reference: + class_name { $$ = $1; } + | dynamic_class_name_reference { $$ = $1; } +; + +dynamic_class_name_reference: + object_access_for_dcnr { $$ = $1; } + | base_variable { $$ = $1; } +; + +class_name_or_var: + class_name { $$ = $1; } + | reference_variable { $$ = $1; } +; + +object_access_for_dcnr: + base_variable T_OBJECT_OPERATOR object_property + { $$ = Expr\PropertyFetch[$1, $3]; } + | object_access_for_dcnr T_OBJECT_OPERATOR object_property + { $$ = Expr\PropertyFetch[$1, $3]; } + | object_access_for_dcnr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | object_access_for_dcnr '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } +; + +exit_expr: + /* empty */ { $$ = null; } + | '(' ')' { $$ = null; } + | parentheses_expr { $$ = $1; } +; + +backticks_expr: + /* empty */ { $$ = array(); } + | T_ENCAPSED_AND_WHITESPACE + { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`', false)]); } + | encaps_list { parseEncapsed($1, '`', false); $$ = $1; } +; + +ctor_arguments: + /* empty */ { $$ = array(); } + | argument_list { $$ = $1; } +; + +common_scalar: + T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); } + | T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; } + | T_CONSTANT_ENCAPSED_STRING + { $attrs = attributes(); $attrs['kind'] = strKind($1); + $$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); } + | T_LINE { $$ = Scalar\MagicConst\Line[]; } + | T_FILE { $$ = Scalar\MagicConst\File[]; } + | T_DIR { $$ = Scalar\MagicConst\Dir[]; } + | T_CLASS_C { $$ = Scalar\MagicConst\Class_[]; } + | T_TRAIT_C { $$ = Scalar\MagicConst\Trait_[]; } + | T_METHOD_C { $$ = Scalar\MagicConst\Method[]; } + | T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; } + | T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; } + | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC + { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), false); } + | T_START_HEREDOC T_END_HEREDOC + { $$ = $this->parseDocString($1, '', $2, attributes(), stackAttributes(#2), false); } +; + +static_scalar: + common_scalar { $$ = $1; } + | class_name T_PAAMAYIM_NEKUDOTAYIM identifier_ex { $$ = Expr\ClassConstFetch[$1, $3]; } + | name { $$ = Expr\ConstFetch[$1]; } + | T_ARRAY '(' static_array_pair_list ')' { $$ = Expr\Array_[$3]; } + | '[' static_array_pair_list ']' { $$ = Expr\Array_[$2]; } + | static_operation { $$ = $1; } +; + +static_operation: + static_scalar T_BOOLEAN_OR static_scalar { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; } + | static_scalar T_BOOLEAN_AND static_scalar { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; } + | static_scalar T_LOGICAL_OR static_scalar { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; } + | static_scalar T_LOGICAL_AND static_scalar { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; } + | static_scalar T_LOGICAL_XOR static_scalar { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; } + | static_scalar '|' static_scalar { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; } + | static_scalar '&' static_scalar { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; } + | static_scalar '^' static_scalar { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; } + | static_scalar '.' static_scalar { $$ = Expr\BinaryOp\Concat [$1, $3]; } + | static_scalar '+' static_scalar { $$ = Expr\BinaryOp\Plus [$1, $3]; } + | static_scalar '-' static_scalar { $$ = Expr\BinaryOp\Minus [$1, $3]; } + | static_scalar '*' static_scalar { $$ = Expr\BinaryOp\Mul [$1, $3]; } + | static_scalar '/' static_scalar { $$ = Expr\BinaryOp\Div [$1, $3]; } + | static_scalar '%' static_scalar { $$ = Expr\BinaryOp\Mod [$1, $3]; } + | static_scalar T_SL static_scalar { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; } + | static_scalar T_SR static_scalar { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; } + | static_scalar T_POW static_scalar { $$ = Expr\BinaryOp\Pow [$1, $3]; } + | '+' static_scalar %prec T_INC { $$ = Expr\UnaryPlus [$2]; } + | '-' static_scalar %prec T_INC { $$ = Expr\UnaryMinus[$2]; } + | '!' static_scalar { $$ = Expr\BooleanNot[$2]; } + | '~' static_scalar { $$ = Expr\BitwiseNot[$2]; } + | static_scalar T_IS_IDENTICAL static_scalar { $$ = Expr\BinaryOp\Identical [$1, $3]; } + | static_scalar T_IS_NOT_IDENTICAL static_scalar { $$ = Expr\BinaryOp\NotIdentical [$1, $3]; } + | static_scalar T_IS_EQUAL static_scalar { $$ = Expr\BinaryOp\Equal [$1, $3]; } + | static_scalar T_IS_NOT_EQUAL static_scalar { $$ = Expr\BinaryOp\NotEqual [$1, $3]; } + | static_scalar '<' static_scalar { $$ = Expr\BinaryOp\Smaller [$1, $3]; } + | static_scalar T_IS_SMALLER_OR_EQUAL static_scalar { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; } + | static_scalar '>' static_scalar { $$ = Expr\BinaryOp\Greater [$1, $3]; } + | static_scalar T_IS_GREATER_OR_EQUAL static_scalar { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; } + | static_scalar '?' static_scalar ':' static_scalar { $$ = Expr\Ternary[$1, $3, $5]; } + | static_scalar '?' ':' static_scalar { $$ = Expr\Ternary[$1, null, $4]; } + | static_scalar '[' static_scalar ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | '(' static_scalar ')' { $$ = $2; } +; + +constant: + name { $$ = Expr\ConstFetch[$1]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex + { $$ = Expr\ClassConstFetch[$1, $3]; } +; + +scalar: + common_scalar { $$ = $1; } + | constant { $$ = $1; } + | '"' encaps_list '"' + { $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); } + | T_START_HEREDOC encaps_list T_END_HEREDOC + { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); } +; + +static_array_pair_list: + /* empty */ { $$ = array(); } + | non_empty_static_array_pair_list optional_comma { $$ = $1; } +; + +optional_comma: + /* empty */ + | ',' +; + +non_empty_static_array_pair_list: + non_empty_static_array_pair_list ',' static_array_pair { push($1, $3); } + | static_array_pair { init($1); } +; + +static_array_pair: + static_scalar T_DOUBLE_ARROW static_scalar { $$ = Expr\ArrayItem[$3, $1, false]; } + | static_scalar { $$ = Expr\ArrayItem[$1, null, false]; } +; + +variable: + object_access { $$ = $1; } + | base_variable { $$ = $1; } + | function_call { $$ = $1; } + | new_expr_array_deref { $$ = $1; } +; + +new_expr_array_deref: + '(' new_expr ')' '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$2, $5]; } + | new_expr_array_deref '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + /* alternative array syntax missing intentionally */ +; + +object_access: + variable_or_new_expr T_OBJECT_OPERATOR object_property + { $$ = Expr\PropertyFetch[$1, $3]; } + | variable_or_new_expr T_OBJECT_OPERATOR object_property argument_list + { $$ = Expr\MethodCall[$1, $3, $4]; } + | object_access argument_list { $$ = Expr\FuncCall[$1, $2]; } + | object_access '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | object_access '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } +; + +variable_or_new_expr: + variable { $$ = $1; } + | '(' new_expr ')' { $$ = $2; } +; + +variable_without_objects: + reference_variable { $$ = $1; } + | '$' variable_without_objects { $$ = Expr\Variable[$2]; } +; + +base_variable: + variable_without_objects { $$ = $1; } + | static_property { $$ = $1; } +; + +static_property: + class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '$' reference_variable + { $$ = Expr\StaticPropertyFetch[$1, $4]; } + | static_property_with_arrays { $$ = $1; } +; + +static_property_simple_name: + T_VARIABLE + { $var = parseVar($1); $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; } +; + +static_property_with_arrays: + class_name_or_var T_PAAMAYIM_NEKUDOTAYIM static_property_simple_name + { $$ = Expr\StaticPropertyFetch[$1, $3]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '$' '{' expr '}' + { $$ = Expr\StaticPropertyFetch[$1, $5]; } + | static_property_with_arrays '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | static_property_with_arrays '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } +; + +reference_variable: + reference_variable '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | reference_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | plain_variable { $$ = $1; } + | '$' '{' expr '}' { $$ = Expr\Variable[$3]; } +; + +dim_offset: + /* empty */ { $$ = null; } + | expr { $$ = $1; } +; + +object_property: + identifier { $$ = $1; } + | '{' expr '}' { $$ = $2; } + | variable_without_objects { $$ = $1; } + | error { $$ = Expr\Error[]; $this->errorState = 2; } +; + +list_expr: + T_LIST '(' list_expr_elements ')' { $$ = Expr\List_[$3]; } +; + +list_expr_elements: + list_expr_elements ',' list_expr_element { push($1, $3); } + | list_expr_element { init($1); } +; + +list_expr_element: + variable { $$ = Expr\ArrayItem[$1, null, false]; } + | list_expr { $$ = Expr\ArrayItem[$1, null, false]; } + | /* empty */ { $$ = null; } +; + +array_pair_list: + /* empty */ { $$ = array(); } + | non_empty_array_pair_list optional_comma { $$ = $1; } +; + +non_empty_array_pair_list: + non_empty_array_pair_list ',' array_pair { push($1, $3); } + | array_pair { init($1); } +; + +array_pair: + expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; } + | expr { $$ = Expr\ArrayItem[$1, null, false]; } + | expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; } + | '&' variable { $$ = Expr\ArrayItem[$2, null, true]; } + | T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; } +; + +encaps_list: + encaps_list encaps_var { push($1, $2); } + | encaps_list encaps_string_part { push($1, $2); } + | encaps_var { init($1); } + | encaps_string_part encaps_var { init($1, $2); } +; + +encaps_string_part: + T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; } +; + +encaps_str_varname: + T_STRING_VARNAME { $$ = Expr\Variable[$1]; } +; + +encaps_var: + plain_variable { $$ = $1; } + | plain_variable '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | plain_variable T_OBJECT_OPERATOR identifier { $$ = Expr\PropertyFetch[$1, $3]; } + | T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; } + | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; } + | T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}' + { $$ = Expr\ArrayDimFetch[$2, $4]; } + | T_CURLY_OPEN variable '}' { $$ = $2; } +; + +encaps_var_offset: + T_STRING { $$ = Scalar\String_[$1]; } + | T_NUM_STRING { $$ = $this->parseNumString($1, attributes()); } + | plain_variable { $$ = $1; } +; + +%% diff --git a/vendor/nikic/php-parser/grammar/php7.y b/vendor/nikic/php-parser/grammar/php7.y new file mode 100644 index 0000000..23b193b --- /dev/null +++ b/vendor/nikic/php-parser/grammar/php7.y @@ -0,0 +1,1023 @@ +%pure_parser +%expect 2 + +%tokens + +%% + +start: + top_statement_list { $$ = $this->handleNamespaces($1); } +; + +top_statement_list_ex: + top_statement_list_ex top_statement { pushNormalizing($1, $2); } + | /* empty */ { init(); } +; + +top_statement_list: + top_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +reserved_non_modifiers: + T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND + | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE + | T_ENDWHILE | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH + | T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO + | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT + | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS + | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN +; + +semi_reserved: + reserved_non_modifiers + | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC +; + +identifier_ex: + T_STRING { $$ = Node\Identifier[$1]; } + | semi_reserved { $$ = Node\Identifier[$1]; } +; + +identifier: + T_STRING { $$ = Node\Identifier[$1]; } +; + +reserved_non_modifiers_identifier: + reserved_non_modifiers { $$ = Node\Identifier[$1]; } +; + +namespace_name_parts: + T_STRING { init($1); } + | namespace_name_parts T_NS_SEPARATOR T_STRING { push($1, $3); } +; + +namespace_name: + namespace_name_parts { $$ = Name[$1]; } +; + +plain_variable: + T_VARIABLE { $$ = Expr\Variable[parseVar($1)]; } +; + +semi: + ';' { /* nothing */ } + | error { /* nothing */ } +; + +no_comma: + /* empty */ { /* nothing */ } + | ',' { $this->emitError(new Error('A trailing comma is not allowed here', attributes())); } +; + +optional_comma: + /* empty */ + | ',' + +top_statement: + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | T_HALT_COMPILER + { $$ = Stmt\HaltCompiler[$this->lexer->handleHaltCompiler()]; } + | T_NAMESPACE namespace_name semi + { $$ = Stmt\Namespace_[$2, null]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($$); } + | T_NAMESPACE namespace_name '{' top_statement_list '}' + { $$ = Stmt\Namespace_[$2, $4]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($$); } + | T_NAMESPACE '{' top_statement_list '}' + { $$ = Stmt\Namespace_[null, $3]; + $$->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($$); } + | T_USE use_declarations semi { $$ = Stmt\Use_[$2, Stmt\Use_::TYPE_NORMAL]; } + | T_USE use_type use_declarations semi { $$ = Stmt\Use_[$3, $2]; } + | group_use_declaration semi { $$ = $1; } + | T_CONST constant_declaration_list semi { $$ = Stmt\Const_[$2]; } +; + +use_type: + T_FUNCTION { $$ = Stmt\Use_::TYPE_FUNCTION; } + | T_CONST { $$ = Stmt\Use_::TYPE_CONSTANT; } +; + +/* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */ +group_use_declaration: + T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; } + | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; } + | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; } + | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; } +; + +unprefixed_use_declarations: + non_empty_unprefixed_use_declarations optional_comma { $$ = $1; } +; + +non_empty_unprefixed_use_declarations: + non_empty_unprefixed_use_declarations ',' unprefixed_use_declaration + { push($1, $3); } + | unprefixed_use_declaration { init($1); } +; + +use_declarations: + non_empty_use_declarations no_comma { $$ = $1; } +; + +non_empty_use_declarations: + non_empty_use_declarations ',' use_declaration { push($1, $3); } + | use_declaration { init($1); } +; + +inline_use_declarations: + non_empty_inline_use_declarations optional_comma { $$ = $1; } +; + +non_empty_inline_use_declarations: + non_empty_inline_use_declarations ',' inline_use_declaration + { push($1, $3); } + | inline_use_declaration { init($1); } +; + +unprefixed_use_declaration: + namespace_name + { $$ = Stmt\UseUse[$1, null, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #1); } + | namespace_name T_AS identifier + { $$ = Stmt\UseUse[$1, $3, Stmt\Use_::TYPE_UNKNOWN]; $this->checkUseUse($$, #3); } +; + +use_declaration: + unprefixed_use_declaration { $$ = $1; } + | T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; } +; + +inline_use_declaration: + unprefixed_use_declaration { $$ = $1; $$->type = Stmt\Use_::TYPE_NORMAL; } + | use_type unprefixed_use_declaration { $$ = $2; $$->type = $1; } +; + +constant_declaration_list: + non_empty_constant_declaration_list no_comma { $$ = $1; } +; + +non_empty_constant_declaration_list: + non_empty_constant_declaration_list ',' constant_declaration + { push($1, $3); } + | constant_declaration { init($1); } +; + +constant_declaration: + identifier '=' expr { $$ = Node\Const_[$1, $3]; } +; + +class_const_list: + non_empty_class_const_list no_comma { $$ = $1; } +; + +non_empty_class_const_list: + non_empty_class_const_list ',' class_const { push($1, $3); } + | class_const { init($1); } +; + +class_const: + identifier_ex '=' expr { $$ = Node\Const_[$1, $3]; } +; + +inner_statement_list_ex: + inner_statement_list_ex inner_statement { pushNormalizing($1, $2); } + | /* empty */ { init(); } +; + +inner_statement_list: + inner_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +inner_statement: + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | class_declaration_statement { $$ = $1; } + | T_HALT_COMPILER + { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); } +; + +non_empty_statement: + '{' inner_statement_list '}' + { + if ($2) { + $$ = $2; prependLeadingComments($$); + } else { + makeNop($$, $this->startAttributeStack[#1], $this->endAttributes); + if (null === $$) { $$ = array(); } + } + } + | T_IF '(' expr ')' statement elseif_list else_single + { $$ = Stmt\If_[$3, ['stmts' => toArray($5), 'elseifs' => $6, 'else' => $7]]; } + | T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';' + { $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; } + | T_WHILE '(' expr ')' while_statement { $$ = Stmt\While_[$3, $5]; } + | T_DO statement T_WHILE '(' expr ')' ';' { $$ = Stmt\Do_ [$5, toArray($2)]; } + | T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement + { $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; } + | T_SWITCH '(' expr ')' switch_case_list { $$ = Stmt\Switch_[$3, $5]; } + | T_BREAK optional_expr semi { $$ = Stmt\Break_[$2]; } + | T_CONTINUE optional_expr semi { $$ = Stmt\Continue_[$2]; } + | T_RETURN optional_expr semi { $$ = Stmt\Return_[$2]; } + | T_GLOBAL global_var_list semi { $$ = Stmt\Global_[$2]; } + | T_STATIC static_var_list semi { $$ = Stmt\Static_[$2]; } + | T_ECHO expr_list semi { $$ = Stmt\Echo_[$2]; } + | T_INLINE_HTML { $$ = Stmt\InlineHTML[$1]; } + | expr semi { $$ = Stmt\Expression[$1]; } + | T_UNSET '(' variables_list ')' semi { $$ = Stmt\Unset_[$3]; } + | T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement + { $$ = Stmt\Foreach_[$3, $5[0], ['keyVar' => null, 'byRef' => $5[1], 'stmts' => $7]]; } + | T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement + { $$ = Stmt\Foreach_[$3, $7[0], ['keyVar' => $5, 'byRef' => $7[1], 'stmts' => $9]]; } + | T_FOREACH '(' expr error ')' foreach_statement + { $$ = Stmt\Foreach_[$3, new Expr\Error(stackAttributes(#4)), ['stmts' => $6]]; } + | T_DECLARE '(' declare_list ')' declare_statement { $$ = Stmt\Declare_[$3, $5]; } + | T_TRY '{' inner_statement_list '}' catches optional_finally + { $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); } + | T_THROW expr semi { $$ = Stmt\Throw_[$2]; } + | T_GOTO identifier semi { $$ = Stmt\Goto_[$2]; } + | identifier ':' { $$ = Stmt\Label[$1]; } + | error { $$ = array(); /* means: no statement */ } +; + +statement: + non_empty_statement { $$ = $1; } + | ';' + { makeNop($$, $this->startAttributeStack[#1], $this->endAttributes); + if ($$ === null) $$ = array(); /* means: no statement */ } +; + +catches: + /* empty */ { init(); } + | catches catch { push($1, $2); } +; + +name_union: + name { init($1); } + | name_union '|' name { push($1, $3); } +; + +catch: + T_CATCH '(' name_union plain_variable ')' '{' inner_statement_list '}' + { $$ = Stmt\Catch_[$3, $4, $7]; } +; + +optional_finally: + /* empty */ { $$ = null; } + | T_FINALLY '{' inner_statement_list '}' { $$ = Stmt\Finally_[$3]; } +; + +variables_list: + non_empty_variables_list optional_comma { $$ = $1; } +; + +non_empty_variables_list: + variable { init($1); } + | non_empty_variables_list ',' variable { push($1, $3); } +; + +optional_ref: + /* empty */ { $$ = false; } + | '&' { $$ = true; } +; + +optional_ellipsis: + /* empty */ { $$ = false; } + | T_ELLIPSIS { $$ = true; } +; + +block_or_error: + '{' inner_statement_list '}' { $$ = $2; } + | error { $$ = []; } +; + +function_declaration_statement: + T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type block_or_error + { $$ = Stmt\Function_[$3, ['byRef' => $2, 'params' => $5, 'returnType' => $7, 'stmts' => $8]]; } +; + +class_declaration_statement: + class_entry_type identifier extends_from implements_list '{' class_statement_list '}' + { $$ = Stmt\Class_[$2, ['type' => $1, 'extends' => $3, 'implements' => $4, 'stmts' => $6]]; + $this->checkClass($$, #2); } + | T_INTERFACE identifier interface_extends_list '{' class_statement_list '}' + { $$ = Stmt\Interface_[$2, ['extends' => $3, 'stmts' => $5]]; + $this->checkInterface($$, #2); } + | T_TRAIT identifier '{' class_statement_list '}' + { $$ = Stmt\Trait_[$2, ['stmts' => $4]]; } +; + +class_entry_type: + T_CLASS { $$ = 0; } + | T_ABSTRACT T_CLASS { $$ = Stmt\Class_::MODIFIER_ABSTRACT; } + | T_FINAL T_CLASS { $$ = Stmt\Class_::MODIFIER_FINAL; } +; + +extends_from: + /* empty */ { $$ = null; } + | T_EXTENDS class_name { $$ = $2; } +; + +interface_extends_list: + /* empty */ { $$ = array(); } + | T_EXTENDS class_name_list { $$ = $2; } +; + +implements_list: + /* empty */ { $$ = array(); } + | T_IMPLEMENTS class_name_list { $$ = $2; } +; + +class_name_list: + non_empty_class_name_list no_comma { $$ = $1; } +; + +non_empty_class_name_list: + class_name { init($1); } + | non_empty_class_name_list ',' class_name { push($1, $3); } +; + +for_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDFOR ';' { $$ = $2; } +; + +foreach_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDFOREACH ';' { $$ = $2; } +; + +declare_statement: + non_empty_statement { $$ = toArray($1); } + | ';' { $$ = null; } + | ':' inner_statement_list T_ENDDECLARE ';' { $$ = $2; } +; + +declare_list: + non_empty_declare_list no_comma { $$ = $1; } +; + +non_empty_declare_list: + declare_list_element { init($1); } + | non_empty_declare_list ',' declare_list_element { push($1, $3); } +; + +declare_list_element: + identifier '=' expr { $$ = Stmt\DeclareDeclare[$1, $3]; } +; + +switch_case_list: + '{' case_list '}' { $$ = $2; } + | '{' ';' case_list '}' { $$ = $3; } + | ':' case_list T_ENDSWITCH ';' { $$ = $2; } + | ':' ';' case_list T_ENDSWITCH ';' { $$ = $3; } +; + +case_list: + /* empty */ { init(); } + | case_list case { push($1, $2); } +; + +case: + T_CASE expr case_separator inner_statement_list_ex { $$ = Stmt\Case_[$2, $4]; } + | T_DEFAULT case_separator inner_statement_list_ex { $$ = Stmt\Case_[null, $3]; } +; + +case_separator: + ':' + | ';' +; + +while_statement: + statement { $$ = toArray($1); } + | ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; } +; + +elseif_list: + /* empty */ { init(); } + | elseif_list elseif { push($1, $2); } +; + +elseif: + T_ELSEIF '(' expr ')' statement { $$ = Stmt\ElseIf_[$3, toArray($5)]; } +; + +new_elseif_list: + /* empty */ { init(); } + | new_elseif_list new_elseif { push($1, $2); } +; + +new_elseif: + T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt\ElseIf_[$3, $6]; } +; + +else_single: + /* empty */ { $$ = null; } + | T_ELSE statement { $$ = Stmt\Else_[toArray($2)]; } +; + +new_else_single: + /* empty */ { $$ = null; } + | T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; } +; + +foreach_variable: + variable { $$ = array($1, false); } + | '&' variable { $$ = array($2, true); } + | list_expr { $$ = array($1, false); } + | array_short_syntax { $$ = array($1, false); } +; + +parameter_list: + non_empty_parameter_list no_comma { $$ = $1; } + | /* empty */ { $$ = array(); } +; + +non_empty_parameter_list: + parameter { init($1); } + | non_empty_parameter_list ',' parameter { push($1, $3); } +; + +parameter: + optional_type optional_ref optional_ellipsis plain_variable + { $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); } + | optional_type optional_ref optional_ellipsis plain_variable '=' expr + { $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); } + | optional_type optional_ref optional_ellipsis error + { $$ = Node\Param[Expr\Error[], null, $1, $2, $3]; } +; + +type_expr: + type { $$ = $1; } + | '?' type { $$ = Node\NullableType[$2]; } +; + +type: + name { $$ = $this->handleBuiltinTypes($1); } + | T_ARRAY { $$ = Node\Identifier['array']; } + | T_CALLABLE { $$ = Node\Identifier['callable']; } +; + +optional_type: + /* empty */ { $$ = null; } + | type_expr { $$ = $1; } +; + +optional_return_type: + /* empty */ { $$ = null; } + | ':' type_expr { $$ = $2; } + | ':' error { $$ = null; } +; + +argument_list: + '(' ')' { $$ = array(); } + | '(' non_empty_argument_list optional_comma ')' { $$ = $2; } +; + +non_empty_argument_list: + argument { init($1); } + | non_empty_argument_list ',' argument { push($1, $3); } +; + +argument: + expr { $$ = Node\Arg[$1, false, false]; } + | '&' variable { $$ = Node\Arg[$2, true, false]; } + | T_ELLIPSIS expr { $$ = Node\Arg[$2, false, true]; } +; + +global_var_list: + non_empty_global_var_list no_comma { $$ = $1; } +; + +non_empty_global_var_list: + non_empty_global_var_list ',' global_var { push($1, $3); } + | global_var { init($1); } +; + +global_var: + simple_variable { $$ = Expr\Variable[$1]; } +; + +static_var_list: + non_empty_static_var_list no_comma { $$ = $1; } +; + +non_empty_static_var_list: + non_empty_static_var_list ',' static_var { push($1, $3); } + | static_var { init($1); } +; + +static_var: + plain_variable { $$ = Stmt\StaticVar[$1, null]; } + | plain_variable '=' expr { $$ = Stmt\StaticVar[$1, $3]; } +; + +class_statement_list_ex: + class_statement_list_ex class_statement { if ($2 !== null) { push($1, $2); } } + | /* empty */ { init(); } +; + +class_statement_list: + class_statement_list_ex + { makeZeroLengthNop($nop, $this->lookaheadStartAttributes); + if ($nop !== null) { $1[] = $nop; } $$ = $1; } +; + +class_statement: + variable_modifiers optional_type property_declaration_list ';' + { $attrs = attributes(); + $$ = new Stmt\Property($1, $3, $attrs, $2); $this->checkProperty($$, #1); } + | method_modifiers T_CONST class_const_list ';' + { $$ = Stmt\ClassConst[$3, $1]; $this->checkClassConst($$, #1); } + | method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body + { $$ = Stmt\ClassMethod[$4, ['type' => $1, 'byRef' => $3, 'params' => $6, 'returnType' => $8, 'stmts' => $9]]; + $this->checkClassMethod($$, #1); } + | T_USE class_name_list trait_adaptations { $$ = Stmt\TraitUse[$2, $3]; } + | error { $$ = null; /* will be skipped */ } +; + +trait_adaptations: + ';' { $$ = array(); } + | '{' trait_adaptation_list '}' { $$ = $2; } +; + +trait_adaptation_list: + /* empty */ { init(); } + | trait_adaptation_list trait_adaptation { push($1, $2); } +; + +trait_adaptation: + trait_method_reference_fully_qualified T_INSTEADOF class_name_list ';' + { $$ = Stmt\TraitUseAdaptation\Precedence[$1[0], $1[1], $3]; } + | trait_method_reference T_AS member_modifier identifier_ex ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, $4]; } + | trait_method_reference T_AS member_modifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], $3, null]; } + | trait_method_reference T_AS identifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; } + | trait_method_reference T_AS reserved_non_modifiers_identifier ';' + { $$ = Stmt\TraitUseAdaptation\Alias[$1[0], $1[1], null, $3]; } +; + +trait_method_reference_fully_qualified: + name T_PAAMAYIM_NEKUDOTAYIM identifier_ex { $$ = array($1, $3); } +; +trait_method_reference: + trait_method_reference_fully_qualified { $$ = $1; } + | identifier_ex { $$ = array(null, $1); } +; + +method_body: + ';' /* abstract method */ { $$ = null; } + | block_or_error { $$ = $1; } +; + +variable_modifiers: + non_empty_member_modifiers { $$ = $1; } + | T_VAR { $$ = 0; } +; + +method_modifiers: + /* empty */ { $$ = 0; } + | non_empty_member_modifiers { $$ = $1; } +; + +non_empty_member_modifiers: + member_modifier { $$ = $1; } + | non_empty_member_modifiers member_modifier { $this->checkModifier($1, $2, #2); $$ = $1 | $2; } +; + +member_modifier: + T_PUBLIC { $$ = Stmt\Class_::MODIFIER_PUBLIC; } + | T_PROTECTED { $$ = Stmt\Class_::MODIFIER_PROTECTED; } + | T_PRIVATE { $$ = Stmt\Class_::MODIFIER_PRIVATE; } + | T_STATIC { $$ = Stmt\Class_::MODIFIER_STATIC; } + | T_ABSTRACT { $$ = Stmt\Class_::MODIFIER_ABSTRACT; } + | T_FINAL { $$ = Stmt\Class_::MODIFIER_FINAL; } +; + +property_declaration_list: + non_empty_property_declaration_list no_comma { $$ = $1; } +; + +non_empty_property_declaration_list: + property_declaration { init($1); } + | non_empty_property_declaration_list ',' property_declaration + { push($1, $3); } +; + +property_decl_name: + T_VARIABLE { $$ = Node\VarLikeIdentifier[parseVar($1)]; } +; + +property_declaration: + property_decl_name { $$ = Stmt\PropertyProperty[$1, null]; } + | property_decl_name '=' expr { $$ = Stmt\PropertyProperty[$1, $3]; } +; + +expr_list: + non_empty_expr_list no_comma { $$ = $1; } +; + +non_empty_expr_list: + non_empty_expr_list ',' expr { push($1, $3); } + | expr { init($1); } +; + +for_expr: + /* empty */ { $$ = array(); } + | expr_list { $$ = $1; } +; + +expr: + variable { $$ = $1; } + | list_expr '=' expr { $$ = Expr\Assign[$1, $3]; } + | array_short_syntax '=' expr { $$ = Expr\Assign[$1, $3]; } + | variable '=' expr { $$ = Expr\Assign[$1, $3]; } + | variable '=' '&' variable { $$ = Expr\AssignRef[$1, $4]; } + | new_expr { $$ = $1; } + | T_CLONE expr { $$ = Expr\Clone_[$2]; } + | variable T_PLUS_EQUAL expr { $$ = Expr\AssignOp\Plus [$1, $3]; } + | variable T_MINUS_EQUAL expr { $$ = Expr\AssignOp\Minus [$1, $3]; } + | variable T_MUL_EQUAL expr { $$ = Expr\AssignOp\Mul [$1, $3]; } + | variable T_DIV_EQUAL expr { $$ = Expr\AssignOp\Div [$1, $3]; } + | variable T_CONCAT_EQUAL expr { $$ = Expr\AssignOp\Concat [$1, $3]; } + | variable T_MOD_EQUAL expr { $$ = Expr\AssignOp\Mod [$1, $3]; } + | variable T_AND_EQUAL expr { $$ = Expr\AssignOp\BitwiseAnd[$1, $3]; } + | variable T_OR_EQUAL expr { $$ = Expr\AssignOp\BitwiseOr [$1, $3]; } + | variable T_XOR_EQUAL expr { $$ = Expr\AssignOp\BitwiseXor[$1, $3]; } + | variable T_SL_EQUAL expr { $$ = Expr\AssignOp\ShiftLeft [$1, $3]; } + | variable T_SR_EQUAL expr { $$ = Expr\AssignOp\ShiftRight[$1, $3]; } + | variable T_POW_EQUAL expr { $$ = Expr\AssignOp\Pow [$1, $3]; } + | variable T_COALESCE_EQUAL expr { $$ = Expr\AssignOp\Coalesce [$1, $3]; } + | variable T_INC { $$ = Expr\PostInc[$1]; } + | T_INC variable { $$ = Expr\PreInc [$2]; } + | variable T_DEC { $$ = Expr\PostDec[$1]; } + | T_DEC variable { $$ = Expr\PreDec [$2]; } + | expr T_BOOLEAN_OR expr { $$ = Expr\BinaryOp\BooleanOr [$1, $3]; } + | expr T_BOOLEAN_AND expr { $$ = Expr\BinaryOp\BooleanAnd[$1, $3]; } + | expr T_LOGICAL_OR expr { $$ = Expr\BinaryOp\LogicalOr [$1, $3]; } + | expr T_LOGICAL_AND expr { $$ = Expr\BinaryOp\LogicalAnd[$1, $3]; } + | expr T_LOGICAL_XOR expr { $$ = Expr\BinaryOp\LogicalXor[$1, $3]; } + | expr '|' expr { $$ = Expr\BinaryOp\BitwiseOr [$1, $3]; } + | expr '&' expr { $$ = Expr\BinaryOp\BitwiseAnd[$1, $3]; } + | expr '^' expr { $$ = Expr\BinaryOp\BitwiseXor[$1, $3]; } + | expr '.' expr { $$ = Expr\BinaryOp\Concat [$1, $3]; } + | expr '+' expr { $$ = Expr\BinaryOp\Plus [$1, $3]; } + | expr '-' expr { $$ = Expr\BinaryOp\Minus [$1, $3]; } + | expr '*' expr { $$ = Expr\BinaryOp\Mul [$1, $3]; } + | expr '/' expr { $$ = Expr\BinaryOp\Div [$1, $3]; } + | expr '%' expr { $$ = Expr\BinaryOp\Mod [$1, $3]; } + | expr T_SL expr { $$ = Expr\BinaryOp\ShiftLeft [$1, $3]; } + | expr T_SR expr { $$ = Expr\BinaryOp\ShiftRight[$1, $3]; } + | expr T_POW expr { $$ = Expr\BinaryOp\Pow [$1, $3]; } + | '+' expr %prec T_INC { $$ = Expr\UnaryPlus [$2]; } + | '-' expr %prec T_INC { $$ = Expr\UnaryMinus[$2]; } + | '!' expr { $$ = Expr\BooleanNot[$2]; } + | '~' expr { $$ = Expr\BitwiseNot[$2]; } + | expr T_IS_IDENTICAL expr { $$ = Expr\BinaryOp\Identical [$1, $3]; } + | expr T_IS_NOT_IDENTICAL expr { $$ = Expr\BinaryOp\NotIdentical [$1, $3]; } + | expr T_IS_EQUAL expr { $$ = Expr\BinaryOp\Equal [$1, $3]; } + | expr T_IS_NOT_EQUAL expr { $$ = Expr\BinaryOp\NotEqual [$1, $3]; } + | expr T_SPACESHIP expr { $$ = Expr\BinaryOp\Spaceship [$1, $3]; } + | expr '<' expr { $$ = Expr\BinaryOp\Smaller [$1, $3]; } + | expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; } + | expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; } + | expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; } + | expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; } + | '(' expr ')' { $$ = $2; } + | expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; } + | expr '?' ':' expr { $$ = Expr\Ternary[$1, null, $4]; } + | expr T_COALESCE expr { $$ = Expr\BinaryOp\Coalesce[$1, $3]; } + | T_ISSET '(' variables_list ')' { $$ = Expr\Isset_[$3]; } + | T_EMPTY '(' expr ')' { $$ = Expr\Empty_[$3]; } + | T_INCLUDE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; } + | T_INCLUDE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE_ONCE]; } + | T_EVAL '(' expr ')' { $$ = Expr\Eval_[$3]; } + | T_REQUIRE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE]; } + | T_REQUIRE_ONCE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_REQUIRE_ONCE]; } + | T_INT_CAST expr { $$ = Expr\Cast\Int_ [$2]; } + | T_DOUBLE_CAST expr + { $attrs = attributes(); + $attrs['kind'] = $this->getFloatCastKind($1); + $$ = new Expr\Cast\Double($2, $attrs); } + | T_STRING_CAST expr { $$ = Expr\Cast\String_ [$2]; } + | T_ARRAY_CAST expr { $$ = Expr\Cast\Array_ [$2]; } + | T_OBJECT_CAST expr { $$ = Expr\Cast\Object_ [$2]; } + | T_BOOL_CAST expr { $$ = Expr\Cast\Bool_ [$2]; } + | T_UNSET_CAST expr { $$ = Expr\Cast\Unset_ [$2]; } + | T_EXIT exit_expr + { $attrs = attributes(); + $attrs['kind'] = strtolower($1) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $$ = new Expr\Exit_($2, $attrs); } + | '@' expr { $$ = Expr\ErrorSuppress[$2]; } + | scalar { $$ = $1; } + | '`' backticks_expr '`' { $$ = Expr\ShellExec[$2]; } + | T_PRINT expr { $$ = Expr\Print_[$2]; } + | T_YIELD { $$ = Expr\Yield_[null, null]; } + | T_YIELD expr { $$ = Expr\Yield_[$2, null]; } + | T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; } + | T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; } + + | T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr + { $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; } + | T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr + { $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; } + + | T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type + block_or_error + { $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; } + | T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type + block_or_error + { $$ = Expr\Closure[['static' => true, 'byRef' => $3, 'params' => $5, 'uses' => $7, 'returnType' => $8, 'stmts' => $9]]; } +; + +anonymous_class: + T_CLASS ctor_arguments extends_from implements_list '{' class_statement_list '}' + { $$ = array(Stmt\Class_[null, ['type' => 0, 'extends' => $3, 'implements' => $4, 'stmts' => $6]], $2); + $this->checkClass($$[0], -1); } +; + +new_expr: + T_NEW class_name_reference ctor_arguments { $$ = Expr\New_[$2, $3]; } + | T_NEW anonymous_class + { list($class, $ctorArgs) = $2; $$ = Expr\New_[$class, $ctorArgs]; } +; + +lexical_vars: + /* empty */ { $$ = array(); } + | T_USE '(' lexical_var_list ')' { $$ = $3; } +; + +lexical_var_list: + non_empty_lexical_var_list no_comma { $$ = $1; } +; + +non_empty_lexical_var_list: + lexical_var { init($1); } + | non_empty_lexical_var_list ',' lexical_var { push($1, $3); } +; + +lexical_var: + optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; } +; + +function_call: + name argument_list { $$ = Expr\FuncCall[$1, $2]; } + | callable_expr argument_list { $$ = Expr\FuncCall[$1, $2]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM member_name argument_list + { $$ = Expr\StaticCall[$1, $3, $4]; } +; + +class_name: + T_STATIC { $$ = Name[$1]; } + | name { $$ = $1; } +; + +name: + namespace_name_parts { $$ = Name[$1]; } + | T_NS_SEPARATOR namespace_name_parts { $$ = Name\FullyQualified[$2]; } + | T_NAMESPACE T_NS_SEPARATOR namespace_name_parts { $$ = Name\Relative[$3]; } +; + +class_name_reference: + class_name { $$ = $1; } + | new_variable { $$ = $1; } + | error { $$ = Expr\Error[]; $this->errorState = 2; } +; + +class_name_or_var: + class_name { $$ = $1; } + | dereferencable { $$ = $1; } +; + +exit_expr: + /* empty */ { $$ = null; } + | '(' optional_expr ')' { $$ = $2; } +; + +backticks_expr: + /* empty */ { $$ = array(); } + | T_ENCAPSED_AND_WHITESPACE + { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); } + | encaps_list { parseEncapsed($1, '`', true); $$ = $1; } +; + +ctor_arguments: + /* empty */ { $$ = array(); } + | argument_list { $$ = $1; } +; + +constant: + name { $$ = Expr\ConstFetch[$1]; } + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM identifier_ex + { $$ = Expr\ClassConstFetch[$1, $3]; } + /* We interpret and isolated FOO:: as an unfinished class constant fetch. It could also be + an unfinished static property fetch or unfinished scoped call. */ + | class_name_or_var T_PAAMAYIM_NEKUDOTAYIM error + { $$ = Expr\ClassConstFetch[$1, new Expr\Error(stackAttributes(#3))]; $this->errorState = 2; } +; + +array_short_syntax: + '[' array_pair_list ']' + { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT; + $$ = new Expr\Array_($2, $attrs); } +; + +dereferencable_scalar: + T_ARRAY '(' array_pair_list ')' + { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG; + $$ = new Expr\Array_($3, $attrs); } + | array_short_syntax { $$ = $1; } + | T_CONSTANT_ENCAPSED_STRING + { $attrs = attributes(); $attrs['kind'] = strKind($1); + $$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); } +; + +scalar: + T_LNUMBER { $$ = $this->parseLNumber($1, attributes()); } + | T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; } + | T_LINE { $$ = Scalar\MagicConst\Line[]; } + | T_FILE { $$ = Scalar\MagicConst\File[]; } + | T_DIR { $$ = Scalar\MagicConst\Dir[]; } + | T_CLASS_C { $$ = Scalar\MagicConst\Class_[]; } + | T_TRAIT_C { $$ = Scalar\MagicConst\Trait_[]; } + | T_METHOD_C { $$ = Scalar\MagicConst\Method[]; } + | T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; } + | T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; } + | dereferencable_scalar { $$ = $1; } + | constant { $$ = $1; } + | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC + { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); } + | T_START_HEREDOC T_END_HEREDOC + { $$ = $this->parseDocString($1, '', $2, attributes(), stackAttributes(#2), true); } + | '"' encaps_list '"' + { $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); } + | T_START_HEREDOC encaps_list T_END_HEREDOC + { $$ = $this->parseDocString($1, $2, $3, attributes(), stackAttributes(#3), true); } +; + +optional_expr: + /* empty */ { $$ = null; } + | expr { $$ = $1; } +; + +dereferencable: + variable { $$ = $1; } + | '(' expr ')' { $$ = $2; } + | dereferencable_scalar { $$ = $1; } +; + +callable_expr: + callable_variable { $$ = $1; } + | '(' expr ')' { $$ = $2; } + | dereferencable_scalar { $$ = $1; } +; + +callable_variable: + simple_variable { $$ = Expr\Variable[$1]; } + | dereferencable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | constant '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | dereferencable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | function_call { $$ = $1; } + | dereferencable T_OBJECT_OPERATOR property_name argument_list + { $$ = Expr\MethodCall[$1, $3, $4]; } +; + +variable: + callable_variable { $$ = $1; } + | static_member { $$ = $1; } + | dereferencable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; } +; + +simple_variable: + T_VARIABLE { $$ = parseVar($1); } + | '$' '{' expr '}' { $$ = $3; } + | '$' simple_variable { $$ = Expr\Variable[$2]; } + | '$' error { $$ = Expr\Error[]; $this->errorState = 2; } +; + +static_member_prop_name: + simple_variable + { $var = $1; $$ = \is_string($var) ? Node\VarLikeIdentifier[$var] : $var; } +; + +static_member: + class_name_or_var T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name + { $$ = Expr\StaticPropertyFetch[$1, $3]; } +; + +new_variable: + simple_variable { $$ = Expr\Variable[$1]; } + | new_variable '[' optional_expr ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | new_variable '{' expr '}' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | new_variable T_OBJECT_OPERATOR property_name { $$ = Expr\PropertyFetch[$1, $3]; } + | class_name T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name + { $$ = Expr\StaticPropertyFetch[$1, $3]; } + | new_variable T_PAAMAYIM_NEKUDOTAYIM static_member_prop_name + { $$ = Expr\StaticPropertyFetch[$1, $3]; } +; + +member_name: + identifier_ex { $$ = $1; } + | '{' expr '}' { $$ = $2; } + | simple_variable { $$ = Expr\Variable[$1]; } +; + +property_name: + identifier { $$ = $1; } + | '{' expr '}' { $$ = $2; } + | simple_variable { $$ = Expr\Variable[$1]; } + | error { $$ = Expr\Error[]; $this->errorState = 2; } +; + +list_expr: + T_LIST '(' list_expr_elements ')' { $$ = Expr\List_[$3]; } +; + +list_expr_elements: + list_expr_elements ',' list_expr_element { push($1, $3); } + | list_expr_element { init($1); } +; + +list_expr_element: + variable { $$ = Expr\ArrayItem[$1, null, false]; } + | '&' variable { $$ = Expr\ArrayItem[$2, null, true]; } + | list_expr { $$ = Expr\ArrayItem[$1, null, false]; } + | expr T_DOUBLE_ARROW variable { $$ = Expr\ArrayItem[$3, $1, false]; } + | expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; } + | expr T_DOUBLE_ARROW list_expr { $$ = Expr\ArrayItem[$3, $1, false]; } + | /* empty */ { $$ = null; } +; + +array_pair_list: + inner_array_pair_list + { $$ = $1; $end = count($$)-1; if ($$[$end] === null) array_pop($$); } +; + +comma_or_error: + ',' + | error + { /* do nothing -- prevent default action of $$=$1. See #551. */ } +; + +inner_array_pair_list: + inner_array_pair_list comma_or_error array_pair { push($1, $3); } + | array_pair { init($1); } +; + +array_pair: + expr T_DOUBLE_ARROW expr { $$ = Expr\ArrayItem[$3, $1, false]; } + | expr { $$ = Expr\ArrayItem[$1, null, false]; } + | expr T_DOUBLE_ARROW '&' variable { $$ = Expr\ArrayItem[$4, $1, true]; } + | '&' variable { $$ = Expr\ArrayItem[$2, null, true]; } + | T_ELLIPSIS expr { $$ = Expr\ArrayItem[$2, null, false, attributes(), true]; } + | /* empty */ { $$ = null; } +; + +encaps_list: + encaps_list encaps_var { push($1, $2); } + | encaps_list encaps_string_part { push($1, $2); } + | encaps_var { init($1); } + | encaps_string_part encaps_var { init($1, $2); } +; + +encaps_string_part: + T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; } +; + +encaps_str_varname: + T_STRING_VARNAME { $$ = Expr\Variable[$1]; } +; + +encaps_var: + plain_variable { $$ = $1; } + | plain_variable '[' encaps_var_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } + | plain_variable T_OBJECT_OPERATOR identifier { $$ = Expr\PropertyFetch[$1, $3]; } + | T_DOLLAR_OPEN_CURLY_BRACES expr '}' { $$ = Expr\Variable[$2]; } + | T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '}' { $$ = Expr\Variable[$2]; } + | T_DOLLAR_OPEN_CURLY_BRACES encaps_str_varname '[' expr ']' '}' + { $$ = Expr\ArrayDimFetch[$2, $4]; } + | T_CURLY_OPEN variable '}' { $$ = $2; } +; + +encaps_var_offset: + T_STRING { $$ = Scalar\String_[$1]; } + | T_NUM_STRING { $$ = $this->parseNumString($1, attributes()); } + | '-' T_NUM_STRING { $$ = $this->parseNumString('-' . $2, attributes()); } + | plain_variable { $$ = $1; } +; + +%% diff --git a/vendor/nikic/php-parser/grammar/rebuildParsers.php b/vendor/nikic/php-parser/grammar/rebuildParsers.php new file mode 100644 index 0000000..b913cda --- /dev/null +++ b/vendor/nikic/php-parser/grammar/rebuildParsers.php @@ -0,0 +1,257 @@ + 'Php5', + __DIR__ . '/php7.y' => 'Php7', +]; + +$tokensFile = __DIR__ . '/tokens.y'; +$tokensTemplate = __DIR__ . '/tokens.template'; +$skeletonFile = __DIR__ . '/parser.template'; +$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy'; +$tmpResultFile = __DIR__ . '/tmp_parser.php'; +$resultDir = __DIR__ . '/../lib/PhpParser/Parser'; +$tokensResultsFile = $resultDir . '/Tokens.php'; + +// check for kmyacc binary in this directory, otherwise fall back to global name +if (file_exists(__DIR__ . '/kmyacc.exe')) { + $kmyacc = __DIR__ . '/kmyacc.exe'; +} else if (file_exists(__DIR__ . '/kmyacc')) { + $kmyacc = __DIR__ . '/kmyacc'; +} else { + $kmyacc = 'kmyacc'; +} + +$options = array_flip($argv); +$optionDebug = isset($options['--debug']); +$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']); + +/////////////////////////////// +/// Utility regex constants /// +/////////////////////////////// + +const LIB = '(?(DEFINE) + (?\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\') + (?"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+") + (?(?&singleQuotedString)|(?&doubleQuotedString)) + (?/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/) + (?\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+}) +)'; + +const PARAMS = '\[(?[^[\]]*+(?:\[(?¶ms)\][^[\]]*+)*+)\]'; +const ARGS = '\((?[^()]*+(?:\((?&args)\)[^()]*+)*+)\)'; + +/////////////////// +/// Main script /// +/////////////////// + +$tokens = file_get_contents($tokensFile); + +foreach ($grammarFileToName as $grammarFile => $name) { + echo "Building temporary $name grammar file.\n"; + + $grammarCode = file_get_contents($grammarFile); + $grammarCode = str_replace('%tokens', $tokens, $grammarCode); + + $grammarCode = resolveNodes($grammarCode); + $grammarCode = resolveMacros($grammarCode); + $grammarCode = resolveStackAccess($grammarCode); + + file_put_contents($tmpGrammarFile, $grammarCode); + + $additionalArgs = $optionDebug ? '-t -v' : ''; + + echo "Building $name parser.\n"; + $output = trim(shell_exec("$kmyacc $additionalArgs -l -m $skeletonFile -p $name $tmpGrammarFile 2>&1")); + echo "Output: \"$output\"\n"; + + $resultCode = file_get_contents($tmpResultFile); + $resultCode = removeTrailingWhitespace($resultCode); + + ensureDirExists($resultDir); + file_put_contents("$resultDir/$name.php", $resultCode); + unlink($tmpResultFile); + + echo "Building token definition.\n"; + $output = trim(shell_exec("$kmyacc -l -m $tokensTemplate $tmpGrammarFile 2>&1")); + assert($output === ''); + rename($tmpResultFile, $tokensResultsFile); + + if (!$optionKeepTmpGrammar) { + unlink($tmpGrammarFile); + } +} + +/////////////////////////////// +/// Preprocessing functions /// +/////////////////////////////// + +function resolveNodes($code) { + return preg_replace_callback( + '~\b(?[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~', + function($matches) { + // recurse + $matches['params'] = resolveNodes($matches['params']); + + $params = magicSplit( + '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', + $matches['params'] + ); + + $paramCode = ''; + foreach ($params as $param) { + $paramCode .= $param . ', '; + } + + return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())'; + }, + $code + ); +} + +function resolveMacros($code) { + return preg_replace_callback( + '~\b(?)(?!array\()(?[a-z][A-Za-z]++)' . ARGS . '~', + function($matches) { + // recurse + $matches['args'] = resolveMacros($matches['args']); + + $name = $matches['name']; + $args = magicSplit( + '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,', + $matches['args'] + ); + + if ('attributes' == $name) { + assertArgs(0, $args, $name); + return '$this->startAttributeStack[#1] + $this->endAttributes'; + } + + if ('stackAttributes' == $name) { + assertArgs(1, $args, $name); + return '$this->startAttributeStack[' . $args[0] . ']' + . ' + $this->endAttributeStack[' . $args[0] . ']'; + } + + if ('init' == $name) { + return '$$ = array(' . implode(', ', $args) . ')'; + } + + if ('push' == $name) { + assertArgs(2, $args, $name); + + return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0]; + } + + if ('pushNormalizing' == $name) { + assertArgs(2, $args, $name); + + return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }' + . ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }'; + } + + if ('toArray' == $name) { + assertArgs(1, $args, $name); + + return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')'; + } + + if ('parseVar' == $name) { + assertArgs(1, $args, $name); + + return 'substr(' . $args[0] . ', 1)'; + } + + if ('parseEncapsed' == $name) { + assertArgs(3, $args, $name); + + return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {' + . ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }'; + } + + if ('makeNop' == $name) { + assertArgs(3, $args, $name); + + return '$startAttributes = ' . $args[1] . ';' + . ' if (isset($startAttributes[\'comments\']))' + . ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }' + . ' else { ' . $args[0] . ' = null; }'; + } + + if ('makeZeroLengthNop' == $name) { + assertArgs(2, $args, $name); + + return '$startAttributes = ' . $args[1] . ';' + . ' if (isset($startAttributes[\'comments\']))' + . ' { ' . $args[0] . ' = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); }' + . ' else { ' . $args[0] . ' = null; }'; + } + + if ('strKind' == $name) { + assertArgs(1, $args, $name); + + return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && ' + . '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) ' + . '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)'; + } + + if ('prependLeadingComments' == $name) { + assertArgs(1, $args, $name); + + return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; ' + . 'if (!empty($attrs[\'comments\'])) {' + . '$stmts[0]->setAttribute(\'comments\', ' + . 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }'; + } + + return $matches[0]; + }, + $code + ); +} + +function assertArgs($num, $args, $name) { + if ($num != count($args)) { + die('Wrong argument count for ' . $name . '().'); + } +} + +function resolveStackAccess($code) { + $code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code); + $code = preg_replace('/#(\d+)/', '$$1', $code); + return $code; +} + +function removeTrailingWhitespace($code) { + $lines = explode("\n", $code); + $lines = array_map('rtrim', $lines); + return implode("\n", $lines); +} + +function ensureDirExists($dir) { + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } +} + +////////////////////////////// +/// Regex helper functions /// +////////////////////////////// + +function regex($regex) { + return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~'; +} + +function magicSplit($regex, $string) { + $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string); + + foreach ($pieces as &$piece) { + $piece = trim($piece); + } + + if ($pieces === ['']) { + return []; + } + + return $pieces; +} diff --git a/vendor/nikic/php-parser/grammar/tokens.template b/vendor/nikic/php-parser/grammar/tokens.template new file mode 100644 index 0000000..ba4e490 --- /dev/null +++ b/vendor/nikic/php-parser/grammar/tokens.template @@ -0,0 +1,17 @@ +semValue +#semval($,%t) $this->semValue +#semval(%n) $this->stackPos-(%l-%n) +#semval(%n,%t) $this->stackPos-(%l-%n) + +namespace PhpParser\Parser; +#include; + +/* GENERATED file based on grammar/tokens.y */ +final class Tokens +{ +#tokenval + const %s = %n; +#endtokenval +} diff --git a/vendor/nikic/php-parser/grammar/tokens.y b/vendor/nikic/php-parser/grammar/tokens.y new file mode 100644 index 0000000..dd83a6f --- /dev/null +++ b/vendor/nikic/php-parser/grammar/tokens.y @@ -0,0 +1,114 @@ +/* We currently rely on the token ID mapping to be the same between PHP 5 and PHP 7 - so the same lexer can be used for + * both. This is enforced by sharing this token file. */ + +%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE +%left ',' +%left T_LOGICAL_OR +%left T_LOGICAL_XOR +%left T_LOGICAL_AND +%right T_PRINT +%right T_YIELD +%right T_DOUBLE_ARROW +%right T_YIELD_FROM +%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL +%left '?' ':' +%right T_COALESCE +%left T_BOOLEAN_OR +%left T_BOOLEAN_AND +%left '|' +%left '^' +%left '&' +%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP +%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL +%left T_SL T_SR +%left '+' '-' '.' +%left '*' '/' '%' +%right '!' +%nonassoc T_INSTANCEOF +%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@' +%right T_POW +%right '[' +%nonassoc T_NEW T_CLONE +%token T_EXIT +%token T_IF +%left T_ELSEIF +%left T_ELSE +%left T_ENDIF +%token T_LNUMBER +%token T_DNUMBER +%token T_STRING +%token T_STRING_VARNAME +%token T_VARIABLE +%token T_NUM_STRING +%token T_INLINE_HTML +%token T_CHARACTER +%token T_BAD_CHARACTER +%token T_ENCAPSED_AND_WHITESPACE +%token T_CONSTANT_ENCAPSED_STRING +%token T_ECHO +%token T_DO +%token T_WHILE +%token T_ENDWHILE +%token T_FOR +%token T_ENDFOR +%token T_FOREACH +%token T_ENDFOREACH +%token T_DECLARE +%token T_ENDDECLARE +%token T_AS +%token T_SWITCH +%token T_ENDSWITCH +%token T_CASE +%token T_DEFAULT +%token T_BREAK +%token T_CONTINUE +%token T_GOTO +%token T_FUNCTION +%token T_FN +%token T_CONST +%token T_RETURN +%token T_TRY +%token T_CATCH +%token T_FINALLY +%token T_THROW +%token T_USE +%token T_INSTEADOF +%token T_GLOBAL +%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC +%token T_VAR +%token T_UNSET +%token T_ISSET +%token T_EMPTY +%token T_HALT_COMPILER +%token T_CLASS +%token T_TRAIT +%token T_INTERFACE +%token T_EXTENDS +%token T_IMPLEMENTS +%token T_OBJECT_OPERATOR +%token T_DOUBLE_ARROW +%token T_LIST +%token T_ARRAY +%token T_CALLABLE +%token T_CLASS_C +%token T_TRAIT_C +%token T_METHOD_C +%token T_FUNC_C +%token T_LINE +%token T_FILE +%token T_COMMENT +%token T_DOC_COMMENT +%token T_OPEN_TAG +%token T_OPEN_TAG_WITH_ECHO +%token T_CLOSE_TAG +%token T_WHITESPACE +%token T_START_HEREDOC +%token T_END_HEREDOC +%token T_DOLLAR_OPEN_CURLY_BRACES +%token T_CURLY_OPEN +%token T_PAAMAYIM_NEKUDOTAYIM +%token T_NAMESPACE +%token T_NS_C +%token T_DIR +%token T_NS_SEPARATOR +%token T_ELLIPSIS diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder.php b/vendor/nikic/php-parser/lib/PhpParser/Builder.php new file mode 100644 index 0000000..26d8921 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder.php @@ -0,0 +1,13 @@ +name = $name; + } + + /** + * Extends a class. + * + * @param Name|string $class Name of class to extend + * + * @return $this The builder instance (for fluid interface) + */ + public function extend($class) { + $this->extends = BuilderHelpers::normalizeName($class); + + return $this; + } + + /** + * Implements one or more interfaces. + * + * @param Name|string ...$interfaces Names of interfaces to implement + * + * @return $this The builder instance (for fluid interface) + */ + public function implement(...$interfaces) { + foreach ($interfaces as $interface) { + $this->implements[] = BuilderHelpers::normalizeName($interface); + } + + return $this; + } + + /** + * Makes the class abstract. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeAbstract() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + + return $this; + } + + /** + * Makes the class final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + + return $this; + } + + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $stmt = BuilderHelpers::normalizeNode($stmt); + + $targets = [ + Stmt\TraitUse::class => &$this->uses, + Stmt\ClassConst::class => &$this->constants, + Stmt\Property::class => &$this->properties, + Stmt\ClassMethod::class => &$this->methods, + ]; + + $class = \get_class($stmt); + if (!isset($targets[$class])) { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); + } + + $targets[$class][] = $stmt; + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\Class_ The built class node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Class_($this->name, [ + 'flags' => $this->flags, + 'extends' => $this->extends, + 'implements' => $this->implements, + 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods), + ], $this->attributes); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php new file mode 100644 index 0000000..8309499 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php @@ -0,0 +1,43 @@ +addStmt($stmt); + } + + return $this; + } + + /** + * Sets doc comment for the declaration. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) + */ + public function setDocComment($docComment) { + $this->attributes['comments'] = [ + BuilderHelpers::normalizeDocComment($docComment) + ]; + + return $this; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php new file mode 100644 index 0000000..8e7db39 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php @@ -0,0 +1,74 @@ +returnByRef = true; + + return $this; + } + + /** + * Adds a parameter. + * + * @param Node\Param|Param $param The parameter to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addParam($param) { + $param = BuilderHelpers::normalizeNode($param); + + if (!$param instanceof Node\Param) { + throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType())); + } + + $this->params[] = $param; + + return $this; + } + + /** + * Adds multiple parameters. + * + * @param array $params The parameters to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addParams(array $params) { + foreach ($params as $param) { + $this->addParam($param); + } + + return $this; + } + + /** + * Sets the return type for PHP 7. + * + * @param string|Node\Name|Node\NullableType $type One of array, callable, string, int, float, + * bool, iterable, or a class/interface name. + * + * @return $this The builder instance (for fluid interface) + */ + public function setReturnType($type) { + $this->returnType = BuilderHelpers::normalizeType($type); + + return $this; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php new file mode 100644 index 0000000..56eda2a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php @@ -0,0 +1,50 @@ +name = $name; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + + return $this; + } + + /** + * Returns the built function node. + * + * @return Stmt\Function_ The built function node + */ + public function getNode() : Node { + return new Stmt\Function_($this->name, [ + 'byRef' => $this->returnByRef, + 'params' => $this->params, + 'returnType' => $this->returnType, + 'stmts' => $this->stmts, + ], $this->attributes); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php new file mode 100644 index 0000000..87e5b93 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php @@ -0,0 +1,75 @@ +name = $name; + } + + /** + * Extends one or more interfaces. + * + * @param Name|string ...$interfaces Names of interfaces to extend + * + * @return $this The builder instance (for fluid interface) + */ + public function extend(...$interfaces) { + foreach ($interfaces as $interface) { + $this->extends[] = BuilderHelpers::normalizeName($interface); + } + + return $this; + } + + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $stmt = BuilderHelpers::normalizeNode($stmt); + + if ($stmt instanceof Stmt\ClassConst) { + $this->constants[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + // we erase all statements in the body of an interface method + $stmt->stmts = null; + $this->methods[] = $stmt; + } else { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); + } + + return $this; + } + + /** + * Returns the built interface node. + * + * @return Stmt\Interface_ The built interface node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Interface_($this->name, [ + 'extends' => $this->extends, + 'stmts' => array_merge($this->constants, $this->methods), + ], $this->attributes); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php new file mode 100644 index 0000000..a3e8676 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php @@ -0,0 +1,129 @@ +name = $name; + } + + /** + * Makes the method public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the method protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the method private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the method static. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeStatic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + + return $this; + } + + /** + * Makes the method abstract. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeAbstract() { + if (!empty($this->stmts)) { + throw new \LogicException('Cannot make method with statements abstract'); + } + + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT); + $this->stmts = null; // abstract methods don't have statements + + return $this; + } + + /** + * Makes the method final. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeFinal() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL); + + return $this; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + if (null === $this->stmts) { + throw new \LogicException('Cannot add statements to an abstract method'); + } + + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + + return $this; + } + + /** + * Returns the built method node. + * + * @return Stmt\ClassMethod The built method node + */ + public function getNode() : Node { + return new Stmt\ClassMethod($this->name, [ + 'flags' => $this->flags, + 'byRef' => $this->returnByRef, + 'params' => $this->params, + 'returnType' => $this->returnType, + 'stmts' => $this->stmts, + ], $this->attributes); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php new file mode 100644 index 0000000..b9ccab3 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php @@ -0,0 +1,45 @@ +name = null !== $name ? BuilderHelpers::normalizeName($name) : null; + } + + /** + * Adds a statement. + * + * @param Node|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); + + return $this; + } + + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node { + return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php new file mode 100644 index 0000000..184813a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php @@ -0,0 +1,106 @@ +name = $name; + } + + /** + * Sets default value for the parameter. + * + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) + */ + public function setDefault($value) { + $this->default = BuilderHelpers::normalizeValue($value); + + return $this; + } + + /** + * Sets type for the parameter. + * + * @param string|Node\Name|Node\NullableType $type Parameter type + * + * @return $this The builder instance (for fluid interface) + */ + public function setType($type) { + $this->type = BuilderHelpers::normalizeType($type); + if ($this->type == 'void') { + throw new \LogicException('Parameter type cannot be void'); + } + + return $this; + } + + /** + * Sets type for the parameter. + * + * @param string|Node\Name|Node\NullableType $type Parameter type + * + * @return $this The builder instance (for fluid interface) + * + * @deprecated Use setType() instead + */ + public function setTypeHint($type) { + return $this->setType($type); + } + + /** + * Make the parameter accept the value by reference. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeByRef() { + $this->byRef = true; + + return $this; + } + + /** + * Make the parameter variadic + * + * @return $this The builder instance (for fluid interface) + */ + public function makeVariadic() { + $this->variadic = true; + + return $this; + } + + /** + * Returns the built parameter node. + * + * @return Node\Param The built parameter node + */ + public function getNode() : Node { + return new Node\Param( + new Node\Expr\Variable($this->name), + $this->default, $this->type, $this->byRef, $this->variadic + ); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php new file mode 100644 index 0000000..1f3bdb2 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php @@ -0,0 +1,132 @@ +name = $name; + } + + /** + * Makes the property public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the property protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the property private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the property static. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeStatic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC); + + return $this; + } + + /** + * Sets default value for the property. + * + * @param mixed $value Default value to use + * + * @return $this The builder instance (for fluid interface) + */ + public function setDefault($value) { + $this->default = BuilderHelpers::normalizeValue($value); + + return $this; + } + + /** + * Sets doc comment for the property. + * + * @param PhpParser\Comment\Doc|string $docComment Doc comment to set + * + * @return $this The builder instance (for fluid interface) + */ + public function setDocComment($docComment) { + $this->attributes = [ + 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] + ]; + + return $this; + } + + /** + * Sets the property type for PHP 7.4+. + * + * @param string|Name|NullableType|Identifier $type + * + * @return $this + */ + public function setType($type) { + $this->type = BuilderHelpers::normalizeType($type); + + return $this; + } + + /** + * Returns the built class node. + * + * @return Stmt\Property The built property node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Property( + $this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC, + [ + new Stmt\PropertyProperty($this->name, $this->default) + ], + $this->attributes, + $this->type + ); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php new file mode 100644 index 0000000..311e8cd --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php @@ -0,0 +1,64 @@ +and($trait); + } + } + + /** + * Adds used trait. + * + * @param Node\Name|string $trait Trait name + * + * @return $this The builder instance (for fluid interface) + */ + public function and($trait) { + $this->traits[] = BuilderHelpers::normalizeName($trait); + return $this; + } + + /** + * Adds trait adaptation. + * + * @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation + * + * @return $this The builder instance (for fluid interface) + */ + public function with($adaptation) { + $adaptation = BuilderHelpers::normalizeNode($adaptation); + + if (!$adaptation instanceof Stmt\TraitUseAdaptation) { + throw new \LogicException('Adaptation must have type TraitUseAdaptation'); + } + + $this->adaptations[] = $adaptation; + return $this; + } + + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node { + return new Stmt\TraitUse($this->traits, $this->adaptations); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php new file mode 100644 index 0000000..eb6c0b6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php @@ -0,0 +1,148 @@ +type = self::TYPE_UNDEFINED; + + $this->trait = is_null($trait)? null: BuilderHelpers::normalizeName($trait); + $this->method = BuilderHelpers::normalizeIdentifier($method); + } + + /** + * Sets alias of method. + * + * @param Node\Identifier|string $alias Alias for adaptated method + * + * @return $this The builder instance (for fluid interface) + */ + public function as($alias) { + if ($this->type === self::TYPE_UNDEFINED) { + $this->type = self::TYPE_ALIAS; + } + + if ($this->type !== self::TYPE_ALIAS) { + throw new \LogicException('Cannot set alias for not alias adaptation buider'); + } + + $this->alias = $alias; + return $this; + } + + /** + * Sets adaptated method public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->setModifier(Stmt\Class_::MODIFIER_PUBLIC); + return $this; + } + + /** + * Sets adaptated method protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->setModifier(Stmt\Class_::MODIFIER_PROTECTED); + return $this; + } + + /** + * Sets adaptated method private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->setModifier(Stmt\Class_::MODIFIER_PRIVATE); + return $this; + } + + /** + * Adds overwritten traits. + * + * @param Node\Name|string ...$traits Traits for overwrite + * + * @return $this The builder instance (for fluid interface) + */ + public function insteadof(...$traits) { + if ($this->type === self::TYPE_UNDEFINED) { + if (is_null($this->trait)) { + throw new \LogicException('Precedence adaptation must have trait'); + } + + $this->type = self::TYPE_PRECEDENCE; + } + + if ($this->type !== self::TYPE_PRECEDENCE) { + throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider'); + } + + foreach ($traits as $trait) { + $this->insteadof[] = BuilderHelpers::normalizeName($trait); + } + + return $this; + } + + protected function setModifier(int $modifier) { + if ($this->type === self::TYPE_UNDEFINED) { + $this->type = self::TYPE_ALIAS; + } + + if ($this->type !== self::TYPE_ALIAS) { + throw new \LogicException('Cannot set access modifier for not alias adaptation buider'); + } + + if (is_null($this->modifier)) { + $this->modifier = $modifier; + } else { + throw new \LogicException('Multiple access type modifiers are not allowed'); + } + } + + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node { + switch ($this->type) { + case self::TYPE_ALIAS: + return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias); + case self::TYPE_PRECEDENCE: + return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof); + default: + throw new \LogicException('Type of adaptation is not defined'); + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php new file mode 100644 index 0000000..a836d40 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php @@ -0,0 +1,60 @@ +name = $name; + } + + /** + * Adds a statement. + * + * @param Stmt|PhpParser\Builder $stmt The statement to add + * + * @return $this The builder instance (for fluid interface) + */ + public function addStmt($stmt) { + $stmt = BuilderHelpers::normalizeNode($stmt); + + if ($stmt instanceof Stmt\Property) { + $this->properties[] = $stmt; + } elseif ($stmt instanceof Stmt\ClassMethod) { + $this->methods[] = $stmt; + } elseif ($stmt instanceof Stmt\TraitUse) { + $this->uses[] = $stmt; + } else { + throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); + } + + return $this; + } + + /** + * Returns the built trait node. + * + * @return Stmt\Trait_ The built interface node + */ + public function getNode() : PhpParser\Node { + return new Stmt\Trait_( + $this->name, [ + 'stmts' => array_merge($this->uses, $this->properties, $this->methods) + ], $this->attributes + ); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php new file mode 100644 index 0000000..2026a17 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php @@ -0,0 +1,49 @@ +name = BuilderHelpers::normalizeName($name); + $this->type = $type; + } + + /** + * Sets alias for used name. + * + * @param string $alias Alias to use (last component of full name by default) + * + * @return $this The builder instance (for fluid interface) + */ + public function as(string $alias) { + $this->alias = $alias; + return $this; + } + + /** + * Returns the built node. + * + * @return Node The built node + */ + public function getNode() : Node { + return new Stmt\Use_([ + new Stmt\UseUse($this->name, $this->alias) + ], $this->type); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php b/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php new file mode 100644 index 0000000..18bd1cd --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php @@ -0,0 +1,348 @@ +args($args) + ); + } + + /** + * Creates a method call node. + * + * @param Expr $var Variable the method is called on + * @param string|Identifier|Expr $name Method name + * @param array $args Method arguments + * + * @return Expr\MethodCall + */ + public function methodCall(Expr $var, $name, array $args = []) : Expr\MethodCall { + return new Expr\MethodCall( + $var, + BuilderHelpers::normalizeIdentifierOrExpr($name), + $this->args($args) + ); + } + + /** + * Creates a static method call node. + * + * @param string|Name|Expr $class Class name + * @param string|Identifier|Expr $name Method name + * @param array $args Method arguments + * + * @return Expr\StaticCall + */ + public function staticCall($class, $name, array $args = []) : Expr\StaticCall { + return new Expr\StaticCall( + BuilderHelpers::normalizeNameOrExpr($class), + BuilderHelpers::normalizeIdentifierOrExpr($name), + $this->args($args) + ); + } + + /** + * Creates an object creation node. + * + * @param string|Name|Expr $class Class name + * @param array $args Constructor arguments + * + * @return Expr\New_ + */ + public function new($class, array $args = []) : Expr\New_ { + return new Expr\New_( + BuilderHelpers::normalizeNameOrExpr($class), + $this->args($args) + ); + } + + /** + * Creates a constant fetch node. + * + * @param string|Name $name Constant name + * + * @return Expr\ConstFetch + */ + public function constFetch($name) : Expr\ConstFetch { + return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); + } + + /** + * Creates a property fetch node. + * + * @param Expr $var Variable holding object + * @param string|Identifier|Expr $name Property name + * + * @return Expr\PropertyFetch + */ + public function propertyFetch(Expr $var, $name) : Expr\PropertyFetch { + return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); + } + + /** + * Creates a class constant fetch node. + * + * @param string|Name|Expr $class Class name + * @param string|Identifier $name Constant name + * + * @return Expr\ClassConstFetch + */ + public function classConstFetch($class, $name): Expr\ClassConstFetch { + return new Expr\ClassConstFetch( + BuilderHelpers::normalizeNameOrExpr($class), + BuilderHelpers::normalizeIdentifier($name) + ); + } + + /** + * Creates nested Concat nodes from a list of expressions. + * + * @param Expr|string ...$exprs Expressions or literal strings + * + * @return Concat + */ + public function concat(...$exprs) : Concat { + $numExprs = count($exprs); + if ($numExprs < 2) { + throw new \LogicException('Expected at least two expressions'); + } + + $lastConcat = $this->normalizeStringExpr($exprs[0]); + for ($i = 1; $i < $numExprs; $i++) { + $lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i])); + } + return $lastConcat; + } + + /** + * @param string|Expr $expr + * @return Expr + */ + private function normalizeStringExpr($expr) : Expr { + if ($expr instanceof Expr) { + return $expr; + } + + if (\is_string($expr)) { + return new String_($expr); + } + + throw new \LogicException('Expected string or Expr'); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php b/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php new file mode 100644 index 0000000..790e887 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php @@ -0,0 +1,277 @@ +getNode(); + } elseif ($node instanceof Node) { + return $node; + } + + throw new \LogicException('Expected node or builder object'); + } + + /** + * Normalizes a node to a statement. + * + * Expressions are wrapped in a Stmt\Expression node. + * + * @param Node|Builder $node The node to normalize + * + * @return Stmt The normalized statement node + */ + public static function normalizeStmt($node) : Stmt { + $node = self::normalizeNode($node); + if ($node instanceof Stmt) { + return $node; + } + + if ($node instanceof Expr) { + return new Stmt\Expression($node); + } + + throw new \LogicException('Expected statement or expression node'); + } + + /** + * Normalizes strings to Identifier. + * + * @param string|Identifier $name The identifier to normalize + * + * @return Identifier The normalized identifier + */ + public static function normalizeIdentifier($name) : Identifier { + if ($name instanceof Identifier) { + return $name; + } + + if (\is_string($name)) { + return new Identifier($name); + } + + throw new \LogicException('Expected string or instance of Node\Identifier'); + } + + /** + * Normalizes strings to Identifier, also allowing expressions. + * + * @param string|Identifier|Expr $name The identifier to normalize + * + * @return Identifier|Expr The normalized identifier or expression + */ + public static function normalizeIdentifierOrExpr($name) { + if ($name instanceof Identifier || $name instanceof Expr) { + return $name; + } + + if (\is_string($name)) { + return new Identifier($name); + } + + throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr'); + } + + /** + * Normalizes a name: Converts string names to Name nodes. + * + * @param Name|string $name The name to normalize + * + * @return Name The normalized name + */ + public static function normalizeName($name) : Name { + return self::normalizeNameCommon($name, false); + } + + /** + * Normalizes a name: Converts string names to Name nodes, while also allowing expressions. + * + * @param Expr|Name|string $name The name to normalize + * + * @return Name|Expr The normalized name or expression + */ + public static function normalizeNameOrExpr($name) { + return self::normalizeNameCommon($name, true); + } + + /** + * Normalizes a name: Converts string names to Name nodes, optionally allowing expressions. + * + * @param Expr|Name|string $name The name to normalize + * @param bool $allowExpr Whether to also allow expressions + * + * @return Name|Expr The normalized name, or expression (if allowed) + */ + private static function normalizeNameCommon($name, bool $allowExpr) { + if ($name instanceof Name) { + return $name; + } elseif (is_string($name)) { + if (!$name) { + throw new \LogicException('Name cannot be empty'); + } + + if ($name[0] === '\\') { + return new Name\FullyQualified(substr($name, 1)); + } elseif (0 === strpos($name, 'namespace\\')) { + return new Name\Relative(substr($name, strlen('namespace\\'))); + } else { + return new Name($name); + } + } + + if ($allowExpr) { + if ($name instanceof Expr) { + return $name; + } + throw new \LogicException( + 'Name must be a string or an instance of Node\Name or Node\Expr' + ); + } else { + throw new \LogicException('Name must be a string or an instance of Node\Name'); + } + } + + /** + * Normalizes a type: Converts plain-text type names into proper AST representation. + * + * In particular, builtin types become Identifiers, custom types become Names and nullables + * are wrapped in NullableType nodes. + * + * @param string|Name|Identifier|NullableType $type The type to normalize + * + * @return Name|Identifier|NullableType The normalized type + */ + public static function normalizeType($type) { + if (!is_string($type)) { + if (!$type instanceof Name && !$type instanceof Identifier + && !$type instanceof NullableType) { + throw new \LogicException( + 'Type must be a string, or an instance of Name, Identifier or NullableType'); + } + return $type; + } + + $nullable = false; + if (strlen($type) > 0 && $type[0] === '?') { + $nullable = true; + $type = substr($type, 1); + } + + $builtinTypes = [ + 'array', 'callable', 'string', 'int', 'float', 'bool', 'iterable', 'void', 'object' + ]; + + $lowerType = strtolower($type); + if (in_array($lowerType, $builtinTypes)) { + $type = new Identifier($lowerType); + } else { + $type = self::normalizeName($type); + } + + if ($nullable && (string) $type === 'void') { + throw new \LogicException('void type cannot be nullable'); + } + + return $nullable ? new Node\NullableType($type) : $type; + } + + /** + * Normalizes a value: Converts nulls, booleans, integers, + * floats, strings and arrays into their respective nodes + * + * @param Node\Expr|bool|null|int|float|string|array $value The value to normalize + * + * @return Expr The normalized value + */ + public static function normalizeValue($value) : Expr { + if ($value instanceof Node\Expr) { + return $value; + } elseif (is_null($value)) { + return new Expr\ConstFetch( + new Name('null') + ); + } elseif (is_bool($value)) { + return new Expr\ConstFetch( + new Name($value ? 'true' : 'false') + ); + } elseif (is_int($value)) { + return new Scalar\LNumber($value); + } elseif (is_float($value)) { + return new Scalar\DNumber($value); + } elseif (is_string($value)) { + return new Scalar\String_($value); + } elseif (is_array($value)) { + $items = []; + $lastKey = -1; + foreach ($value as $itemKey => $itemValue) { + // for consecutive, numeric keys don't generate keys + if (null !== $lastKey && ++$lastKey === $itemKey) { + $items[] = new Expr\ArrayItem( + self::normalizeValue($itemValue) + ); + } else { + $lastKey = null; + $items[] = new Expr\ArrayItem( + self::normalizeValue($itemValue), + self::normalizeValue($itemKey) + ); + } + } + + return new Expr\Array_($items); + } else { + throw new \LogicException('Invalid value'); + } + } + + /** + * Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc. + * + * @param Comment\Doc|string $docComment The doc comment to normalize + * + * @return Comment\Doc The normalized doc comment + */ + public static function normalizeDocComment($docComment) : Comment\Doc { + if ($docComment instanceof Comment\Doc) { + return $docComment; + } elseif (is_string($docComment)) { + return new Comment\Doc($docComment); + } else { + throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); + } + } + + /** + * Adds a modifier and returns new modifier bitmask. + * + * @param int $modifiers Existing modifiers + * @param int $modifier Modifier to set + * + * @return int New modifiers + */ + public static function addModifier(int $modifiers, int $modifier) : int { + Stmt\Class_::verifyModifier($modifiers, $modifier); + return $modifiers | $modifier; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Comment.php b/vendor/nikic/php-parser/lib/PhpParser/Comment.php new file mode 100644 index 0000000..5da8420 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Comment.php @@ -0,0 +1,167 @@ +text = $text; + $this->line = $startLine; + $this->filePos = $startFilePos; + $this->tokenPos = $startTokenPos; + } + + /** + * Gets the comment text. + * + * @return string The comment text (including comment delimiters like /*) + */ + public function getText() : string { + return $this->text; + } + + /** + * Gets the line number the comment started on. + * + * @return int Line number + */ + public function getLine() : int { + return $this->line; + } + + /** + * Gets the file offset the comment started on. + * + * @return int File offset + */ + public function getFilePos() : int { + return $this->filePos; + } + + /** + * Gets the token offset the comment started on. + * + * @return int Token offset + */ + public function getTokenPos() : int { + return $this->tokenPos; + } + + /** + * Gets the comment text. + * + * @return string The comment text (including comment delimiters like /*) + */ + public function __toString() : string { + return $this->text; + } + + /** + * Gets the reformatted comment text. + * + * "Reformatted" here means that we try to clean up the whitespace at the + * starts of the lines. This is necessary because we receive the comments + * without trailing whitespace on the first line, but with trailing whitespace + * on all subsequent lines. + * + * @return mixed|string + */ + public function getReformattedText() { + $text = trim($this->text); + $newlinePos = strpos($text, "\n"); + if (false === $newlinePos) { + // Single line comments don't need further processing + return $text; + } elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) { + // Multi line comment of the type + // + // /* + // * Some text. + // * Some more text. + // */ + // + // is handled by replacing the whitespace sequences before the * by a single space + return preg_replace('(^\s+\*)m', ' *', $this->text); + } elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) { + // Multi line comment of the type + // + // /* + // Some text. + // Some more text. + // */ + // + // is handled by removing the whitespace sequence on the line before the closing + // */ on all lines. So if the last line is " */", then " " is removed at the + // start of all lines. + return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text); + } elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) { + // Multi line comment of the type + // + // /* Some text. + // Some more text. + // Indented text. + // Even more text. */ + // + // is handled by removing the difference between the shortest whitespace prefix on all + // lines and the length of the "/* " opening sequence. + $prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1)); + $removeLen = $prefixLen - strlen($matches[0]); + return preg_replace('(^\s{' . $removeLen . '})m', '', $text); + } + + // No idea how to format this comment, so simply return as is + return $text; + } + + /** + * Get length of shortest whitespace prefix (at the start of a line). + * + * If there is a line with no prefix whitespace, 0 is a valid return value. + * + * @param string $str String to check + * @return int Length in characters. Tabs count as single characters. + */ + private function getShortestWhitespacePrefixLen(string $str) : int { + $lines = explode("\n", $str); + $shortestPrefixLen = \INF; + foreach ($lines as $line) { + preg_match('(^\s*)', $line, $matches); + $prefixLen = strlen($matches[0]); + if ($prefixLen < $shortestPrefixLen) { + $shortestPrefixLen = $prefixLen; + } + } + return $shortestPrefixLen; + } + + /** + * @return array + * @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed} + */ + public function jsonSerialize() : array { + // Technically not a node, but we make it look like one anyway + $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment'; + return [ + 'nodeType' => $type, + 'text' => $this->text, + 'line' => $this->line, + 'filePos' => $this->filePos, + 'tokenPos' => $this->tokenPos, + ]; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php b/vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php new file mode 100644 index 0000000..a9db612 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php @@ -0,0 +1,7 @@ +fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) { + throw new ConstExprEvaluationException( + "Expression of type {$expr->getType()} cannot be evaluated" + ); + }; + } + + /** + * Silently evaluates a constant expression into a PHP value. + * + * Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException. + * The original source of the exception is available through getPrevious(). + * + * If some part of the expression cannot be evaluated, the fallback evaluator passed to the + * constructor will be invoked. By default, if no fallback is provided, an exception of type + * ConstExprEvaluationException is thrown. + * + * See class doc comment for caveats and limitations. + * + * @param Expr $expr Constant expression to evaluate + * @return mixed Result of evaluation + * + * @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred + */ + public function evaluateSilently(Expr $expr) { + set_error_handler(function($num, $str, $file, $line) { + throw new \ErrorException($str, 0, $num, $file, $line); + }); + + try { + return $this->evaluate($expr); + } catch (\Throwable $e) { + if (!$e instanceof ConstExprEvaluationException) { + $e = new ConstExprEvaluationException( + "An error occurred during constant expression evaluation", 0, $e); + } + throw $e; + } finally { + restore_error_handler(); + } + } + + /** + * Directly evaluates a constant expression into a PHP value. + * + * May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these + * into a ConstExprEvaluationException. + * + * If some part of the expression cannot be evaluated, the fallback evaluator passed to the + * constructor will be invoked. By default, if no fallback is provided, an exception of type + * ConstExprEvaluationException is thrown. + * + * See class doc comment for caveats and limitations. + * + * @param Expr $expr Constant expression to evaluate + * @return mixed Result of evaluation + * + * @throws ConstExprEvaluationException if the expression cannot be evaluated + */ + public function evaluateDirectly(Expr $expr) { + return $this->evaluate($expr); + } + + private function evaluate(Expr $expr) { + if ($expr instanceof Scalar\LNumber + || $expr instanceof Scalar\DNumber + || $expr instanceof Scalar\String_ + ) { + return $expr->value; + } + + if ($expr instanceof Expr\Array_) { + return $this->evaluateArray($expr); + } + + // Unary operators + if ($expr instanceof Expr\UnaryPlus) { + return +$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\UnaryMinus) { + return -$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\BooleanNot) { + return !$this->evaluate($expr->expr); + } + if ($expr instanceof Expr\BitwiseNot) { + return ~$this->evaluate($expr->expr); + } + + if ($expr instanceof Expr\BinaryOp) { + return $this->evaluateBinaryOp($expr); + } + + if ($expr instanceof Expr\Ternary) { + return $this->evaluateTernary($expr); + } + + if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) { + return $this->evaluate($expr->var)[$this->evaluate($expr->dim)]; + } + + if ($expr instanceof Expr\ConstFetch) { + return $this->evaluateConstFetch($expr); + } + + return ($this->fallbackEvaluator)($expr); + } + + private function evaluateArray(Expr\Array_ $expr) { + $array = []; + foreach ($expr->items as $item) { + if (null !== $item->key) { + $array[$this->evaluate($item->key)] = $this->evaluate($item->value); + } else { + $array[] = $this->evaluate($item->value); + } + } + return $array; + } + + private function evaluateTernary(Expr\Ternary $expr) { + if (null === $expr->if) { + return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); + } + + return $this->evaluate($expr->cond) + ? $this->evaluate($expr->if) + : $this->evaluate($expr->else); + } + + private function evaluateBinaryOp(Expr\BinaryOp $expr) { + if ($expr instanceof Expr\BinaryOp\Coalesce + && $expr->left instanceof Expr\ArrayDimFetch + ) { + // This needs to be special cased to respect BP_VAR_IS fetch semantics + return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)] + ?? $this->evaluate($expr->right); + } + + // The evaluate() calls are repeated in each branch, because some of the operators are + // short-circuiting and evaluating the RHS in advance may be illegal in that case + $l = $expr->left; + $r = $expr->right; + switch ($expr->getOperatorSigil()) { + case '&': return $this->evaluate($l) & $this->evaluate($r); + case '|': return $this->evaluate($l) | $this->evaluate($r); + case '^': return $this->evaluate($l) ^ $this->evaluate($r); + case '&&': return $this->evaluate($l) && $this->evaluate($r); + case '||': return $this->evaluate($l) || $this->evaluate($r); + case '??': return $this->evaluate($l) ?? $this->evaluate($r); + case '.': return $this->evaluate($l) . $this->evaluate($r); + case '/': return $this->evaluate($l) / $this->evaluate($r); + case '==': return $this->evaluate($l) == $this->evaluate($r); + case '>': return $this->evaluate($l) > $this->evaluate($r); + case '>=': return $this->evaluate($l) >= $this->evaluate($r); + case '===': return $this->evaluate($l) === $this->evaluate($r); + case 'and': return $this->evaluate($l) and $this->evaluate($r); + case 'or': return $this->evaluate($l) or $this->evaluate($r); + case 'xor': return $this->evaluate($l) xor $this->evaluate($r); + case '-': return $this->evaluate($l) - $this->evaluate($r); + case '%': return $this->evaluate($l) % $this->evaluate($r); + case '*': return $this->evaluate($l) * $this->evaluate($r); + case '!=': return $this->evaluate($l) != $this->evaluate($r); + case '!==': return $this->evaluate($l) !== $this->evaluate($r); + case '+': return $this->evaluate($l) + $this->evaluate($r); + case '**': return $this->evaluate($l) ** $this->evaluate($r); + case '<<': return $this->evaluate($l) << $this->evaluate($r); + case '>>': return $this->evaluate($l) >> $this->evaluate($r); + case '<': return $this->evaluate($l) < $this->evaluate($r); + case '<=': return $this->evaluate($l) <= $this->evaluate($r); + case '<=>': return $this->evaluate($l) <=> $this->evaluate($r); + } + + throw new \Exception('Should not happen'); + } + + private function evaluateConstFetch(Expr\ConstFetch $expr) { + $name = $expr->name->toLowerString(); + switch ($name) { + case 'null': return null; + case 'false': return false; + case 'true': return true; + } + + return ($this->fallbackEvaluator)($expr); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Error.php b/vendor/nikic/php-parser/lib/PhpParser/Error.php new file mode 100644 index 0000000..d1fb959 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Error.php @@ -0,0 +1,180 @@ +rawMessage = $message; + if (is_array($attributes)) { + $this->attributes = $attributes; + } else { + $this->attributes = ['startLine' => $attributes]; + } + $this->updateMessage(); + } + + /** + * Gets the error message + * + * @return string Error message + */ + public function getRawMessage() : string { + return $this->rawMessage; + } + + /** + * Gets the line the error starts in. + * + * @return int Error start line + */ + public function getStartLine() : int { + return $this->attributes['startLine'] ?? -1; + } + + /** + * Gets the line the error ends in. + * + * @return int Error end line + */ + public function getEndLine() : int { + return $this->attributes['endLine'] ?? -1; + } + + /** + * Gets the attributes of the node/token the error occurred at. + * + * @return array + */ + public function getAttributes() : array { + return $this->attributes; + } + + /** + * Sets the attributes of the node/token the error occurred at. + * + * @param array $attributes + */ + public function setAttributes(array $attributes) { + $this->attributes = $attributes; + $this->updateMessage(); + } + + /** + * Sets the line of the PHP file the error occurred in. + * + * @param string $message Error message + */ + public function setRawMessage(string $message) { + $this->rawMessage = $message; + $this->updateMessage(); + } + + /** + * Sets the line the error starts in. + * + * @param int $line Error start line + */ + public function setStartLine(int $line) { + $this->attributes['startLine'] = $line; + $this->updateMessage(); + } + + /** + * Returns whether the error has start and end column information. + * + * For column information enable the startFilePos and endFilePos in the lexer options. + * + * @return bool + */ + public function hasColumnInfo() : bool { + return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); + } + + /** + * Gets the start column (1-based) into the line where the error started. + * + * @param string $code Source code of the file + * @return int + */ + public function getStartColumn(string $code) : int { + if (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); + } + + return $this->toColumn($code, $this->attributes['startFilePos']); + } + + /** + * Gets the end column (1-based) into the line where the error ended. + * + * @param string $code Source code of the file + * @return int + */ + public function getEndColumn(string $code) : int { + if (!$this->hasColumnInfo()) { + throw new \RuntimeException('Error does not have column information'); + } + + return $this->toColumn($code, $this->attributes['endFilePos']); + } + + /** + * Formats message including line and column information. + * + * @param string $code Source code associated with the error, for calculation of the columns + * + * @return string Formatted message + */ + public function getMessageWithColumnInfo(string $code) : string { + return sprintf( + '%s from %d:%d to %d:%d', $this->getRawMessage(), + $this->getStartLine(), $this->getStartColumn($code), + $this->getEndLine(), $this->getEndColumn($code) + ); + } + + /** + * Converts a file offset into a column. + * + * @param string $code Source code that $pos indexes into + * @param int $pos 0-based position in $code + * + * @return int 1-based column (relative to start of line) + */ + private function toColumn(string $code, int $pos) : int { + if ($pos > strlen($code)) { + throw new \RuntimeException('Invalid position information'); + } + + $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); + if (false === $lineStartPos) { + $lineStartPos = -1; + } + + return $pos - $lineStartPos; + } + + /** + * Updates the exception message after a change to rawMessage or rawLine. + */ + protected function updateMessage() { + $this->message = $this->rawMessage; + + if (-1 === $this->getStartLine()) { + $this->message .= ' on unknown line'; + } else { + $this->message .= ' on line ' . $this->getStartLine(); + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php b/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php new file mode 100644 index 0000000..d620e74 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php @@ -0,0 +1,13 @@ +errors[] = $error; + } + + /** + * Get collected errors. + * + * @return Error[] + */ + public function getErrors() : array { + return $this->errors; + } + + /** + * Check whether there are any errors. + * + * @return bool + */ + public function hasErrors() : bool { + return !empty($this->errors); + } + + /** + * Reset/clear collected errors. + */ + public function clearErrors() { + $this->errors = []; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php b/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php new file mode 100644 index 0000000..aeee989 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php @@ -0,0 +1,18 @@ +type = $type; + $this->old = $old; + $this->new = $new; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php new file mode 100644 index 0000000..7f218c7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php @@ -0,0 +1,164 @@ +isEqual = $isEqual; + } + + /** + * Calculate diff (edit script) from $old to $new. + * + * @param array $old Original array + * @param array $new New array + * + * @return DiffElem[] Diff (edit script) + */ + public function diff(array $old, array $new) { + list($trace, $x, $y) = $this->calculateTrace($old, $new); + return $this->extractDiff($trace, $x, $y, $old, $new); + } + + /** + * Calculate diff, including "replace" operations. + * + * If a sequence of remove operations is followed by the same number of add operations, these + * will be coalesced into replace operations. + * + * @param array $old Original array + * @param array $new New array + * + * @return DiffElem[] Diff (edit script), including replace operations + */ + public function diffWithReplacements(array $old, array $new) { + return $this->coalesceReplacements($this->diff($old, $new)); + } + + private function calculateTrace(array $a, array $b) { + $n = \count($a); + $m = \count($b); + $max = $n + $m; + $v = [1 => 0]; + $trace = []; + for ($d = 0; $d <= $max; $d++) { + $trace[] = $v; + for ($k = -$d; $k <= $d; $k += 2) { + if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) { + $x = $v[$k+1]; + } else { + $x = $v[$k-1] + 1; + } + + $y = $x - $k; + while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) { + $x++; + $y++; + } + + $v[$k] = $x; + if ($x >= $n && $y >= $m) { + return [$trace, $x, $y]; + } + } + } + throw new \Exception('Should not happen'); + } + + private function extractDiff(array $trace, int $x, int $y, array $a, array $b) { + $result = []; + for ($d = \count($trace) - 1; $d >= 0; $d--) { + $v = $trace[$d]; + $k = $x - $y; + + if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) { + $prevK = $k + 1; + } else { + $prevK = $k - 1; + } + + $prevX = $v[$prevK]; + $prevY = $prevX - $prevK; + + while ($x > $prevX && $y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]); + $x--; + $y--; + } + + if ($d === 0) { + break; + } + + while ($x > $prevX) { + $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null); + $x--; + } + + while ($y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]); + $y--; + } + } + return array_reverse($result); + } + + /** + * Coalesce equal-length sequences of remove+add into a replace operation. + * + * @param DiffElem[] $diff + * @return DiffElem[] + */ + private function coalesceReplacements(array $diff) { + $newDiff = []; + $c = \count($diff); + for ($i = 0; $i < $c; $i++) { + $diffType = $diff[$i]->type; + if ($diffType !== DiffElem::TYPE_REMOVE) { + $newDiff[] = $diff[$i]; + continue; + } + + $j = $i; + while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { + $j++; + } + + $k = $j; + while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { + $k++; + } + + if ($j - $i === $k - $j) { + $len = $j - $i; + for ($n = 0; $n < $len; $n++) { + $newDiff[] = new DiffElem( + DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new + ); + } + } else { + for (; $i < $k; $i++) { + $newDiff[] = $diff[$i]; + } + } + $i = $k - 1; + } + return $newDiff; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php new file mode 100644 index 0000000..7b019db --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php @@ -0,0 +1,57 @@ +args = $args; + $this->extends = $extends; + $this->implements = $implements; + $this->stmts = $stmts; + } + + public static function fromNewNode(Expr\New_ $newNode) { + $class = $newNode->class; + assert($class instanceof Node\Stmt\Class_); + // We don't assert that $class->name is null here, to allow consumers to assign unique names + // to anonymous classes for their own purposes. We simplify ignore the name here. + return new self( + $newNode->args, $class->extends, $class->implements, + $class->stmts, $newNode->getAttributes() + ); + } + + public function getType() : string { + return 'Expr_PrintableNewAnonClass'; + } + + public function getSubNodeNames() : array { + return ['args', 'extends', 'implements', 'stmts']; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php b/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php new file mode 100644 index 0000000..cf9e00a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php @@ -0,0 +1,256 @@ +tokens = $tokens; + $this->indentMap = $this->calcIndentMap(); + } + + /** + * Whether the given position is immediately surrounded by parenthesis. + * + * @param int $startPos Start position + * @param int $endPos End position + * + * @return bool + */ + public function haveParens(int $startPos, int $endPos) : bool { + return $this->haveTokenImmediativelyBefore($startPos, '(') + && $this->haveTokenImmediatelyAfter($endPos, ')'); + } + + /** + * Whether the given position is immediately surrounded by braces. + * + * @param int $startPos Start position + * @param int $endPos End position + * + * @return bool + */ + public function haveBraces(int $startPos, int $endPos) : bool { + return $this->haveTokenImmediativelyBefore($startPos, '{') + && $this->haveTokenImmediatelyAfter($endPos, '}'); + } + + /** + * Check whether the position is directly preceded by a certain token type. + * + * During this check whitespace and comments are skipped. + * + * @param int $pos Position before which the token should occur + * @param int|string $expectedTokenType Token to check for + * + * @return bool Whether the expected token was found + */ + public function haveTokenImmediativelyBefore(int $pos, $expectedTokenType) : bool { + $tokens = $this->tokens; + $pos--; + for (; $pos >= 0; $pos--) { + $tokenType = $tokens[$pos][0]; + if ($tokenType === $expectedTokenType) { + return true; + } + if ($tokenType !== \T_WHITESPACE + && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + break; + } + } + return false; + } + + /** + * Check whether the position is directly followed by a certain token type. + * + * During this check whitespace and comments are skipped. + * + * @param int $pos Position after which the token should occur + * @param int|string $expectedTokenType Token to check for + * + * @return bool Whether the expected token was found + */ + public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool { + $tokens = $this->tokens; + $pos++; + for (; $pos < \count($tokens); $pos++) { + $tokenType = $tokens[$pos][0]; + if ($tokenType === $expectedTokenType) { + return true; + } + if ($tokenType !== \T_WHITESPACE + && $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) { + break; + } + } + return false; + } + + public function skipLeft(int $pos, $skipTokenType) { + $tokens = $this->tokens; + + $pos = $this->skipLeftWhitespace($pos); + if ($skipTokenType === \T_WHITESPACE) { + return $pos; + } + + if ($tokens[$pos][0] !== $skipTokenType) { + // Shouldn't happen. The skip token MUST be there + throw new \Exception('Encountered unexpected token'); + } + $pos--; + + return $this->skipLeftWhitespace($pos); + } + + public function skipRight(int $pos, $skipTokenType) { + $tokens = $this->tokens; + + $pos = $this->skipRightWhitespace($pos); + if ($skipTokenType === \T_WHITESPACE) { + return $pos; + } + + if ($tokens[$pos][0] !== $skipTokenType) { + // Shouldn't happen. The skip token MUST be there + throw new \Exception('Encountered unexpected token'); + } + $pos++; + + return $this->skipRightWhitespace($pos); + } + + /** + * Return first non-whitespace token position smaller or equal to passed position. + * + * @param int $pos Token position + * @return int Non-whitespace token position + */ + public function skipLeftWhitespace(int $pos) { + $tokens = $this->tokens; + for (; $pos >= 0; $pos--) { + $type = $tokens[$pos][0]; + if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + break; + } + } + return $pos; + } + + /** + * Return first non-whitespace position greater or equal to passed position. + * + * @param int $pos Token position + * @return int Non-whitespace token position + */ + public function skipRightWhitespace(int $pos) { + $tokens = $this->tokens; + for ($count = \count($tokens); $pos < $count; $pos++) { + $type = $tokens[$pos][0]; + if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) { + break; + } + } + return $pos; + } + + public function findRight($pos, $findTokenType) { + $tokens = $this->tokens; + for ($count = \count($tokens); $pos < $count; $pos++) { + $type = $tokens[$pos][0]; + if ($type === $findTokenType) { + return $pos; + } + } + return -1; + } + + /** + * Get indentation before token position. + * + * @param int $pos Token position + * + * @return int Indentation depth (in spaces) + */ + public function getIndentationBefore(int $pos) : int { + return $this->indentMap[$pos]; + } + + /** + * Get the code corresponding to a token offset range, optionally adjusted for indentation. + * + * @param int $from Token start position (inclusive) + * @param int $to Token end position (exclusive) + * @param int $indent By how much the code should be indented (can be negative as well) + * + * @return string Code corresponding to token range, adjusted for indentation + */ + public function getTokenCode(int $from, int $to, int $indent) : string { + $tokens = $this->tokens; + $result = ''; + for ($pos = $from; $pos < $to; $pos++) { + $token = $tokens[$pos]; + if (\is_array($token)) { + $type = $token[0]; + $content = $token[1]; + if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) { + $result .= $content; + } else { + // TODO Handle non-space indentation + if ($indent < 0) { + $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content); + } elseif ($indent > 0) { + $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content); + } else { + $result .= $content; + } + } + } else { + $result .= $token; + } + } + return $result; + } + + /** + * Precalculate the indentation at every token position. + * + * @return int[] Token position to indentation map + */ + private function calcIndentMap() { + $indentMap = []; + $indent = 0; + foreach ($this->tokens as $token) { + $indentMap[] = $indent; + + if ($token[0] === \T_WHITESPACE) { + $content = $token[1]; + $newlinePos = \strrpos($content, "\n"); + if (false !== $newlinePos) { + $indent = \strlen($content) - $newlinePos - 1; + } + } + } + + // Add a sentinel for one past end of the file + $indentMap[] = $indent; + + return $indentMap; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php b/vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php new file mode 100644 index 0000000..25d1c6a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php @@ -0,0 +1,101 @@ +decodeRecursive($value); + } + + private function decodeRecursive($value) { + if (\is_array($value)) { + if (isset($value['nodeType'])) { + if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') { + return $this->decodeComment($value); + } + return $this->decodeNode($value); + } + return $this->decodeArray($value); + } + return $value; + } + + private function decodeArray(array $array) : array { + $decodedArray = []; + foreach ($array as $key => $value) { + $decodedArray[$key] = $this->decodeRecursive($value); + } + return $decodedArray; + } + + private function decodeNode(array $value) : Node { + $nodeType = $value['nodeType']; + if (!\is_string($nodeType)) { + throw new \RuntimeException('Node type must be a string'); + } + + $reflectionClass = $this->reflectionClassFromNodeType($nodeType); + /** @var Node $node */ + $node = $reflectionClass->newInstanceWithoutConstructor(); + + if (isset($value['attributes'])) { + if (!\is_array($value['attributes'])) { + throw new \RuntimeException('Attributes must be an array'); + } + + $node->setAttributes($this->decodeArray($value['attributes'])); + } + + foreach ($value as $name => $subNode) { + if ($name === 'nodeType' || $name === 'attributes') { + continue; + } + + $node->$name = $this->decodeRecursive($subNode); + } + + return $node; + } + + private function decodeComment(array $value) : Comment { + $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; + if (!isset($value['text'])) { + throw new \RuntimeException('Comment must have text'); + } + + return new $className( + $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1 + ); + } + + private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass { + if (!isset($this->reflectionClassCache[$nodeType])) { + $className = $this->classNameFromNodeType($nodeType); + $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className); + } + return $this->reflectionClassCache[$nodeType]; + } + + private function classNameFromNodeType(string $nodeType) : string { + $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); + if (class_exists($className)) { + return $className; + } + + $className .= '_'; + if (class_exists($className)) { + return $className; + } + + throw new \RuntimeException("Unknown node type \"$nodeType\""); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer.php new file mode 100644 index 0000000..07ec5ca --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer.php @@ -0,0 +1,406 @@ +tokenMap = $this->createTokenMap(); + + // Compatibility define for PHP < 7.4 + if (!defined('T_BAD_CHARACTER')) { + \define('T_BAD_CHARACTER', -1); + } + + // map of tokens to drop while lexing (the map is only used for isset lookup, + // that's why the value is simply set to 1; the value is never actually used.) + $this->dropTokens = array_fill_keys( + [\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1 + ); + + $defaultAttributes = ['comments', 'startLine', 'endLine']; + $usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true); + + // Create individual boolean properties to make these checks faster. + $this->attributeStartLineUsed = isset($usedAttributes['startLine']); + $this->attributeEndLineUsed = isset($usedAttributes['endLine']); + $this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']); + $this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']); + $this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']); + $this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']); + $this->attributeCommentsUsed = isset($usedAttributes['comments']); + } + + /** + * Initializes the lexer for lexing the provided source code. + * + * This function does not throw if lexing errors occur. Instead, errors may be retrieved using + * the getErrors() method. + * + * @param string $code The source code to lex + * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to + * ErrorHandler\Throwing + */ + public function startLexing(string $code, ErrorHandler $errorHandler = null) { + if (null === $errorHandler) { + $errorHandler = new ErrorHandler\Throwing(); + } + + $this->code = $code; // keep the code around for __halt_compiler() handling + $this->pos = -1; + $this->line = 1; + $this->filePos = 0; + + // If inline HTML occurs without preceding code, treat it as if it had a leading newline. + // This ensures proper composability, because having a newline is the "safe" assumption. + $this->prevCloseTagHasNewline = true; + + $scream = ini_set('xdebug.scream', '0'); + + error_clear_last(); + $this->tokens = @token_get_all($code); + $this->handleErrors($errorHandler); + + if (false !== $scream) { + ini_set('xdebug.scream', $scream); + } + } + + private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) { + $tokens = []; + for ($i = $start; $i < $end; $i++) { + $chr = $this->code[$i]; + if ($chr === "\0") { + // PHP cuts error message after null byte, so need special case + $errorMsg = 'Unexpected null byte'; + } else { + $errorMsg = sprintf( + 'Unexpected character "%s" (ASCII %d)', $chr, ord($chr) + ); + } + + $tokens[] = [\T_BAD_CHARACTER, $chr, $line]; + $errorHandler->handleError(new Error($errorMsg, [ + 'startLine' => $line, + 'endLine' => $line, + 'startFilePos' => $i, + 'endFilePos' => $i, + ])); + } + return $tokens; + } + + /** + * Check whether comment token is unterminated. + * + * @return bool + */ + private function isUnterminatedComment($token) : bool { + return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT) + && substr($token[1], 0, 2) === '/*' + && substr($token[1], -2) !== '*/'; + } + + /** + * Check whether an error *may* have occurred during tokenization. + * + * @return bool + */ + private function errorMayHaveOccurred() : bool { + if (defined('HHVM_VERSION')) { + // In HHVM token_get_all() does not throw warnings, so we need to conservatively + // assume that an error occurred + return true; + } + + return null !== error_get_last(); + } + + protected function handleErrors(ErrorHandler $errorHandler) { + if (!$this->errorMayHaveOccurred()) { + return; + } + + // PHP's error handling for token_get_all() is rather bad, so if we want detailed + // error information we need to compute it ourselves. Invalid character errors are + // detected by finding "gaps" in the token array. Unterminated comments are detected + // by checking if a trailing comment has a "*/" at the end. + + $filePos = 0; + $line = 1; + $numTokens = \count($this->tokens); + for ($i = 0; $i < $numTokens; $i++) { + $token = $this->tokens[$i]; + + // Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token. + // In this case we only need to emit an error. + if ($token[0] === \T_BAD_CHARACTER) { + $this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler); + } + + $tokenValue = \is_string($token) ? $token : $token[1]; + $tokenLen = \strlen($tokenValue); + + if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) { + // Something is missing, must be an invalid character + $nextFilePos = strpos($this->code, $tokenValue, $filePos); + $badCharTokens = $this->handleInvalidCharacterRange( + $filePos, $nextFilePos, $line, $errorHandler); + $filePos = (int) $nextFilePos; + + array_splice($this->tokens, $i, 0, $badCharTokens); + $numTokens += \count($badCharTokens); + $i += \count($badCharTokens); + } + + $filePos += $tokenLen; + $line += substr_count($tokenValue, "\n"); + } + + if ($filePos !== \strlen($this->code)) { + if (substr($this->code, $filePos, 2) === '/*') { + // Unlike PHP, HHVM will drop unterminated comments entirely + $comment = substr($this->code, $filePos); + $errorHandler->handleError(new Error('Unterminated comment', [ + 'startLine' => $line, + 'endLine' => $line + substr_count($comment, "\n"), + 'startFilePos' => $filePos, + 'endFilePos' => $filePos + \strlen($comment), + ])); + + // Emulate the PHP behavior + $isDocComment = isset($comment[3]) && $comment[3] === '*'; + $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line]; + } else { + // Invalid characters at the end of the input + $badCharTokens = $this->handleInvalidCharacterRange( + $filePos, \strlen($this->code), $line, $errorHandler); + $this->tokens = array_merge($this->tokens, $badCharTokens); + } + return; + } + + if (count($this->tokens) > 0) { + // Check for unterminated comment + $lastToken = $this->tokens[count($this->tokens) - 1]; + if ($this->isUnterminatedComment($lastToken)) { + $errorHandler->handleError(new Error('Unterminated comment', [ + 'startLine' => $line - substr_count($lastToken[1], "\n"), + 'endLine' => $line, + 'startFilePos' => $filePos - \strlen($lastToken[1]), + 'endFilePos' => $filePos, + ])); + } + } + } + + /** + * Fetches the next token. + * + * The available attributes are determined by the 'usedAttributes' option, which can + * be specified in the constructor. The following attributes are supported: + * + * * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances, + * representing all comments that occurred between the previous + * non-discarded token and the current one. + * * 'startLine' => Line in which the node starts. + * * 'endLine' => Line in which the node ends. + * * 'startTokenPos' => Offset into the token array of the first token in the node. + * * 'endTokenPos' => Offset into the token array of the last token in the node. + * * 'startFilePos' => Offset into the code string of the first character that is part of the node. + * * 'endFilePos' => Offset into the code string of the last character that is part of the node. + * + * @param mixed $value Variable to store token content in + * @param mixed $startAttributes Variable to store start attributes in + * @param mixed $endAttributes Variable to store end attributes in + * + * @return int Token id + */ + public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int { + $startAttributes = []; + $endAttributes = []; + + while (1) { + if (isset($this->tokens[++$this->pos])) { + $token = $this->tokens[$this->pos]; + } else { + // EOF token with ID 0 + $token = "\0"; + } + + if ($this->attributeStartLineUsed) { + $startAttributes['startLine'] = $this->line; + } + if ($this->attributeStartTokenPosUsed) { + $startAttributes['startTokenPos'] = $this->pos; + } + if ($this->attributeStartFilePosUsed) { + $startAttributes['startFilePos'] = $this->filePos; + } + + if (\is_string($token)) { + $value = $token; + if (isset($token[1])) { + // bug in token_get_all + $this->filePos += 2; + $id = ord('"'); + } else { + $this->filePos += 1; + $id = ord($token); + } + } elseif (!isset($this->dropTokens[$token[0]])) { + $value = $token[1]; + $id = $this->tokenMap[$token[0]]; + if (\T_CLOSE_TAG === $token[0]) { + $this->prevCloseTagHasNewline = false !== strpos($token[1], "\n"); + } elseif (\T_INLINE_HTML === $token[0]) { + $startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline; + } + + $this->line += substr_count($value, "\n"); + $this->filePos += \strlen($value); + } else { + if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) { + if ($this->attributeCommentsUsed) { + $comment = \T_DOC_COMMENT === $token[0] + ? new Comment\Doc($token[1], $this->line, $this->filePos, $this->pos) + : new Comment($token[1], $this->line, $this->filePos, $this->pos); + $startAttributes['comments'][] = $comment; + } + } + + $this->line += substr_count($token[1], "\n"); + $this->filePos += \strlen($token[1]); + continue; + } + + if ($this->attributeEndLineUsed) { + $endAttributes['endLine'] = $this->line; + } + if ($this->attributeEndTokenPosUsed) { + $endAttributes['endTokenPos'] = $this->pos; + } + if ($this->attributeEndFilePosUsed) { + $endAttributes['endFilePos'] = $this->filePos - 1; + } + + return $id; + } + + throw new \RuntimeException('Reached end of lexer loop'); + } + + /** + * Returns the token array for current code. + * + * The token array is in the same format as provided by the + * token_get_all() function and does not discard tokens (i.e. + * whitespace and comments are included). The token position + * attributes are against this token array. + * + * @return array Array of tokens in token_get_all() format + */ + public function getTokens() : array { + return $this->tokens; + } + + /** + * Handles __halt_compiler() by returning the text after it. + * + * @return string Remaining text + */ + public function handleHaltCompiler() : string { + // text after T_HALT_COMPILER, still including (); + $textAfter = substr($this->code, $this->filePos); + + // ensure that it is followed by (); + // this simplifies the situation, by not allowing any comments + // in between of the tokens. + if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) { + throw new Error('__HALT_COMPILER must be followed by "();"'); + } + + // prevent the lexer from returning any further tokens + $this->pos = count($this->tokens); + + // return with (); removed + return substr($textAfter, strlen($matches[0])); + } + + /** + * Creates the token map. + * + * The token map maps the PHP internal token identifiers + * to the identifiers used by the Parser. Additionally it + * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'. + * + * @return array The token map + */ + protected function createTokenMap() : array { + $tokenMap = []; + + // 256 is the minimum possible token number, as everything below + // it is an ASCII value + for ($i = 256; $i < 1000; ++$i) { + if (\T_DOUBLE_COLON === $i) { + // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM + $tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM; + } elseif(\T_OPEN_TAG_WITH_ECHO === $i) { + // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO + $tokenMap[$i] = Tokens::T_ECHO; + } elseif(\T_CLOSE_TAG === $i) { + // T_CLOSE_TAG is equivalent to ';' + $tokenMap[$i] = ord(';'); + } elseif ('UNKNOWN' !== $name = token_name($i)) { + if ('T_HASHBANG' === $name) { + // HHVM uses a special token for #! hashbang lines + $tokenMap[$i] = Tokens::T_INLINE_HTML; + } elseif (defined($name = Tokens::class . '::' . $name)) { + // Other tokens can be mapped directly + $tokenMap[$i] = constant($name); + } + } + } + + // HHVM uses a special token for numbers that overflow to double + if (defined('T_ONUMBER')) { + $tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER; + } + // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant + if (defined('T_COMPILER_HALT_OFFSET')) { + $tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING; + } + + return $tokenMap; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php new file mode 100644 index 0000000..0e14541 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php @@ -0,0 +1,250 @@ +\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?(?:;?[\r\n])?)/x +REGEX; + + /** @var mixed[] Patches used to reverse changes introduced in the code */ + private $patches = []; + + /** @var TokenEmulatorInterface[] */ + private $tokenEmulators = []; + + /** + * @param mixed[] $options + */ + public function __construct(array $options = []) + { + parent::__construct($options); + + $this->tokenEmulators[] = new FnTokenEmulator(); + $this->tokenEmulators[] = new CoaleseEqualTokenEmulator(); + $this->tokenEmulators[] = new NumericLiteralSeparatorEmulator(); + + $this->tokenMap[self::T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL; + $this->tokenMap[self::T_FN] = Tokens::T_FN; + } + + public function startLexing(string $code, ErrorHandler $errorHandler = null) { + $this->patches = []; + + if ($this->isEmulationNeeded($code) === false) { + // Nothing to emulate, yay + parent::startLexing($code, $errorHandler); + return; + } + + $collector = new ErrorHandler\Collecting(); + + // 1. emulation of heredoc and nowdoc new syntax + $preparedCode = $this->processHeredocNowdoc($code); + parent::startLexing($preparedCode, $collector); + $this->fixupTokens(); + + $errors = $collector->getErrors(); + if (!empty($errors)) { + $this->fixupErrors($errors); + foreach ($errors as $error) { + $errorHandler->handleError($error); + } + } + + // add token emulation + foreach ($this->tokenEmulators as $emulativeToken) { + if ($emulativeToken->isEmulationNeeded($code)) { + $this->tokens = $emulativeToken->emulate($code, $this->tokens); + } + } + } + + private function isHeredocNowdocEmulationNeeded(string $code): bool + { + // skip version where this works without emulation + if (version_compare(\PHP_VERSION, self::PHP_7_3, '>=')) { + return false; + } + + return strpos($code, '<<<') !== false; + } + + private function processHeredocNowdoc(string $code): string + { + if ($this->isHeredocNowdocEmulationNeeded($code) === false) { + return $code; + } + + if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) { + // No heredoc/nowdoc found + return $code; + } + + // Keep track of how much we need to adjust string offsets due to the modifications we + // already made + $posDelta = 0; + foreach ($matches as $match) { + $indentation = $match['indentation'][0]; + $indentationStart = $match['indentation'][1]; + + $separator = $match['separator'][0]; + $separatorStart = $match['separator'][1]; + + if ($indentation === '' && $separator !== '') { + // Ordinary heredoc/nowdoc + continue; + } + + if ($indentation !== '') { + // Remove indentation + $indentationLen = strlen($indentation); + $code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen); + $this->patches[] = [$indentationStart + $posDelta, 'add', $indentation]; + $posDelta -= $indentationLen; + } + + if ($separator === '') { + // Insert newline as separator + $code = substr_replace($code, "\n", $separatorStart + $posDelta, 0); + $this->patches[] = [$separatorStart + $posDelta, 'remove', "\n"]; + $posDelta += 1; + } + } + + return $code; + } + + private function isEmulationNeeded(string $code): bool + { + foreach ($this->tokenEmulators as $emulativeToken) { + if ($emulativeToken->isEmulationNeeded($code)) { + return true; + } + } + + return $this->isHeredocNowdocEmulationNeeded($code); + } + + private function fixupTokens() + { + if (\count($this->patches) === 0) { + return; + } + + // Load first patch + $patchIdx = 0; + + list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; + + // We use a manual loop over the tokens, because we modify the array on the fly + $pos = 0; + for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) { + $token = $this->tokens[$i]; + if (\is_string($token)) { + // We assume that patches don't apply to string tokens + $pos += \strlen($token); + continue; + } + + $len = \strlen($token[1]); + $posDelta = 0; + while ($patchPos >= $pos && $patchPos < $pos + $len) { + $patchTextLen = \strlen($patchText); + if ($patchType === 'remove') { + if ($patchPos === $pos && $patchTextLen === $len) { + // Remove token entirely + array_splice($this->tokens, $i, 1, []); + $i--; + $c--; + } else { + // Remove from token string + $this->tokens[$i][1] = substr_replace( + $token[1], '', $patchPos - $pos + $posDelta, $patchTextLen + ); + $posDelta -= $patchTextLen; + } + } elseif ($patchType === 'add') { + // Insert into the token string + $this->tokens[$i][1] = substr_replace( + $token[1], $patchText, $patchPos - $pos + $posDelta, 0 + ); + $posDelta += $patchTextLen; + } else { + assert(false); + } + + // Fetch the next patch + $patchIdx++; + if ($patchIdx >= \count($this->patches)) { + // No more patches, we're done + return; + } + + list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; + + // Multiple patches may apply to the same token. Reload the current one to check + // If the new patch applies + $token = $this->tokens[$i]; + } + + $pos += $len; + } + + // A patch did not apply + assert(false); + } + + /** + * Fixup line and position information in errors. + * + * @param Error[] $errors + */ + private function fixupErrors(array $errors) { + foreach ($errors as $error) { + $attrs = $error->getAttributes(); + + $posDelta = 0; + $lineDelta = 0; + foreach ($this->patches as $patch) { + list($patchPos, $patchType, $patchText) = $patch; + if ($patchPos >= $attrs['startFilePos']) { + // No longer relevant + break; + } + + if ($patchType === 'add') { + $posDelta += strlen($patchText); + $lineDelta += substr_count($patchText, "\n"); + } else { + $posDelta -= strlen($patchText); + $lineDelta -= substr_count($patchText, "\n"); + } + } + + $attrs['startFilePos'] += $posDelta; + $attrs['endFilePos'] += $posDelta; + $attrs['startLine'] += $lineDelta; + $attrs['endLine'] += $lineDelta; + $error->setAttributes($attrs); + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php new file mode 100644 index 0000000..10f7e1e --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php @@ -0,0 +1,41 @@ +=')) { + return false; + } + + return strpos($code, '??=') !== false; + } + + public function emulate(string $code, array $tokens): array + { + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + $line = 1; + for ($i = 0, $c = count($tokens); $i < $c; ++$i) { + if (isset($tokens[$i + 1])) { + if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') { + array_splice($tokens, $i, 2, [ + [Emulative::T_COALESCE_EQUAL, '??=', $line] + ]); + $c--; + continue; + } + } + if (\is_array($tokens[$i])) { + $line += substr_count($tokens[$i][1], "\n"); + } + } + + return $tokens; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php new file mode 100644 index 0000000..0939dd4 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php @@ -0,0 +1,53 @@ +=')) { + return false; + } + + return strpos($code, 'fn') !== false; + } + + public function emulate(string $code, array $tokens): array + { + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + foreach ($tokens as $i => $token) { + if ($token[0] === T_STRING && $token[1] === 'fn') { + $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i); + if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === T_OBJECT_OPERATOR) { + continue; + } + + $tokens[$i][0] = Emulative::T_FN; + } + } + + return $tokens; + } + + /** + * @param mixed[] $tokens + * @return mixed[]|null + */ + private function getPreviousNonSpaceToken(array $tokens, int $start) + { + for ($i = $start - 1; $i >= 0; --$i) { + if ($tokens[$i][0] === T_WHITESPACE) { + continue; + } + + return $tokens[$i]; + } + + return null; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php new file mode 100644 index 0000000..f564c59 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php @@ -0,0 +1,98 @@ +=')) { + return false; + } + + return preg_match('~[0-9a-f]_[0-9a-f]~i', $code) !== false; + } + + public function emulate(string $code, array $tokens): array + { + // We need to manually iterate and manage a count because we'll change + // the tokens array on the way + $codeOffset = 0; + for ($i = 0, $c = count($tokens); $i < $c; ++$i) { + $token = $tokens[$i]; + $tokenLen = \strlen(\is_array($token) ? $token[1] : $token); + + if ($token[0] !== T_LNUMBER && $token[0] !== T_DNUMBER) { + $codeOffset += $tokenLen; + continue; + } + + $res = preg_match(self::NUMBER, $code, $matches, 0, $codeOffset); + assert($res, "No number at number token position"); + + $match = $matches[0]; + $matchLen = \strlen($match); + if ($matchLen === $tokenLen) { + // Original token already holds the full number. + $codeOffset += $tokenLen; + continue; + } + + $tokenKind = $this->resolveIntegerOrFloatToken($match); + $newTokens = [[$tokenKind, $match, $token[2]]]; + + $numTokens = 1; + $len = $tokenLen; + while ($matchLen > $len) { + $nextToken = $tokens[$i + $numTokens]; + $nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken; + $nextTokenLen = \strlen($nextTokenText); + + $numTokens++; + if ($matchLen < $len + $nextTokenLen) { + // Split trailing characters into a partial token. + assert(is_array($nextToken), "Partial token should be an array token"); + $partialText = substr($nextTokenText, $matchLen - $len); + $newTokens[] = [$nextToken[0], $partialText, $nextToken[2]]; + break; + } + + $len += $nextTokenLen; + } + + array_splice($tokens, $i, $numTokens, $newTokens); + $c -= $numTokens - \count($newTokens); + $codeOffset += $matchLen; + } + + return $tokens; + } + + private function resolveIntegerOrFloatToken(string $str): int + { + $str = str_replace('_', '', $str); + + if (stripos($str, '0b') === 0) { + $num = bindec($str); + } elseif (stripos($str, '0x') === 0) { + $num = hexdec($str); + } elseif (stripos($str, '0') === 0 && ctype_digit($str)) { + $num = octdec($str); + } else { + $num = +$str; + } + + return is_float($num) ? T_DNUMBER : T_LNUMBER; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulatorInterface.php b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulatorInterface.php new file mode 100644 index 0000000..e03ccc6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulatorInterface.php @@ -0,0 +1,14 @@ + [aliasName => originalName]] */ + protected $aliases = []; + + /** @var Name[][] Same as $aliases but preserving original case */ + protected $origAliases = []; + + /** @var ErrorHandler Error handler */ + protected $errorHandler; + + /** + * Create a name context. + * + * @param ErrorHandler $errorHandler Error handling used to report errors + */ + public function __construct(ErrorHandler $errorHandler) { + $this->errorHandler = $errorHandler; + } + + /** + * Start a new namespace. + * + * This also resets the alias table. + * + * @param Name|null $namespace Null is the global namespace + */ + public function startNamespace(Name $namespace = null) { + $this->namespace = $namespace; + $this->origAliases = $this->aliases = [ + Stmt\Use_::TYPE_NORMAL => [], + Stmt\Use_::TYPE_FUNCTION => [], + Stmt\Use_::TYPE_CONSTANT => [], + ]; + } + + /** + * Add an alias / import. + * + * @param Name $name Original name + * @param string $aliasName Aliased name + * @param int $type One of Stmt\Use_::TYPE_* + * @param array $errorAttrs Attributes to use to report an error + */ + public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) { + // Constant names are case sensitive, everything else case insensitive + if ($type === Stmt\Use_::TYPE_CONSTANT) { + $aliasLookupName = $aliasName; + } else { + $aliasLookupName = strtolower($aliasName); + } + + if (isset($this->aliases[$type][$aliasLookupName])) { + $typeStringMap = [ + Stmt\Use_::TYPE_NORMAL => '', + Stmt\Use_::TYPE_FUNCTION => 'function ', + Stmt\Use_::TYPE_CONSTANT => 'const ', + ]; + + $this->errorHandler->handleError(new Error( + sprintf( + 'Cannot use %s%s as %s because the name is already in use', + $typeStringMap[$type], $name, $aliasName + ), + $errorAttrs + )); + return; + } + + $this->aliases[$type][$aliasLookupName] = $name; + $this->origAliases[$type][$aliasName] = $name; + } + + /** + * Get current namespace. + * + * @return null|Name Namespace (or null if global namespace) + */ + public function getNamespace() { + return $this->namespace; + } + + /** + * Get resolved name. + * + * @param Name $name Name to resolve + * @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT} + * + * @return null|Name Resolved name, or null if static resolution is not possible + */ + public function getResolvedName(Name $name, int $type) { + // don't resolve special class names + if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { + if (!$name->isUnqualified()) { + $this->errorHandler->handleError(new Error( + sprintf("'\\%s' is an invalid class name", $name->toString()), + $name->getAttributes() + )); + } + return $name; + } + + // fully qualified names are already resolved + if ($name->isFullyQualified()) { + return $name; + } + + // Try to resolve aliases + if (null !== $resolvedName = $this->resolveAlias($name, $type)) { + return $resolvedName; + } + + if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) { + if (null === $this->namespace) { + // outside of a namespace unaliased unqualified is same as fully qualified + return new FullyQualified($name, $name->getAttributes()); + } + + // Cannot resolve statically + return null; + } + + // if no alias exists prepend current namespace + return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); + } + + /** + * Get resolved class name. + * + * @param Name $name Class ame to resolve + * + * @return Name Resolved name + */ + public function getResolvedClassName(Name $name) : Name { + return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL); + } + + /** + * Get possible ways of writing a fully qualified name (e.g., by making use of aliases). + * + * @param string $name Fully-qualified name (without leading namespace separator) + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name[] Possible representations of the name + */ + public function getPossibleNames(string $name, int $type) : array { + $lcName = strtolower($name); + + if ($type === Stmt\Use_::TYPE_NORMAL) { + // self, parent and static must always be unqualified + if ($lcName === "self" || $lcName === "parent" || $lcName === "static") { + return [new Name($name)]; + } + } + + // Collect possible ways to write this name, starting with the fully-qualified name + $possibleNames = [new FullyQualified($name)]; + + if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) { + // Make sure there is no alias that makes the normally namespace-relative name + // into something else + if (null === $this->resolveAlias($nsRelativeName, $type)) { + $possibleNames[] = $nsRelativeName; + } + } + + // Check for relevant namespace use statements + foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) { + $lcOrig = $orig->toLowerString(); + if (0 === strpos($lcName, $lcOrig . '\\')) { + $possibleNames[] = new Name($alias . substr($name, strlen($lcOrig))); + } + } + + // Check for relevant type-specific use statements + foreach ($this->origAliases[$type] as $alias => $orig) { + if ($type === Stmt\Use_::TYPE_CONSTANT) { + // Constants are are complicated-sensitive + $normalizedOrig = $this->normalizeConstName($orig->toString()); + if ($normalizedOrig === $this->normalizeConstName($name)) { + $possibleNames[] = new Name($alias); + } + } else { + // Everything else is case-insensitive + if ($orig->toLowerString() === $lcName) { + $possibleNames[] = new Name($alias); + } + } + } + + return $possibleNames; + } + + /** + * Get shortest representation of this fully-qualified name. + * + * @param string $name Fully-qualified name (without leading namespace separator) + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name Shortest representation + */ + public function getShortName(string $name, int $type) : Name { + $possibleNames = $this->getPossibleNames($name, $type); + + // Find shortest name + $shortestName = null; + $shortestLength = \INF; + foreach ($possibleNames as $possibleName) { + $length = strlen($possibleName->toCodeString()); + if ($length < $shortestLength) { + $shortestName = $possibleName; + $shortestLength = $length; + } + } + + return $shortestName; + } + + private function resolveAlias(Name $name, $type) { + $firstPart = $name->getFirst(); + + if ($name->isQualified()) { + // resolve aliases for qualified names, always against class alias table + $checkName = strtolower($firstPart); + if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) { + $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName]; + return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); + } + } elseif ($name->isUnqualified()) { + // constant aliases are case-sensitive, function aliases case-insensitive + $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart); + if (isset($this->aliases[$type][$checkName])) { + // resolve unqualified aliases + return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes()); + } + } + + // No applicable aliases + return null; + } + + private function getNamespaceRelativeName(string $name, string $lcName, int $type) { + if (null === $this->namespace) { + return new Name($name); + } + + if ($type === Stmt\Use_::TYPE_CONSTANT) { + // The constants true/false/null always resolve to the global symbols, even inside a + // namespace, so they may be used without qualification + if ($lcName === "true" || $lcName === "false" || $lcName === "null") { + return new Name($name); + } + } + + $namespacePrefix = strtolower($this->namespace . '\\'); + if (0 === strpos($lcName, $namespacePrefix)) { + return new Name(substr($name, strlen($namespacePrefix))); + } + + return null; + } + + private function normalizeConstName(string $name) { + $nsSep = strrpos($name, '\\'); + if (false === $nsSep) { + return $name; + } + + // Constants have case-insensitive namespace and case-sensitive short-name + $ns = substr($name, 0, $nsSep); + $shortName = substr($name, $nsSep + 1); + return strtolower($ns) . '\\' . $shortName; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node.php b/vendor/nikic/php-parser/lib/PhpParser/Node.php new file mode 100644 index 0000000..7f04c34 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node.php @@ -0,0 +1,153 @@ +attributes = $attributes; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; + } + + public function getSubNodeNames() : array { + return ['value', 'byRef', 'unpack']; + } + + public function getType() : string { + return 'Arg'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php new file mode 100644 index 0000000..4094a65 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php @@ -0,0 +1,37 @@ +attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['name', 'value']; + } + + public function getType() : string { + return 'Const'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php new file mode 100644 index 0000000..6cf4df2 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php @@ -0,0 +1,9 @@ +attributes = $attributes; + $this->var = $var; + $this->dim = $dim; + } + + public function getSubNodeNames() : array { + return ['var', 'dim']; + } + + public function getType() : string { + return 'Expr_ArrayDimFetch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php new file mode 100644 index 0000000..1b078f8 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.php @@ -0,0 +1,41 @@ +attributes = $attributes; + $this->key = $key; + $this->value = $value; + $this->byRef = $byRef; + $this->unpack = $unpack; + } + + public function getSubNodeNames() : array { + return ['key', 'value', 'byRef', 'unpack']; + } + + public function getType() : string { + return 'Expr_ArrayItem'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php new file mode 100644 index 0000000..fbe207e --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->items = $items; + } + + public function getSubNodeNames() : array { + return ['items']; + } + + public function getType() : string { + return 'Expr_Array'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php new file mode 100644 index 0000000..b4b816f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php @@ -0,0 +1,71 @@ + false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'expr' => Expr : Expression body + * @param array $attributes Additional attributes + */ + public function __construct(array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->static = $subNodes['static'] ?? false; + $this->byRef = $subNodes['byRef'] ?? false; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->expr = $subNodes['expr'] ?? null; + } + + public function getSubNodeNames() : array { + return ['static', 'byRef', 'params', 'returnType', 'expr']; + } + + public function returnsByRef() : bool { + return $this->byRef; + } + + public function getParams() : array { + return $this->params; + } + + public function getReturnType() { + return $this->returnType; + } + + /** + * @return Node\Stmt\Return_[] + */ + public function getStmts() : array { + return [new Node\Stmt\Return_($this->expr)]; + } + + public function getType() : string { + return 'Expr_ArrowFunction'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php new file mode 100644 index 0000000..cf9e6e8 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['var', 'expr']; + } + + public function getType() : string { + return 'Expr_Assign'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php new file mode 100644 index 0000000..bce8604 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['var', 'expr']; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php new file mode 100644 index 0000000..420284c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php @@ -0,0 +1,12 @@ +attributes = $attributes; + $this->var = $var; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['var', 'expr']; + } + + public function getType() : string { + return 'Expr_AssignRef'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php new file mode 100644 index 0000000..d9c582b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php @@ -0,0 +1,40 @@ +attributes = $attributes; + $this->left = $left; + $this->right = $right; + } + + public function getSubNodeNames() : array { + return ['left', 'right']; + } + + /** + * Get the operator sigil for this binary operation. + * + * In the case there are multiple possible sigils for an operator, this method does not + * necessarily return the one used in the parsed code. + * + * @return string + */ + abstract public function getOperatorSigil() : string; +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php new file mode 100644 index 0000000..d907393 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php @@ -0,0 +1,16 @@ +'; + } + + public function getType() : string { + return 'Expr_BinaryOp_Greater'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php new file mode 100644 index 0000000..d677502 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php @@ -0,0 +1,16 @@ +='; + } + + public function getType() : string { + return 'Expr_BinaryOp_GreaterOrEqual'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php new file mode 100644 index 0000000..3d96285 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php @@ -0,0 +1,16 @@ +>'; + } + + public function getType() : string { + return 'Expr_BinaryOp_ShiftRight'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php new file mode 100644 index 0000000..3cb8e7e --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php @@ -0,0 +1,16 @@ +'; + } + + public function getType() : string { + return 'Expr_BinaryOp_Spaceship'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php new file mode 100644 index 0000000..ed44984 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_BitwiseNot'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php new file mode 100644 index 0000000..bf27e9f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_BooleanNot'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php new file mode 100644 index 0000000..36769d4 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php @@ -0,0 +1,26 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php new file mode 100644 index 0000000..57cc473 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php @@ -0,0 +1,12 @@ +attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['class', 'name']; + } + + public function getType() : string { + return 'Expr_ClassConstFetch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php new file mode 100644 index 0000000..db216b8 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Clone'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php new file mode 100644 index 0000000..dfda88a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php @@ -0,0 +1,71 @@ + false : Whether the closure is static + * 'byRef' => false : Whether to return by reference + * 'params' => array(): Parameters + * 'uses' => array(): use()s + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct(array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->static = $subNodes['static'] ?? false; + $this->byRef = $subNodes['byRef'] ?? false; + $this->params = $subNodes['params'] ?? []; + $this->uses = $subNodes['uses'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; + } + + public function returnsByRef() : bool { + return $this->byRef; + } + + public function getParams() : array { + return $this->params; + } + + public function getReturnType() { + return $this->returnType; + } + + /** @return Node\Stmt[] */ + public function getStmts() : array { + return $this->stmts; + } + + public function getType() : string { + return 'Expr_Closure'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php new file mode 100644 index 0000000..2b8a096 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->var = $var; + $this->byRef = $byRef; + } + + public function getSubNodeNames() : array { + return ['var', 'byRef']; + } + + public function getType() : string { + return 'Expr_ClosureUse'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php new file mode 100644 index 0000000..14ebd16 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php @@ -0,0 +1,31 @@ +attributes = $attributes; + $this->name = $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Expr_ConstFetch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php new file mode 100644 index 0000000..4042ec9 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Empty'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php new file mode 100644 index 0000000..1637f3a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php @@ -0,0 +1,31 @@ +attributes = $attributes; + } + + public function getSubNodeNames() : array { + return []; + } + + public function getType() : string { + return 'Expr_Error'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php new file mode 100644 index 0000000..c44ff6f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_ErrorSuppress'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php new file mode 100644 index 0000000..8568547 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Eval'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php new file mode 100644 index 0000000..b88a8f7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Exit'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php new file mode 100644 index 0000000..1e8afa5 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php @@ -0,0 +1,35 @@ +attributes = $attributes; + $this->name = $name; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['name', 'args']; + } + + public function getType() : string { + return 'Expr_FuncCall'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php new file mode 100644 index 0000000..07ce596 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php @@ -0,0 +1,39 @@ +attributes = $attributes; + $this->expr = $expr; + $this->type = $type; + } + + public function getSubNodeNames() : array { + return ['expr', 'type']; + } + + public function getType() : string { + return 'Expr_Include'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php new file mode 100644 index 0000000..9000d47 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php @@ -0,0 +1,35 @@ +attributes = $attributes; + $this->expr = $expr; + $this->class = $class; + } + + public function getSubNodeNames() : array { + return ['expr', 'class']; + } + + public function getType() : string { + return 'Expr_Instanceof'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php new file mode 100644 index 0000000..76b7387 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Expr_Isset'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php new file mode 100644 index 0000000..c27a27b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->items = $items; + } + + public function getSubNodeNames() : array { + return ['items']; + } + + public function getType() : string { + return 'Expr_List'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php new file mode 100644 index 0000000..bd81bb4 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php @@ -0,0 +1,40 @@ +attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['var', 'name', 'args']; + } + + public function getType() : string { + return 'Expr_MethodCall'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php new file mode 100644 index 0000000..c86f0c6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php @@ -0,0 +1,35 @@ +attributes = $attributes; + $this->class = $class; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['class', 'args']; + } + + public function getType() : string { + return 'Expr_New'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php new file mode 100644 index 0000000..94d6c29 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PostDec'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php new file mode 100644 index 0000000..005c443 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PostInc'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php new file mode 100644 index 0000000..a5ca685 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PreDec'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php new file mode 100644 index 0000000..0986c44 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->var = $var; + } + + public function getSubNodeNames() : array { + return ['var']; + } + + public function getType() : string { + return 'Expr_PreInc'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php new file mode 100644 index 0000000..2d43c2a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_Print'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php new file mode 100644 index 0000000..4281f31 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php @@ -0,0 +1,35 @@ +attributes = $attributes; + $this->var = $var; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['var', 'name']; + } + + public function getType() : string { + return 'Expr_PropertyFetch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php new file mode 100644 index 0000000..537a7cc --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->parts = $parts; + } + + public function getSubNodeNames() : array { + return ['parts']; + } + + public function getType() : string { + return 'Expr_ShellExec'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php new file mode 100644 index 0000000..9883f5a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php @@ -0,0 +1,40 @@ +attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new Identifier($name) : $name; + $this->args = $args; + } + + public function getSubNodeNames() : array { + return ['class', 'name', 'args']; + } + + public function getType() : string { + return 'Expr_StaticCall'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php new file mode 100644 index 0000000..1ee1a25 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php @@ -0,0 +1,36 @@ +attributes = $attributes; + $this->class = $class; + $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['class', 'name']; + } + + public function getType() : string { + return 'Expr_StaticPropertyFetch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php new file mode 100644 index 0000000..9316f47 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php @@ -0,0 +1,38 @@ +attributes = $attributes; + $this->cond = $cond; + $this->if = $if; + $this->else = $else; + } + + public function getSubNodeNames() : array { + return ['cond', 'if', 'else']; + } + + public function getType() : string { + return 'Expr_Ternary'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php new file mode 100644 index 0000000..ce8808b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_UnaryMinus'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php new file mode 100644 index 0000000..d23047e --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_UnaryPlus'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php new file mode 100644 index 0000000..1f2b231 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->name = $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Expr_Variable'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php new file mode 100644 index 0000000..a3efce6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Expr_YieldFrom'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php new file mode 100644 index 0000000..aef8fc3 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->key = $key; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['key', 'value']; + } + + public function getType() : string { + return 'Expr_Yield'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php b/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php new file mode 100644 index 0000000..d574e02 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php @@ -0,0 +1,36 @@ + true, + 'parent' => true, + 'static' => true, + ]; + + /** + * Constructs an identifier node. + * + * @param string $name Identifier as string + * @param array $attributes Additional attributes + */ + public function __construct(string $name, array $attributes = []) { + $this->attributes = $attributes; + $this->name = $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + /** + * Get identifier as string. + * + * @return string Identifier as string. + */ + public function toString() : string { + return $this->name; + } + + /** + * Get lowercased identifier as string. + * + * @return string Lowercased identifier as string + */ + public function toLowerString() : string { + return strtolower($this->name); + } + + /** + * Checks whether the identifier is a special class name (self, parent or static). + * + * @return bool Whether identifier is a special class name + */ + public function isSpecialClassName() : bool { + return isset(self::$specialClassNames[strtolower($this->name)]); + } + + /** + * Get identifier as string. + * + * @return string Identifier as string + */ + public function __toString() : string { + return $this->name; + } + + public function getType() : string { + return 'Identifier'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php new file mode 100644 index 0000000..7ebdd57 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php @@ -0,0 +1,242 @@ + true, + 'parent' => true, + 'static' => true, + ]; + + /** + * Constructs a name node. + * + * @param string|string[]|self $name Name as string, part array or Name instance (copy ctor) + * @param array $attributes Additional attributes + */ + public function __construct($name, array $attributes = []) { + $this->attributes = $attributes; + $this->parts = self::prepareName($name); + } + + public function getSubNodeNames() : array { + return ['parts']; + } + + /** + * Gets the first part of the name, i.e. everything before the first namespace separator. + * + * @return string First part of the name + */ + public function getFirst() : string { + return $this->parts[0]; + } + + /** + * Gets the last part of the name, i.e. everything after the last namespace separator. + * + * @return string Last part of the name + */ + public function getLast() : string { + return $this->parts[count($this->parts) - 1]; + } + + /** + * Checks whether the name is unqualified. (E.g. Name) + * + * @return bool Whether the name is unqualified + */ + public function isUnqualified() : bool { + return 1 === count($this->parts); + } + + /** + * Checks whether the name is qualified. (E.g. Name\Name) + * + * @return bool Whether the name is qualified + */ + public function isQualified() : bool { + return 1 < count($this->parts); + } + + /** + * Checks whether the name is fully qualified. (E.g. \Name) + * + * @return bool Whether the name is fully qualified + */ + public function isFullyQualified() : bool { + return false; + } + + /** + * Checks whether the name is explicitly relative to the current namespace. (E.g. namespace\Name) + * + * @return bool Whether the name is relative + */ + public function isRelative() : bool { + return false; + } + + /** + * Returns a string representation of the name itself, without taking taking the name type into + * account (e.g., not including a leading backslash for fully qualified names). + * + * @return string String representation + */ + public function toString() : string { + return implode('\\', $this->parts); + } + + /** + * Returns a string representation of the name as it would occur in code (e.g., including + * leading backslash for fully qualified names. + * + * @return string String representation + */ + public function toCodeString() : string { + return $this->toString(); + } + + /** + * Returns lowercased string representation of the name, without taking the name type into + * account (e.g., no leading backslash for fully qualified names). + * + * @return string Lowercased string representation + */ + public function toLowerString() : string { + return strtolower(implode('\\', $this->parts)); + } + + /** + * Checks whether the identifier is a special class name (self, parent or static). + * + * @return bool Whether identifier is a special class name + */ + public function isSpecialClassName() : bool { + return count($this->parts) === 1 + && isset(self::$specialClassNames[strtolower($this->parts[0])]); + } + + /** + * Returns a string representation of the name by imploding the namespace parts with the + * namespace separator. + * + * @return string String representation + */ + public function __toString() : string { + return implode('\\', $this->parts); + } + + /** + * Gets a slice of a name (similar to array_slice). + * + * This method returns a new instance of the same type as the original and with the same + * attributes. + * + * If the slice is empty, null is returned. The null value will be correctly handled in + * concatenations using concat(). + * + * Offset and length have the same meaning as in array_slice(). + * + * @param int $offset Offset to start the slice at (may be negative) + * @param int|null $length Length of the slice (may be negative) + * + * @return static|null Sliced name + */ + public function slice(int $offset, int $length = null) { + $numParts = count($this->parts); + + $realOffset = $offset < 0 ? $offset + $numParts : $offset; + if ($realOffset < 0 || $realOffset > $numParts) { + throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset)); + } + + if (null === $length) { + $realLength = $numParts - $realOffset; + } else { + $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; + if ($realLength < 0 || $realLength > $numParts) { + throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length)); + } + } + + if ($realLength === 0) { + // Empty slice is represented as null + return null; + } + + return new static(array_slice($this->parts, $realOffset, $realLength), $this->attributes); + } + + /** + * Concatenate two names, yielding a new Name instance. + * + * The type of the generated instance depends on which class this method is called on, for + * example Name\FullyQualified::concat() will yield a Name\FullyQualified instance. + * + * If one of the arguments is null, a new instance of the other name will be returned. If both + * arguments are null, null will be returned. As such, writing + * Name::concat($namespace, $shortName) + * where $namespace is a Name node or null will work as expected. + * + * @param string|string[]|self|null $name1 The first name + * @param string|string[]|self|null $name2 The second name + * @param array $attributes Attributes to assign to concatenated name + * + * @return static|null Concatenated name + */ + public static function concat($name1, $name2, array $attributes = []) { + if (null === $name1 && null === $name2) { + return null; + } elseif (null === $name1) { + return new static(self::prepareName($name2), $attributes); + } elseif (null === $name2) { + return new static(self::prepareName($name1), $attributes); + } else { + return new static( + array_merge(self::prepareName($name1), self::prepareName($name2)), $attributes + ); + } + } + + /** + * Prepares a (string, array or Name node) name for use in name changing methods by converting + * it to an array. + * + * @param string|string[]|self $name Name to prepare + * + * @return string[] Prepared name + */ + private static function prepareName($name) : array { + if (\is_string($name)) { + if ('' === $name) { + throw new \InvalidArgumentException('Name cannot be empty'); + } + + return explode('\\', $name); + } elseif (\is_array($name)) { + if (empty($name)) { + throw new \InvalidArgumentException('Name cannot be empty'); + } + + return $name; + } elseif ($name instanceof self) { + return $name->parts; + } + + throw new \InvalidArgumentException( + 'Expected string, array of parts or Name instance' + ); + } + + public function getType() : string { + return 'Name'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php new file mode 100644 index 0000000..1df93a5 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php @@ -0,0 +1,50 @@ +toString(); + } + + public function getType() : string { + return 'Name_FullyQualified'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php new file mode 100644 index 0000000..57bf7af --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php @@ -0,0 +1,50 @@ +toString(); + } + + public function getType() : string { + return 'Name_Relative'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php b/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php new file mode 100644 index 0000000..3646365 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->type = \is_string($type) ? new Identifier($type) : $type; + } + + public function getSubNodeNames() : array { + return ['type']; + } + + public function getType() : string { + return 'NullableType'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php new file mode 100644 index 0000000..5bc8a1c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php @@ -0,0 +1,49 @@ +attributes = $attributes; + $this->type = \is_string($type) ? new Identifier($type) : $type; + $this->byRef = $byRef; + $this->variadic = $variadic; + $this->var = $var; + $this->default = $default; + } + + public function getSubNodeNames() : array { + return ['type', 'byRef', 'variadic', 'var', 'default']; + } + + public function getType() : string { + return 'Param'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.php new file mode 100644 index 0000000..8117909 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.php @@ -0,0 +1,7 @@ +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * @internal + * + * Parses a DNUMBER token like PHP would. + * + * @param string $str A string number + * + * @return float The parsed number + */ + public static function parse(string $str) : float { + $str = str_replace('_', '', $str); + + // if string contains any of .eE just cast it to float + if (false !== strpbrk($str, '.eE')) { + return (float) $str; + } + + // otherwise it's an integer notation that overflowed into a float + // if it starts with 0 it's one of the special integer notations + if ('0' === $str[0]) { + // hex + if ('x' === $str[1] || 'X' === $str[1]) { + return hexdec($str); + } + + // bin + if ('b' === $str[1] || 'B' === $str[1]) { + return bindec($str); + } + + // oct + // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9) + // so that only the digits before that are used + return octdec(substr($str, 0, strcspn($str, '89'))); + } + + // dec + return (float) $str; + } + + public function getType() : string { + return 'Scalar_DNumber'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php new file mode 100644 index 0000000..fa5d2e2 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php @@ -0,0 +1,31 @@ +attributes = $attributes; + $this->parts = $parts; + } + + public function getSubNodeNames() : array { + return ['parts']; + } + + public function getType() : string { + return 'Scalar_Encapsed'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php new file mode 100644 index 0000000..bb3194c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + public function getType() : string { + return 'Scalar_EncapsedStringPart'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php new file mode 100644 index 0000000..b339435 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php @@ -0,0 +1,73 @@ +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * Constructs an LNumber node from a string number literal. + * + * @param string $str String number literal (decimal, octal, hex or binary) + * @param array $attributes Additional attributes + * @param bool $allowInvalidOctal Whether to allow invalid octal numbers (PHP 5) + * + * @return LNumber The constructed LNumber, including kind attribute + */ + public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false) : LNumber { + $str = str_replace('_', '', $str); + + if ('0' !== $str[0] || '0' === $str) { + $attributes['kind'] = LNumber::KIND_DEC; + return new LNumber((int) $str, $attributes); + } + + if ('x' === $str[1] || 'X' === $str[1]) { + $attributes['kind'] = LNumber::KIND_HEX; + return new LNumber(hexdec($str), $attributes); + } + + if ('b' === $str[1] || 'B' === $str[1]) { + $attributes['kind'] = LNumber::KIND_BIN; + return new LNumber(bindec($str), $attributes); + } + + if (!$allowInvalidOctal && strpbrk($str, '89')) { + throw new Error('Invalid numeric literal', $attributes); + } + + // use intval instead of octdec to get proper cutting behavior with malformed numbers + $attributes['kind'] = LNumber::KIND_OCT; + return new LNumber(intval($str, 8), $attributes); + } + + public function getType() : string { + return 'Scalar_LNumber'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php new file mode 100644 index 0000000..941f0c7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php @@ -0,0 +1,28 @@ +attributes = $attributes; + } + + public function getSubNodeNames() : array { + return []; + } + + /** + * Get name of magic constant. + * + * @return string Name of magic constant + */ + abstract public function getName() : string; +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php new file mode 100644 index 0000000..2443284 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php @@ -0,0 +1,16 @@ + '\\', + '$' => '$', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ]; + + /** + * Constructs a string scalar node. + * + * @param string $value Value of the string + * @param array $attributes Additional attributes + */ + public function __construct(string $value, array $attributes = []) { + $this->attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + /** + * @internal + * + * Parses a string token. + * + * @param string $str String token content + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + * + * @return string The parsed string + */ + public static function parse(string $str, bool $parseUnicodeEscape = true) : string { + $bLength = 0; + if ('b' === $str[0] || 'B' === $str[0]) { + $bLength = 1; + } + + if ('\'' === $str[$bLength]) { + return str_replace( + ['\\\\', '\\\''], + ['\\', '\''], + substr($str, $bLength + 1, -1) + ); + } else { + return self::parseEscapeSequences( + substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape + ); + } + } + + /** + * @internal + * + * Parses escape sequences in strings (all string types apart from single quoted). + * + * @param string $str String without quotes + * @param null|string $quote Quote type + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + * + * @return string String with escape sequences parsed + */ + public static function parseEscapeSequences(string $str, $quote, bool $parseUnicodeEscape = true) : string { + if (null !== $quote) { + $str = str_replace('\\' . $quote, $quote, $str); + } + + $extra = ''; + if ($parseUnicodeEscape) { + $extra = '|u\{([0-9a-fA-F]+)\}'; + } + + return preg_replace_callback( + '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', + function($matches) { + $str = $matches[1]; + + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return chr(hexdec(substr($str, 1))); + } elseif ('u' === $str[0]) { + return self::codePointToUtf8(hexdec($matches[2])); + } else { + return chr(octdec($str)); + } + }, + $str + ); + } + + /** + * Converts a Unicode code point to its UTF-8 encoded representation. + * + * @param int $num Code point + * + * @return string UTF-8 representation of code point + */ + private static function codePointToUtf8(int $num) : string { + if ($num <= 0x7F) { + return chr($num); + } + if ($num <= 0x7FF) { + return chr(($num>>6) + 0xC0) . chr(($num&0x3F) + 0x80); + } + if ($num <= 0xFFFF) { + return chr(($num>>12) + 0xE0) . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80); + } + if ($num <= 0x1FFFFF) { + return chr(($num>>18) + 0xF0) . chr((($num>>12)&0x3F) + 0x80) + . chr((($num>>6)&0x3F) + 0x80) . chr(($num&0x3F) + 0x80); + } + throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); + } + + public function getType() : string { + return 'Scalar_String'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php new file mode 100644 index 0000000..69d33e5 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php @@ -0,0 +1,9 @@ +attributes = $attributes; + $this->num = $num; + } + + public function getSubNodeNames() : array { + return ['num']; + } + + public function getType() : string { + return 'Stmt_Break'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php new file mode 100644 index 0000000..2bf044c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Case'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php new file mode 100644 index 0000000..7d4a703 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php @@ -0,0 +1,41 @@ +attributes = $attributes; + $this->types = $types; + $this->var = $var; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['types', 'var', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Catch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php new file mode 100644 index 0000000..73a5010 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php @@ -0,0 +1,62 @@ +attributes = $attributes; + $this->flags = $flags; + $this->consts = $consts; + } + + public function getSubNodeNames() : array { + return ['flags', 'consts']; + } + + /** + * Whether constant is explicitly or implicitly public. + * + * @return bool + */ + public function isPublic() : bool { + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 + || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + } + + /** + * Whether constant is protected. + * + * @return bool + */ + public function isProtected() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + + /** + * Whether constant is private. + * + * @return bool + */ + public function isPrivate() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + + public function getType() : string { + return 'Stmt_ClassConst'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php new file mode 100644 index 0000000..b1cd326 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php @@ -0,0 +1,87 @@ +stmts as $stmt) { + if ($stmt instanceof TraitUse) { + $traitUses[] = $stmt; + } + } + return $traitUses; + } + + /** + * @return ClassConst[] + */ + public function getConstants() : array { + $constants = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassConst) { + $constants[] = $stmt; + } + } + return $constants; + } + + /** + * @return Property[] + */ + public function getProperties() : array { + $properties = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof Property) { + $properties[] = $stmt; + } + } + return $properties; + } + + /** + * Gets all methods defined directly in this class/interface/trait + * + * @return ClassMethod[] + */ + public function getMethods() : array { + $methods = []; + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassMethod) { + $methods[] = $stmt; + } + } + return $methods; + } + + /** + * Gets method with the given name defined directly in this class/interface/trait. + * + * @param string $name Name of the method (compared case-insensitively) + * + * @return ClassMethod|null Method node or null if the method does not exist + */ + public function getMethod(string $name) { + $lowerName = strtolower($name); + foreach ($this->stmts as $stmt) { + if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) { + return $stmt; + } + } + return null; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php new file mode 100644 index 0000000..881dcd7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php @@ -0,0 +1,151 @@ + true, + '__destruct' => true, + '__call' => true, + '__callstatic' => true, + '__get' => true, + '__set' => true, + '__isset' => true, + '__unset' => true, + '__sleep' => true, + '__wakeup' => true, + '__tostring' => true, + '__set_state' => true, + '__clone' => true, + '__invoke' => true, + '__debuginfo' => true, + ]; + + /** + * Constructs a class method node. + * + * @param string|Node\Identifier $name Name + * @param array $subNodes Array of the following optional subnodes: + * 'flags => MODIFIER_PUBLIC: Flags + * 'byRef' => false : Whether to return by reference + * 'params' => array() : Parameters + * 'returnType' => null : Return type + * 'stmts' => array() : Statements + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; + $this->byRef = $subNodes['byRef'] ?? false; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; + } + + public function getSubNodeNames() : array { + return ['flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; + } + + public function returnsByRef() : bool { + return $this->byRef; + } + + public function getParams() : array { + return $this->params; + } + + public function getReturnType() { + return $this->returnType; + } + + public function getStmts() { + return $this->stmts; + } + + /** + * Whether the method is explicitly or implicitly public. + * + * @return bool + */ + public function isPublic() : bool { + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 + || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + } + + /** + * Whether the method is protected. + * + * @return bool + */ + public function isProtected() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + + /** + * Whether the method is private. + * + * @return bool + */ + public function isPrivate() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + + /** + * Whether the method is abstract. + * + * @return bool + */ + public function isAbstract() : bool { + return (bool) ($this->flags & Class_::MODIFIER_ABSTRACT); + } + + /** + * Whether the method is final. + * + * @return bool + */ + public function isFinal() : bool { + return (bool) ($this->flags & Class_::MODIFIER_FINAL); + } + + /** + * Whether the method is static. + * + * @return bool + */ + public function isStatic() : bool { + return (bool) ($this->flags & Class_::MODIFIER_STATIC); + } + + /** + * Whether the method is magic. + * + * @return bool + */ + public function isMagic() : bool { + return isset(self::$magicNames[$this->name->toLowerString()]); + } + + public function getType() : string { + return 'Stmt_ClassMethod'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php new file mode 100644 index 0000000..e2a92e9 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php @@ -0,0 +1,105 @@ + 0 : Flags + * 'extends' => null : Name of extended class + * 'implements' => array(): Names of implemented interfaces + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->extends = $subNodes['extends'] ?? null; + $this->implements = $subNodes['implements'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['flags', 'name', 'extends', 'implements', 'stmts']; + } + + /** + * Whether the class is explicitly abstract. + * + * @return bool + */ + public function isAbstract() : bool { + return (bool) ($this->flags & self::MODIFIER_ABSTRACT); + } + + /** + * Whether the class is final. + * + * @return bool + */ + public function isFinal() : bool { + return (bool) ($this->flags & self::MODIFIER_FINAL); + } + + /** + * Whether the class is anonymous. + * + * @return bool + */ + public function isAnonymous() : bool { + return null === $this->name; + } + + /** + * @internal + */ + public static function verifyModifier($a, $b) { + if ($a & self::VISIBILITY_MODIFIER_MASK && $b & self::VISIBILITY_MODIFIER_MASK) { + throw new Error('Multiple access type modifiers are not allowed'); + } + + if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) { + throw new Error('Multiple abstract modifiers are not allowed'); + } + + if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) { + throw new Error('Multiple static modifiers are not allowed'); + } + + if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) { + throw new Error('Multiple final modifiers are not allowed'); + } + + if ($a & 48 && $b & 48) { + throw new Error('Cannot use the final modifier on an abstract class member'); + } + } + + public function getType() : string { + return 'Stmt_Class'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php new file mode 100644 index 0000000..e631634 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->consts = $consts; + } + + public function getSubNodeNames() : array { + return ['consts']; + } + + public function getType() : string { + return 'Stmt_Const'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php new file mode 100644 index 0000000..2488268 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->num = $num; + } + + public function getSubNodeNames() : array { + return ['num']; + } + + public function getType() : string { + return 'Stmt_Continue'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php new file mode 100644 index 0000000..ac07f30 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.php @@ -0,0 +1,34 @@ +value pair node. + * + * @param string|Node\Identifier $key Key + * @param Node\Expr $value Value + * @param array $attributes Additional attributes + */ + public function __construct($key, Node\Expr $value, array $attributes = []) { + $this->attributes = $attributes; + $this->key = \is_string($key) ? new Node\Identifier($key) : $key; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['key', 'value']; + } + + public function getType() : string { + return 'Stmt_DeclareDeclare'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php new file mode 100644 index 0000000..f46ff0b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->declares = $declares; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['declares', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Declare'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php new file mode 100644 index 0000000..78e90da --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts', 'cond']; + } + + public function getType() : string { + return 'Stmt_Do'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php new file mode 100644 index 0000000..7cc50d5 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->exprs = $exprs; + } + + public function getSubNodeNames() : array { + return ['exprs']; + } + + public function getType() : string { + return 'Stmt_Echo'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php new file mode 100644 index 0000000..eef1ece --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts']; + } + + public function getType() : string { + return 'Stmt_ElseIf'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php new file mode 100644 index 0000000..0e61778 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts']; + } + + public function getType() : string { + return 'Stmt_Else'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php new file mode 100644 index 0000000..99d1687 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php @@ -0,0 +1,33 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Stmt_Expression'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php new file mode 100644 index 0000000..d55b8b6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['stmts']; + } + + public function getType() : string { + return 'Stmt_Finally'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php new file mode 100644 index 0000000..1323d37 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php @@ -0,0 +1,43 @@ + array(): Init expressions + * 'cond' => array(): Loop conditions + * 'loop' => array(): Loop expressions + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct(array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->init = $subNodes['init'] ?? []; + $this->cond = $subNodes['cond'] ?? []; + $this->loop = $subNodes['loop'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['init', 'cond', 'loop', 'stmts']; + } + + public function getType() : string { + return 'Stmt_For'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php new file mode 100644 index 0000000..0556a7c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php @@ -0,0 +1,47 @@ + null : Variable to assign key to + * 'byRef' => false : Whether to assign value by reference + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct(Node\Expr $expr, Node\Expr $valueVar, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->expr = $expr; + $this->keyVar = $subNodes['keyVar'] ?? null; + $this->byRef = $subNodes['byRef'] ?? false; + $this->valueVar = $valueVar; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Foreach'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php new file mode 100644 index 0000000..d11a4df --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php @@ -0,0 +1,69 @@ + false : Whether to return by reference + * 'params' => array(): Parameters + * 'returnType' => null : Return type + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->byRef = $subNodes['byRef'] ?? false; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->params = $subNodes['params'] ?? []; + $returnType = $subNodes['returnType'] ?? null; + $this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['byRef', 'name', 'params', 'returnType', 'stmts']; + } + + public function returnsByRef() : bool { + return $this->byRef; + } + + public function getParams() : array { + return $this->params; + } + + public function getReturnType() { + return $this->returnType; + } + + /** @return Node\Stmt[] */ + public function getStmts() : array { + return $this->stmts; + } + + public function getType() : string { + return 'Stmt_Function'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php new file mode 100644 index 0000000..a0022ad --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Stmt_Global'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php new file mode 100644 index 0000000..24a57f7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php @@ -0,0 +1,31 @@ +attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Stmt_Goto'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php new file mode 100644 index 0000000..24520d2 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php @@ -0,0 +1,39 @@ +attributes = $attributes; + $this->type = $type; + $this->prefix = $prefix; + $this->uses = $uses; + } + + public function getSubNodeNames() : array { + return ['type', 'prefix', 'uses']; + } + + public function getType() : string { + return 'Stmt_GroupUse'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php new file mode 100644 index 0000000..8e624e0 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->remaining = $remaining; + } + + public function getSubNodeNames() : array { + return ['remaining']; + } + + public function getType() : string { + return 'Stmt_HaltCompiler'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php new file mode 100644 index 0000000..a1bae4b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php @@ -0,0 +1,43 @@ + array(): Statements + * 'elseifs' => array(): Elseif clauses + * 'else' => null : Else clause + * @param array $attributes Additional attributes + */ + public function __construct(Node\Expr $cond, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->cond = $cond; + $this->stmts = $subNodes['stmts'] ?? []; + $this->elseifs = $subNodes['elseifs'] ?? []; + $this->else = $subNodes['else'] ?? null; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts', 'elseifs', 'else']; + } + + public function getType() : string { + return 'Stmt_If'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php new file mode 100644 index 0000000..0711d28 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->value = $value; + } + + public function getSubNodeNames() : array { + return ['value']; + } + + public function getType() : string { + return 'Stmt_InlineHTML'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php new file mode 100644 index 0000000..cf5d62a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php @@ -0,0 +1,35 @@ + array(): Name of extended interfaces + * 'stmts' => array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->extends = $subNodes['extends'] ?? []; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['name', 'extends', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Interface'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php new file mode 100644 index 0000000..3edcb3b --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php @@ -0,0 +1,31 @@ +attributes = $attributes; + $this->name = \is_string($name) ? new Identifier($name) : $name; + } + + public function getSubNodeNames() : array { + return ['name']; + } + + public function getType() : string { + return 'Stmt_Label'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php new file mode 100644 index 0000000..c632045 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php @@ -0,0 +1,38 @@ +attributes = $attributes; + $this->name = $name; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['name', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Namespace'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php new file mode 100644 index 0000000..f86f8df --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php @@ -0,0 +1,17 @@ +attributes = $attributes; + $this->flags = $flags; + $this->props = $props; + $this->type = \is_string($type) ? new Identifier($type) : $type; + } + + public function getSubNodeNames() : array { + return ['flags', 'type', 'props']; + } + + /** + * Whether the property is explicitly or implicitly public. + * + * @return bool + */ + public function isPublic() : bool { + return ($this->flags & Class_::MODIFIER_PUBLIC) !== 0 + || ($this->flags & Class_::VISIBILITY_MODIFIER_MASK) === 0; + } + + /** + * Whether the property is protected. + * + * @return bool + */ + public function isProtected() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PROTECTED); + } + + /** + * Whether the property is private. + * + * @return bool + */ + public function isPrivate() : bool { + return (bool) ($this->flags & Class_::MODIFIER_PRIVATE); + } + + /** + * Whether the property is static. + * + * @return bool + */ + public function isStatic() : bool { + return (bool) ($this->flags & Class_::MODIFIER_STATIC); + } + + public function getType() : string { + return 'Stmt_Property'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php new file mode 100644 index 0000000..205731e --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; + $this->default = $default; + } + + public function getSubNodeNames() : array { + return ['name', 'default']; + } + + public function getType() : string { + return 'Stmt_PropertyProperty'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php new file mode 100644 index 0000000..efc578c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Stmt_Return'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php new file mode 100644 index 0000000..2958456 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.php @@ -0,0 +1,37 @@ +attributes = $attributes; + $this->var = $var; + $this->default = $default; + } + + public function getSubNodeNames() : array { + return ['var', 'default']; + } + + public function getType() : string { + return 'Stmt_StaticVar'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php new file mode 100644 index 0000000..464898f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Stmt_Static'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php new file mode 100644 index 0000000..2c8dae0 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->cond = $cond; + $this->cases = $cases; + } + + public function getSubNodeNames() : array { + return ['cond', 'cases']; + } + + public function getType() : string { + return 'Stmt_Switch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Throw_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Throw_.php new file mode 100644 index 0000000..f97f567 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Throw_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->expr = $expr; + } + + public function getSubNodeNames() : array { + return ['expr']; + } + + public function getType() : string { + return 'Stmt_Throw'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php new file mode 100644 index 0000000..9e97053 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->traits = $traits; + $this->adaptations = $adaptations; + } + + public function getSubNodeNames() : array { + return ['traits', 'adaptations']; + } + + public function getType() : string { + return 'Stmt_TraitUse'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php new file mode 100644 index 0000000..8bdd2c0 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php @@ -0,0 +1,13 @@ +attributes = $attributes; + $this->trait = $trait; + $this->method = \is_string($method) ? new Node\Identifier($method) : $method; + $this->newModifier = $newModifier; + $this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName; + } + + public function getSubNodeNames() : array { + return ['trait', 'method', 'newModifier', 'newName']; + } + + public function getType() : string { + return 'Stmt_TraitUseAdaptation_Alias'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php new file mode 100644 index 0000000..80385f6 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->trait = $trait; + $this->method = \is_string($method) ? new Node\Identifier($method) : $method; + $this->insteadof = $insteadof; + } + + public function getSubNodeNames() : array { + return ['trait', 'method', 'insteadof']; + } + + public function getType() : string { + return 'Stmt_TraitUseAdaptation_Precedence'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php new file mode 100644 index 0000000..c2e8cca --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php @@ -0,0 +1,30 @@ + array(): Statements + * @param array $attributes Additional attributes + */ + public function __construct($name, array $subNodes = [], array $attributes = []) { + $this->attributes = $attributes; + $this->name = \is_string($name) ? new Node\Identifier($name) : $name; + $this->stmts = $subNodes['stmts'] ?? []; + } + + public function getSubNodeNames() : array { + return ['name', 'stmts']; + } + + public function getType() : string { + return 'Stmt_Trait'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php new file mode 100644 index 0000000..7fc158c --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php @@ -0,0 +1,38 @@ +attributes = $attributes; + $this->stmts = $stmts; + $this->catches = $catches; + $this->finally = $finally; + } + + public function getSubNodeNames() : array { + return ['stmts', 'catches', 'finally']; + } + + public function getType() : string { + return 'Stmt_TryCatch'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php new file mode 100644 index 0000000..310e427 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->vars = $vars; + } + + public function getSubNodeNames() : array { + return ['vars']; + } + + public function getType() : string { + return 'Stmt_Unset'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php new file mode 100644 index 0000000..32bd784 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php @@ -0,0 +1,52 @@ +attributes = $attributes; + $this->type = $type; + $this->name = $name; + $this->alias = \is_string($alias) ? new Identifier($alias) : $alias; + } + + public function getSubNodeNames() : array { + return ['type', 'name', 'alias']; + } + + /** + * Get alias. If not explicitly given this is the last component of the used name. + * + * @return Identifier + */ + public function getAlias() : Identifier { + if (null !== $this->alias) { + return $this->alias; + } + + return new Identifier($this->name->getLast()); + } + + public function getType() : string { + return 'Stmt_UseUse'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php new file mode 100644 index 0000000..8753da3 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php @@ -0,0 +1,47 @@ +attributes = $attributes; + $this->type = $type; + $this->uses = $uses; + } + + public function getSubNodeNames() : array { + return ['type', 'uses']; + } + + public function getType() : string { + return 'Stmt_Use'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php new file mode 100644 index 0000000..f41034f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php @@ -0,0 +1,34 @@ +attributes = $attributes; + $this->cond = $cond; + $this->stmts = $stmts; + } + + public function getSubNodeNames() : array { + return ['cond', 'stmts']; + } + + public function getType() : string { + return 'Stmt_While'; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php b/vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php new file mode 100644 index 0000000..a30807a --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php @@ -0,0 +1,17 @@ +attributes = $attributes; + } + + /** + * Gets line the node started in (alias of getStartLine). + * + * @return int Start line (or -1 if not available) + */ + public function getLine() : int { + return $this->attributes['startLine'] ?? -1; + } + + /** + * Gets line the node started in. + * + * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). + * + * @return int Start line (or -1 if not available) + */ + public function getStartLine() : int { + return $this->attributes['startLine'] ?? -1; + } + + /** + * Gets the line the node ended in. + * + * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). + * + * @return int End line (or -1 if not available) + */ + public function getEndLine() : int { + return $this->attributes['endLine'] ?? -1; + } + + /** + * Gets the token offset of the first token that is part of this node. + * + * The offset is an index into the array returned by Lexer::getTokens(). + * + * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token start position (or -1 if not available) + */ + public function getStartTokenPos() : int { + return $this->attributes['startTokenPos'] ?? -1; + } + + /** + * Gets the token offset of the last token that is part of this node. + * + * The offset is an index into the array returned by Lexer::getTokens(). + * + * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int Token end position (or -1 if not available) + */ + public function getEndTokenPos() : int { + return $this->attributes['endTokenPos'] ?? -1; + } + + /** + * Gets the file offset of the first character that is part of this node. + * + * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int File start position (or -1 if not available) + */ + public function getStartFilePos() : int { + return $this->attributes['startFilePos'] ?? -1; + } + + /** + * Gets the file offset of the last character that is part of this node. + * + * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). + * + * @return int File end position (or -1 if not available) + */ + public function getEndFilePos() : int { + return $this->attributes['endFilePos'] ?? -1; + } + + /** + * Gets all comments directly preceding this node. + * + * The comments are also available through the "comments" attribute. + * + * @return Comment[] + */ + public function getComments() : array { + return $this->attributes['comments'] ?? []; + } + + /** + * Gets the doc comment of the node. + * + * The doc comment has to be the last comment associated with the node. + * + * @return null|Comment\Doc Doc comment object or null + */ + public function getDocComment() { + $comments = $this->getComments(); + if (!$comments) { + return null; + } + + $lastComment = $comments[count($comments) - 1]; + if (!$lastComment instanceof Comment\Doc) { + return null; + } + + return $lastComment; + } + + /** + * Sets the doc comment of the node. + * + * This will either replace an existing doc comment or add it to the comments array. + * + * @param Comment\Doc $docComment Doc comment to set + */ + public function setDocComment(Comment\Doc $docComment) { + $comments = $this->getComments(); + + $numComments = count($comments); + if ($numComments > 0 && $comments[$numComments - 1] instanceof Comment\Doc) { + // Replace existing doc comment + $comments[$numComments - 1] = $docComment; + } else { + // Append new comment + $comments[] = $docComment; + } + + $this->setAttribute('comments', $comments); + } + + public function setAttribute(string $key, $value) { + $this->attributes[$key] = $value; + } + + public function hasAttribute(string $key) : bool { + return array_key_exists($key, $this->attributes); + } + + public function getAttribute(string $key, $default = null) { + if (array_key_exists($key, $this->attributes)) { + return $this->attributes[$key]; + } + + return $default; + } + + public function getAttributes() : array { + return $this->attributes; + } + + public function setAttributes(array $attributes) { + $this->attributes = $attributes; + } + + /** + * @return array + */ + public function jsonSerialize() : array { + return ['nodeType' => $this->getType()] + get_object_vars($this); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php b/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php new file mode 100644 index 0000000..197ebc1 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php @@ -0,0 +1,203 @@ +dumpComments = !empty($options['dumpComments']); + $this->dumpPositions = !empty($options['dumpPositions']); + } + + /** + * Dumps a node or array. + * + * @param array|Node $node Node or array to dump + * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if + * the dumpPositions option is enabled and the dumping of node offsets + * is desired. + * + * @return string Dumped value + */ + public function dump($node, string $code = null) : string { + $this->code = $code; + return $this->dumpRecursive($node); + } + + protected function dumpRecursive($node) { + if ($node instanceof Node) { + $r = $node->getType(); + if ($this->dumpPositions && null !== $p = $this->dumpPosition($node)) { + $r .= $p; + } + $r .= '('; + + foreach ($node->getSubNodeNames() as $key) { + $r .= "\n " . $key . ': '; + + $value = $node->$key; + if (null === $value) { + $r .= 'null'; + } elseif (false === $value) { + $r .= 'false'; + } elseif (true === $value) { + $r .= 'true'; + } elseif (is_scalar($value)) { + if ('flags' === $key || 'newModifier' === $key) { + $r .= $this->dumpFlags($value); + } elseif ('type' === $key && $node instanceof Include_) { + $r .= $this->dumpIncludeType($value); + } elseif ('type' === $key + && ($node instanceof Use_ || $node instanceof UseUse || $node instanceof GroupUse)) { + $r .= $this->dumpUseType($value); + } else { + $r .= $value; + } + } else { + $r .= str_replace("\n", "\n ", $this->dumpRecursive($value)); + } + } + + if ($this->dumpComments && $comments = $node->getComments()) { + $r .= "\n comments: " . str_replace("\n", "\n ", $this->dumpRecursive($comments)); + } + } elseif (is_array($node)) { + $r = 'array('; + + foreach ($node as $key => $value) { + $r .= "\n " . $key . ': '; + + if (null === $value) { + $r .= 'null'; + } elseif (false === $value) { + $r .= 'false'; + } elseif (true === $value) { + $r .= 'true'; + } elseif (is_scalar($value)) { + $r .= $value; + } else { + $r .= str_replace("\n", "\n ", $this->dumpRecursive($value)); + } + } + } elseif ($node instanceof Comment) { + return $node->getReformattedText(); + } else { + throw new \InvalidArgumentException('Can only dump nodes and arrays.'); + } + + return $r . "\n)"; + } + + protected function dumpFlags($flags) { + $strs = []; + if ($flags & Class_::MODIFIER_PUBLIC) { + $strs[] = 'MODIFIER_PUBLIC'; + } + if ($flags & Class_::MODIFIER_PROTECTED) { + $strs[] = 'MODIFIER_PROTECTED'; + } + if ($flags & Class_::MODIFIER_PRIVATE) { + $strs[] = 'MODIFIER_PRIVATE'; + } + if ($flags & Class_::MODIFIER_ABSTRACT) { + $strs[] = 'MODIFIER_ABSTRACT'; + } + if ($flags & Class_::MODIFIER_STATIC) { + $strs[] = 'MODIFIER_STATIC'; + } + if ($flags & Class_::MODIFIER_FINAL) { + $strs[] = 'MODIFIER_FINAL'; + } + + if ($strs) { + return implode(' | ', $strs) . ' (' . $flags . ')'; + } else { + return $flags; + } + } + + protected function dumpIncludeType($type) { + $map = [ + Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', + Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', + Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', + Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE', + ]; + + if (!isset($map[$type])) { + return $type; + } + return $map[$type] . ' (' . $type . ')'; + } + + protected function dumpUseType($type) { + $map = [ + Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', + Use_::TYPE_NORMAL => 'TYPE_NORMAL', + Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', + Use_::TYPE_CONSTANT => 'TYPE_CONSTANT', + ]; + + if (!isset($map[$type])) { + return $type; + } + return $map[$type] . ' (' . $type . ')'; + } + + /** + * Dump node position, if possible. + * + * @param Node $node Node for which to dump position + * + * @return string|null Dump of position, or null if position information not available + */ + protected function dumpPosition(Node $node) { + if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { + return null; + } + + $start = $node->getStartLine(); + $end = $node->getEndLine(); + if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos') + && null !== $this->code + ) { + $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos()); + $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos()); + } + return "[$start - $end]"; + } + + // Copied from Error class + private function toColumn($code, $pos) { + if ($pos > strlen($code)) { + throw new \RuntimeException('Invalid position information'); + } + + $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); + if (false === $lineStartPos) { + $lineStartPos = -1; + } + + return $pos - $lineStartPos; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php b/vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php new file mode 100644 index 0000000..2e7cfda --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php @@ -0,0 +1,81 @@ +addVisitor($visitor); + $traverser->traverse($nodes); + + return $visitor->getFoundNodes(); + } + + /** + * Find all nodes that are instances of a certain class. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param string $class Class name + * + * @return Node[] Found nodes (all instances of $class) + */ + public function findInstanceOf($nodes, string $class) : array { + return $this->find($nodes, function ($node) use ($class) { + return $node instanceof $class; + }); + } + + /** + * Find first node satisfying a filter callback. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param callable $filter Filter callback: function(Node $node) : bool + * + * @return null|Node Found node (or null if none found) + */ + public function findFirst($nodes, callable $filter) { + if (!is_array($nodes)) { + $nodes = [$nodes]; + } + + $visitor = new FirstFindingVisitor($filter); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $traverser->traverse($nodes); + + return $visitor->getFoundNode(); + } + + /** + * Find first node that is an instance of a certain class. + * + * @param Node|Node[] $nodes Single node or array of nodes to search in + * @param string $class Class name + * + * @return null|Node Found node, which is an instance of $class (or null if none found) + */ + public function findFirstInstanceOf($nodes, string $class) { + return $this->findFirst($nodes, function ($node) use ($class) { + return $node instanceof $class; + }); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php new file mode 100644 index 0000000..97d45bd --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php @@ -0,0 +1,291 @@ +visitors[] = $visitor; + } + + /** + * Removes an added visitor. + * + * @param NodeVisitor $visitor + */ + public function removeVisitor(NodeVisitor $visitor) { + foreach ($this->visitors as $index => $storedVisitor) { + if ($storedVisitor === $visitor) { + unset($this->visitors[$index]); + break; + } + } + } + + /** + * Traverses an array of nodes using the registered visitors. + * + * @param Node[] $nodes Array of nodes + * + * @return Node[] Traversed array of nodes + */ + public function traverse(array $nodes) : array { + $this->stopTraversal = false; + + foreach ($this->visitors as $visitor) { + if (null !== $return = $visitor->beforeTraverse($nodes)) { + $nodes = $return; + } + } + + $nodes = $this->traverseArray($nodes); + + foreach ($this->visitors as $visitor) { + if (null !== $return = $visitor->afterTraverse($nodes)) { + $nodes = $return; + } + } + + return $nodes; + } + + /** + * Recursively traverse a node. + * + * @param Node $node Node to traverse. + * + * @return Node Result of traversal (may be original node or new one) + */ + protected function traverseNode(Node $node) : Node { + foreach ($node->getSubNodeNames() as $name) { + $subNode =& $node->$name; + + if (\is_array($subNode)) { + $subNode = $this->traverseArray($subNode); + if ($this->stopTraversal) { + break; + } + } elseif ($subNode instanceof Node) { + $traverseChildren = true; + $breakVisitorIndex = null; + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($subNode); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $traverseChildren = false; + } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = true; + break 2; + } else { + throw new \LogicException( + 'enterNode() returned invalid value of type ' . gettype($return) + ); + } + } + } + + if ($traverseChildren) { + $subNode = $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($subNode); + + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = true; + break 2; + } elseif (\is_array($return)) { + throw new \LogicException( + 'leaveNode() may only return an array ' . + 'if the parent structure is an array' + ); + } else { + throw new \LogicException( + 'leaveNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + } + + return $node; + } + + /** + * Recursively traverse array (usually of nodes). + * + * @param array $nodes Array to traverse + * + * @return array Result of traversal (may be original array or changed one) + */ + protected function traverseArray(array $nodes) : array { + $doNodes = []; + + foreach ($nodes as $i => &$node) { + if ($node instanceof Node) { + $traverseChildren = true; + $breakVisitorIndex = null; + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($node); + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (self::DONT_TRAVERSE_CHILDREN === $return) { + $traverseChildren = false; + } elseif (self::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = true; + break 2; + } else { + throw new \LogicException( + 'enterNode() returned invalid value of type ' . gettype($return) + ); + } + } + } + + if ($traverseChildren) { + $node = $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($node); + + if (null !== $return) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (\is_array($return)) { + $doNodes[] = [$i, $return]; + break; + } elseif (self::REMOVE_NODE === $return) { + $doNodes[] = [$i, []]; + break; + } elseif (self::STOP_TRAVERSAL === $return) { + $this->stopTraversal = true; + break 2; + } elseif (false === $return) { + throw new \LogicException( + 'bool(false) return from leaveNode() no longer supported. ' . + 'Return NodeTraverser::REMOVE_NODE instead' + ); + } else { + throw new \LogicException( + 'leaveNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } elseif (\is_array($node)) { + throw new \LogicException('Invalid node structure: Contains nested arrays'); + } + } + + if (!empty($doNodes)) { + while (list($i, $replace) = array_pop($doNodes)) { + array_splice($nodes, $i, 1, $replace); + } + } + + return $nodes; + } + + private function ensureReplacementReasonable($old, $new) { + if ($old instanceof Node\Stmt && $new instanceof Node\Expr) { + throw new \LogicException( + "Trying to replace statement ({$old->getType()}) " . + "with expression ({$new->getType()}). Are you missing a " . + "Stmt_Expression wrapper?" + ); + } + + if ($old instanceof Node\Expr && $new instanceof Node\Stmt) { + throw new \LogicException( + "Trying to replace expression ({$old->getType()}) " . + "with statement ({$new->getType()})" + ); + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php new file mode 100644 index 0000000..77ff3d2 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php @@ -0,0 +1,29 @@ + $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return null|int|Node Replacement node (or special return value) + */ + public function enterNode(Node $node); + + /** + * Called when leaving a node. + * + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return null|int|Node|Node[] Replacement node (or special return value) + */ + public function leaveNode(Node $node); + + /** + * Called once after traversal. + * + * Return value semantics: + * * null: $nodes stays as-is + * * otherwise: $nodes is set to the return value + * + * @param Node[] $nodes Array of nodes + * + * @return null|Node[] Array of nodes + */ + public function afterTraverse(array $nodes); +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php new file mode 100644 index 0000000..a85fa49 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php @@ -0,0 +1,20 @@ +setAttribute('origNode', $origNode); + return $node; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php new file mode 100644 index 0000000..9531edb --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php @@ -0,0 +1,48 @@ +filterCallback = $filterCallback; + } + + /** + * Get found nodes satisfying the filter callback. + * + * Nodes are returned in pre-order. + * + * @return Node[] Found nodes + */ + public function getFoundNodes() : array { + return $this->foundNodes; + } + + public function beforeTraverse(array $nodes) { + $this->foundNodes = []; + + return null; + } + + public function enterNode(Node $node) { + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNodes[] = $node; + } + + return null; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php new file mode 100644 index 0000000..596a7d7 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php @@ -0,0 +1,50 @@ +filterCallback = $filterCallback; + } + + /** + * Get found node satisfying the filter callback. + * + * Returns null if no node satisfies the filter callback. + * + * @return null|Node Found node (or null if not found) + */ + public function getFoundNode() { + return $this->foundNode; + } + + public function beforeTraverse(array $nodes) { + $this->foundNode = null; + + return null; + } + + public function enterNode(Node $node) { + $filterCallback = $this->filterCallback; + if ($filterCallback($node)) { + $this->foundNode = $node; + return NodeTraverser::STOP_TRAVERSAL; + } + + return null; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php new file mode 100644 index 0000000..2d466d1 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php @@ -0,0 +1,222 @@ +nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing); + $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; + $this->replaceNodes = $options['replaceNodes'] ?? true; + } + + /** + * Get name resolution context. + * + * @return NameContext + */ + public function getNameContext() : NameContext { + return $this->nameContext; + } + + public function beforeTraverse(array $nodes) { + $this->nameContext->startNamespace(); + return null; + } + + public function enterNode(Node $node) { + if ($node instanceof Stmt\Namespace_) { + $this->nameContext->startNamespace($node->name); + } elseif ($node instanceof Stmt\Use_) { + foreach ($node->uses as $use) { + $this->addAlias($use, $node->type, null); + } + } elseif ($node instanceof Stmt\GroupUse) { + foreach ($node->uses as $use) { + $this->addAlias($use, $node->type, $node->prefix); + } + } elseif ($node instanceof Stmt\Class_) { + if (null !== $node->extends) { + $node->extends = $this->resolveClassName($node->extends); + } + + foreach ($node->implements as &$interface) { + $interface = $this->resolveClassName($interface); + } + + if (null !== $node->name) { + $this->addNamespacedName($node); + } + } elseif ($node instanceof Stmt\Interface_) { + foreach ($node->extends as &$interface) { + $interface = $this->resolveClassName($interface); + } + + $this->addNamespacedName($node); + } elseif ($node instanceof Stmt\Trait_) { + $this->addNamespacedName($node); + } elseif ($node instanceof Stmt\Function_) { + $this->addNamespacedName($node); + $this->resolveSignature($node); + } elseif ($node instanceof Stmt\ClassMethod + || $node instanceof Expr\Closure + || $node instanceof Expr\ArrowFunction + ) { + $this->resolveSignature($node); + } elseif ($node instanceof Stmt\Property) { + if (null !== $node->type) { + $node->type = $this->resolveType($node->type); + } + } elseif ($node instanceof Stmt\Const_) { + foreach ($node->consts as $const) { + $this->addNamespacedName($const); + } + } elseif ($node instanceof Expr\StaticCall + || $node instanceof Expr\StaticPropertyFetch + || $node instanceof Expr\ClassConstFetch + || $node instanceof Expr\New_ + || $node instanceof Expr\Instanceof_ + ) { + if ($node->class instanceof Name) { + $node->class = $this->resolveClassName($node->class); + } + } elseif ($node instanceof Stmt\Catch_) { + foreach ($node->types as &$type) { + $type = $this->resolveClassName($type); + } + } elseif ($node instanceof Expr\FuncCall) { + if ($node->name instanceof Name) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); + } + } elseif ($node instanceof Expr\ConstFetch) { + $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); + } elseif ($node instanceof Stmt\TraitUse) { + foreach ($node->traits as &$trait) { + $trait = $this->resolveClassName($trait); + } + + foreach ($node->adaptations as $adaptation) { + if (null !== $adaptation->trait) { + $adaptation->trait = $this->resolveClassName($adaptation->trait); + } + + if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { + foreach ($adaptation->insteadof as &$insteadof) { + $insteadof = $this->resolveClassName($insteadof); + } + } + } + } + + return null; + } + + private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null) { + // Add prefix for group uses + $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; + // Type is determined either by individual element or whole use declaration + $type |= $use->type; + + $this->nameContext->addAlias( + $name, (string) $use->getAlias(), $type, $use->getAttributes() + ); + } + + /** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */ + private function resolveSignature($node) { + foreach ($node->params as $param) { + $param->type = $this->resolveType($param->type); + } + $node->returnType = $this->resolveType($node->returnType); + } + + private function resolveType($node) { + if ($node instanceof Node\NullableType) { + $node->type = $this->resolveType($node->type); + return $node; + } + if ($node instanceof Name) { + return $this->resolveClassName($node); + } + return $node; + } + + /** + * Resolve name, according to name resolver options. + * + * @param Name $name Function or constant name to resolve + * @param int $type One of Stmt\Use_::TYPE_* + * + * @return Name Resolved name, or original name with attribute + */ + protected function resolveName(Name $name, int $type) : Name { + if (!$this->replaceNodes) { + $resolvedName = $this->nameContext->getResolvedName($name, $type); + if (null !== $resolvedName) { + $name->setAttribute('resolvedName', $resolvedName); + } else { + $name->setAttribute('namespacedName', FullyQualified::concat( + $this->nameContext->getNamespace(), $name, $name->getAttributes())); + } + return $name; + } + + if ($this->preserveOriginalNames) { + // Save the original name + $originalName = $name; + $name = clone $originalName; + $name->setAttribute('originalName', $originalName); + } + + $resolvedName = $this->nameContext->getResolvedName($name, $type); + if (null !== $resolvedName) { + return $resolvedName; + } + + // unqualified names inside a namespace cannot be resolved at compile-time + // add the namespaced version of the name as an attribute + $name->setAttribute('namespacedName', FullyQualified::concat( + $this->nameContext->getNamespace(), $name, $name->getAttributes())); + return $name; + } + + protected function resolveClassName(Name $name) { + return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); + } + + protected function addNamespacedName(Node $node) { + $node->namespacedName = Name::concat( + $this->nameContext->getNamespace(), (string) $node->name); + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php new file mode 100644 index 0000000..d378d67 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php @@ -0,0 +1,25 @@ +parsers = $parsers; + } + + public function parse(string $code, ErrorHandler $errorHandler = null) { + if (null === $errorHandler) { + $errorHandler = new ErrorHandler\Throwing; + } + + list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code); + if ($firstError === null) { + return $firstStmts; + } + + for ($i = 1, $c = count($this->parsers); $i < $c; ++$i) { + list($stmts, $error) = $this->tryParse($this->parsers[$i], $errorHandler, $code); + if ($error === null) { + return $stmts; + } + } + + throw $firstError; + } + + private function tryParse(Parser $parser, ErrorHandler $errorHandler, $code) { + $stmts = null; + $error = null; + try { + $stmts = $parser->parse($code, $errorHandler); + } catch (Error $error) {} + return [$stmts, $error]; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Parser/Php5.php b/vendor/nikic/php-parser/lib/PhpParser/Parser/Php5.php new file mode 100644 index 0000000..a558d28 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Parser/Php5.php @@ -0,0 +1,2640 @@ +'", + "T_IS_GREATER_OR_EQUAL", + "T_SL", + "T_SR", + "'+'", + "'-'", + "'.'", + "'*'", + "'/'", + "'%'", + "'!'", + "T_INSTANCEOF", + "'~'", + "T_INC", + "T_DEC", + "T_INT_CAST", + "T_DOUBLE_CAST", + "T_STRING_CAST", + "T_ARRAY_CAST", + "T_OBJECT_CAST", + "T_BOOL_CAST", + "T_UNSET_CAST", + "'@'", + "T_POW", + "'['", + "T_NEW", + "T_CLONE", + "T_EXIT", + "T_IF", + "T_ELSEIF", + "T_ELSE", + "T_ENDIF", + "T_LNUMBER", + "T_DNUMBER", + "T_STRING", + "T_STRING_VARNAME", + "T_VARIABLE", + "T_NUM_STRING", + "T_INLINE_HTML", + "T_ENCAPSED_AND_WHITESPACE", + "T_CONSTANT_ENCAPSED_STRING", + "T_ECHO", + "T_DO", + "T_WHILE", + "T_ENDWHILE", + "T_FOR", + "T_ENDFOR", + "T_FOREACH", + "T_ENDFOREACH", + "T_DECLARE", + "T_ENDDECLARE", + "T_AS", + "T_SWITCH", + "T_ENDSWITCH", + "T_CASE", + "T_DEFAULT", + "T_BREAK", + "T_CONTINUE", + "T_GOTO", + "T_FUNCTION", + "T_FN", + "T_CONST", + "T_RETURN", + "T_TRY", + "T_CATCH", + "T_FINALLY", + "T_THROW", + "T_USE", + "T_INSTEADOF", + "T_GLOBAL", + "T_STATIC", + "T_ABSTRACT", + "T_FINAL", + "T_PRIVATE", + "T_PROTECTED", + "T_PUBLIC", + "T_VAR", + "T_UNSET", + "T_ISSET", + "T_EMPTY", + "T_HALT_COMPILER", + "T_CLASS", + "T_TRAIT", + "T_INTERFACE", + "T_EXTENDS", + "T_IMPLEMENTS", + "T_OBJECT_OPERATOR", + "T_LIST", + "T_ARRAY", + "T_CALLABLE", + "T_CLASS_C", + "T_TRAIT_C", + "T_METHOD_C", + "T_FUNC_C", + "T_LINE", + "T_FILE", + "T_START_HEREDOC", + "T_END_HEREDOC", + "T_DOLLAR_OPEN_CURLY_BRACES", + "T_CURLY_OPEN", + "T_PAAMAYIM_NEKUDOTAYIM", + "T_NAMESPACE", + "T_NS_C", + "T_DIR", + "T_NS_SEPARATOR", + "T_ELLIPSIS", + "';'", + "'{'", + "'}'", + "'('", + "')'", + "'$'", + "'`'", + "']'", + "'\"'" + ); + + protected $tokenToSymbol = array( + 0, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 54, 158, 159, 155, 53, 36, 159, + 153, 154, 51, 48, 7, 49, 50, 52, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 30, 150, + 42, 15, 44, 29, 66, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 68, 159, 157, 35, 159, 156, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 151, 34, 152, 56, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 1, 2, 3, 4, + 5, 6, 8, 9, 10, 11, 12, 13, 14, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 31, 32, 33, 37, 38, 39, 40, 41, + 43, 45, 46, 47, 55, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 67, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 159, + 159, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 159, 159, + 159, 159, 159, 159, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149 + ); + + protected $action = array( + 681, 682, 683, 684, 685, 283, 686, 687, 688, 724, + 725, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 0, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242,-32766,-32766,-32766,-32766,-32766,-32766,-32766, + -32766,-32767,-32767,-32767,-32767, 1052, 243, 244,-32766,-32766, + -32766,-32766,-32766, 689,-32766,-32766,-32766,-32766,-32766,-32766, + -32766,-32766,-32767,-32767,-32767,-32767,-32767, 690, 691, 692, + 693, 694, 695, 696, 834, 27, 757, 959, 960, 961, + 958, 957, 956, 697, 698, 699, 700, 701, 702, 703, + 704, 705, 706, 707, 727, 728, 729, 730, 731, 719, + 720, 721, 749, 722, 723, 708, 709, 710, 711, 712, + 713, 714, 751, 752, 753, 754, 755, 756, 715, 716, + 717, 718, 748, 739, 737, 738, 734, 735, 1188, 726, + 732, 733, 740, 741, 743, 742, 744, 745, 54, 55, + 426, 56, 57, 736, 747, 746, -220, 58, 59, 419, + 60,-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766, + 28,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767, 97, + 98, 99, 100, 101, 9, 906, 907,-32766, 1202, 761, + 328, 764, 1188, 814, 61, 62, 52, 285, 500, 951, + 63, 434, 64, 294, 295, 65, 66, 67, 68, 69, + 70, 71, 72, 340, 25, 302, 73, 418,-32766,-32766, + -32766, 882, 1103, 1104, 761, 1080, 764,-32766,-32766,-32766, + 475, -255, 298, 825, 835, 759, 219, 220, 221,-32766, + 36,-32766,-32766,-32766,-32766,-32766,-32766, 125,-32766, 438, + -32766,-32766,-32766,-32766,-32766, 1052, 500, 206, 310, 434, + 499, 423, 1052, 129, 439, 759, 336, 340, 483, 484, + 10, 959, 960, 961, 958, 957, 956, 485, 486, 124, + 1109, 1110, 1111, 1112, 1106, 1107, 313,-32766,-32766,-32766, + -32766, 500, 1113, 1108, 434, 219, 220, 221, 425, 41, + 764, 336, 324, 1222, 325, 427, -126, -126, -126, -4, + 835, 474,-32766,-32766,-32766, 823, 206, 913, 40, 21, + 428, -126, 476, -126, 477, -126, 478, -126, 123, 429, + 219, 220, 221, 31, 32, 430, 431, 354, 130, 33, + 479, 883, 336, 74, 1203, 128, 352, 353, 480, 481, + 912, 206, 247, 301, 482, 210, 1052, 806, 853, 432, + 433, 312, 30, 297, 35, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 421, 1052, + 296, 427, 1154, 837, 647, -126, 835, 474, 102, 103, + 104, 823, 304, 816, 40, 21, 428, 332, 476, 632, + 477, 422, 478, 298, 105, 429, 991, 993, 20, 31, + 32, 430, 431, 933, 409, 33, 479, 220, 221, 74, + 323,-32766, 352, 353, 480, 481,-32766,-32766,-32766, 424, + 482, 1052, 448, 768, 853, 432, 433, 206,-32766,-32766, + -32766, 48, 906, 907, 1153, 1070, 339,-32766, 485,-32766, + -32766,-32766,-32766, 1231, 337, 500, 1232, 427, 434, 837, + 647, -4, 835, 474, -224, 1052, 633, 823, 49, 434, + 40, 21, 428, 937, 476, 776, 477, 777, 478, -504, + 820, 429, -205, -205, -205, 31, 32, 430, 431,-32766, + -32766, 33, 479, 75,-32766, 74,-32766, 594, 352, 353, + 480, 481,-32766,-32766,-32766, 119, 482, 526, 451, 806, + 853, 432, 433, 240, 241, 242, 128,-32766,-32766,-32766, + 411, 775, 51,-32766, 120,-32766,-32766,-32766, 500, 243, + 244, 434, 552, 427, 417, 837, 647, -205,-32766, 474, + -32766,-32766, 1052, 823, 821, 351, 40, 21, 428, 1052, + 476, 121, 477, 449, 478, 1115, 211, 429, -204, -204, + -204, 31, 32, 430, 431, 1115, 286, 33, 479, 818, + 835, 74, 932, 299, 352, 353, 480, 481, 212, 219, + 220, 221, 482, 335, 245, 806, 853, 432, 433,-32766, + -32766,-32766, 213, 79, 80, 81, 99, 100, 101, 650, + 206, 237, 238, 239, 444, 122, 776, 649, 777, 300, + -32766, 837, 647, -204, 34, 248, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 1052, + 304, 427, 221, 249, 835, 250, 78, 474, 206, 133, + 336, 823, 105, 585, 40, 21, 428, 127, 476, -83, + 477, 206, 478,-32766,-32766, 429, 219, 220, 221, 31, + 32, 430, 316, 651, 658, 33, 479, 835, 661, 74, + 243, 244, 352, 353, 458, 22, 833, 206, 463, 601, + 482, 134, 362, 132, 608, 609, 769, 653,-32766, 948, + 667, 936, 675, 846, 304, 105, 655, 43, 44, 45, + 616, 761, 759, 46, 47, 427, 302, -274, 50, 837, + 647, 474, 53, 131, 588, 823, 442, 468, 40, 21, + 428, 762, 476, 606, 477,-32766, 478, 764, 447, 429, + 964, 854, 532, 31, 32, 430, 855, 327, 427, 33, + 479, 835, 641, 74, 474, 620, 352, 353, 823, -81, + 628, 40, 21, 428, 482, 476, 11, 477, 450, 478, + 282, 591, 429, 375, 605, 848, 31, 32, 430, 0, + 659, 329, 33, 479, 835, 0, 74, 0, 0, 352, + 353, 0, 0, 837, 647, -504, 0, 482, 326, 0, + 331, 0, 0, 0, 0, 0, 0, 0, 309, 311, + -505, 0, 485, 664, 0, 0, 0, 0, 0, 0, + 0, 0, 427, 1114, 1160, 0, 869, 647, 474, -413, + -405, 835, 823, 5, 6, 40, 21, 428, 12, 476, + 14, 477, 361, 478, -414, 386, 429, 387, 24, 395, + 31, 32, 430, 444, 534, 427, 33, 479, 412, 413, + 74, 474, 38, 352, 353, 823, 39, 670, 40, 21, + 428, 482, 476, 671, 477, 774, 478, 824, 832, 429, + 811, 826, 885, 31, 32, 430, 876, 877, 809, 33, + 479, 870, 867, 74, 865, 943, 352, 353, 944, 941, + 837, 647, 427, 815, 482, 817, 819, 831, 474, 822, + 940, 772, 823, 773, 942, 40, 21, 428, 78, 476, + 648, 477, 652, 478, 654, 656, 429, 657, 246, 660, + 31, 32, 430, 837, 647, 662, 33, 479, 663, 665, + 74, 666, 126, 352, 353, 333, 214, 215, 334, 407, + 408, 482, 216, 672, 217, 807, 1228, 1230, 771, 852, + 770, 851, 1229, 850, 1066, 842, 208, 1054, 849, 1055, + 840, 214, 215, 949, 1103, 1104, 874, 216,-32766, 217, + 837, 647, 1105, 875, 462, 1227, 1196, 1194, 1179, 1192, + 1094, 208, 924, 1200, 1190, 780, 781, 778, 779, 1103, + 1104, 26, 29,-32766, 37, 42, 76, 1105, 77, 209, + 284, 292, 293, 305, 306, 307, 308, 341, 410, 416, + -32766, 0, -221, -220, 16, 17, 18, 380, 459, 466, + 573, 467, 1109, 1110, 1111, 1112, 1106, 1107, 385, 472, + 567, 638, 1057, 1060, 1113, 1108, 914, 1119, 1056, 1032, + 577, 218, 287,-32766, 1031, 573, 1096, 1109, 1110, 1111, + 1112, 1106, 1107, 385, -423, 1050, 0, 1061, 1063, 1113, + 1108, 1062, 1065, 1064, 1079, 1193, 218, 1178,-32766, 1174, + 0, 1191, 1093, 1225, 1120, 1173, 612, 0, 1159 + ); + + protected $actionCheck = array( + 2, 3, 4, 5, 6, 13, 8, 9, 10, 11, + 12, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 0, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 8, 9, 10, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 12, 67, 68, 32, 33, + 34, 35, 36, 55, 29, 8, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 69, 70, 71, + 72, 73, 74, 75, 1, 7, 78, 114, 115, 116, + 117, 118, 119, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 80, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 2, 3, + 4, 5, 6, 145, 146, 147, 154, 11, 12, 125, + 14, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 7, 42, 43, 44, 45, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 105, 132, 133, 153, 1, 78, + 111, 80, 80, 150, 48, 49, 68, 7, 145, 120, + 54, 148, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 155, 68, 69, 70, 71, 8, 9, + 10, 30, 76, 77, 78, 114, 80, 8, 9, 10, + 84, 152, 36, 150, 1, 78, 8, 9, 10, 29, + 13, 31, 32, 33, 34, 35, 36, 7, 29, 103, + 31, 32, 33, 34, 35, 12, 145, 29, 130, 148, + 114, 7, 12, 151, 153, 78, 155, 155, 122, 123, + 7, 114, 115, 116, 117, 118, 119, 131, 132, 151, + 134, 135, 136, 137, 138, 139, 140, 32, 33, 34, + 35, 145, 146, 147, 148, 8, 9, 10, 7, 153, + 80, 155, 156, 83, 158, 72, 73, 74, 75, 0, + 1, 78, 8, 9, 10, 82, 29, 154, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 13, 96, + 8, 9, 10, 100, 101, 102, 103, 7, 151, 106, + 107, 150, 155, 110, 154, 149, 113, 114, 115, 116, + 154, 29, 30, 36, 121, 7, 12, 124, 125, 126, + 127, 7, 142, 143, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 7, 12, + 36, 72, 154, 150, 151, 152, 1, 78, 51, 52, + 53, 82, 55, 150, 85, 86, 87, 7, 89, 78, + 91, 7, 93, 36, 67, 96, 57, 58, 154, 100, + 101, 102, 103, 150, 105, 106, 107, 9, 10, 110, + 111, 80, 113, 114, 115, 116, 8, 9, 10, 7, + 121, 12, 30, 124, 125, 126, 127, 29, 32, 33, + 34, 68, 132, 133, 157, 154, 68, 29, 131, 31, + 32, 33, 34, 78, 145, 145, 81, 72, 148, 150, + 151, 152, 1, 78, 154, 12, 145, 82, 68, 148, + 85, 86, 87, 152, 89, 103, 91, 105, 93, 130, + 150, 96, 97, 98, 99, 100, 101, 102, 103, 8, + 9, 106, 107, 149, 153, 110, 155, 83, 113, 114, + 115, 116, 8, 9, 10, 15, 121, 83, 130, 124, + 125, 126, 127, 51, 52, 53, 149, 8, 9, 10, + 148, 150, 68, 29, 151, 31, 32, 33, 145, 67, + 68, 148, 79, 72, 7, 150, 151, 152, 29, 78, + 31, 32, 12, 82, 150, 7, 85, 86, 87, 12, + 89, 151, 91, 151, 93, 141, 15, 96, 97, 98, + 99, 100, 101, 102, 103, 141, 36, 106, 107, 150, + 1, 110, 150, 36, 113, 114, 115, 116, 15, 8, + 9, 10, 121, 7, 13, 124, 125, 126, 127, 8, + 9, 10, 15, 8, 9, 10, 48, 49, 50, 30, + 29, 48, 49, 50, 148, 151, 103, 151, 105, 7, + 29, 150, 151, 152, 29, 15, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 12, + 55, 72, 10, 15, 1, 15, 151, 78, 29, 151, + 155, 82, 67, 155, 85, 86, 87, 30, 89, 30, + 91, 29, 93, 32, 33, 96, 8, 9, 10, 100, + 101, 102, 30, 30, 30, 106, 107, 1, 30, 110, + 67, 68, 113, 114, 73, 74, 30, 29, 73, 74, + 121, 98, 99, 30, 108, 109, 150, 151, 32, 150, + 151, 150, 151, 36, 55, 67, 30, 68, 68, 68, + 75, 78, 78, 68, 68, 72, 69, 80, 68, 150, + 151, 78, 68, 68, 88, 82, 103, 103, 85, 86, + 87, 78, 89, 111, 91, 83, 93, 80, 87, 96, + 80, 125, 83, 100, 101, 102, 125, 129, 72, 106, + 107, 1, 90, 110, 78, 94, 113, 114, 82, 95, + 92, 85, 86, 87, 121, 89, 95, 91, 95, 93, + 95, 97, 96, 148, 97, 149, 100, 101, 102, -1, + 30, 112, 106, 107, 1, -1, 110, -1, -1, 113, + 114, -1, -1, 150, 151, 130, -1, 121, 128, -1, + 128, -1, -1, -1, -1, -1, -1, -1, 130, 130, + 130, -1, 131, 30, -1, -1, -1, -1, -1, -1, + -1, -1, 72, 141, 141, -1, 150, 151, 78, 144, + 144, 1, 82, 144, 144, 85, 86, 87, 144, 89, + 144, 91, 144, 93, 144, 148, 96, 148, 153, 148, + 100, 101, 102, 148, 148, 72, 106, 107, 148, 148, + 110, 78, 150, 113, 114, 82, 150, 150, 85, 86, + 87, 121, 89, 150, 91, 150, 93, 150, 150, 96, + 150, 150, 150, 100, 101, 102, 150, 150, 150, 106, + 107, 150, 150, 110, 150, 150, 113, 114, 150, 150, + 150, 151, 72, 150, 121, 150, 150, 150, 78, 150, + 150, 150, 82, 150, 150, 85, 86, 87, 151, 89, + 151, 91, 151, 93, 151, 151, 96, 151, 30, 151, + 100, 101, 102, 150, 151, 151, 106, 107, 151, 151, + 110, 151, 151, 113, 114, 151, 48, 49, 151, 151, + 151, 121, 54, 152, 56, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 68, 152, 152, 152, + 152, 48, 49, 152, 76, 77, 152, 54, 80, 56, + 150, 151, 84, 152, 152, 152, 152, 152, 152, 152, + 152, 68, 152, 152, 152, 152, 152, 152, 152, 76, + 77, 153, 153, 80, 153, 153, 153, 84, 153, 153, + 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, + 153, -1, 154, 154, 154, 154, 154, 154, 154, 154, + 132, 154, 134, 135, 136, 137, 138, 139, 140, 154, + 154, 154, 154, 154, 146, 147, 154, 154, 154, 154, + 154, 153, 155, 155, 154, 132, 154, 134, 135, 136, + 137, 138, 139, 140, 156, 156, -1, 157, 157, 146, + 147, 157, 157, 157, 157, 157, 153, 157, 155, 157, + -1, 157, 157, 157, 157, 157, 157, -1, 158 + ); + + protected $actionBase = array( + 0, 223, 299, 375, 451, 102, 102, 311, 754, -2, + -2, 147, -2, -2, -2, 633, 740, 773, 740, 559, + 666, 820, 820, 820, 186, 334, 334, 334, 357, 892, + 443, 33, 233, 409, 617, 520, 527, 240, 240, 240, + 240, 136, 136, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, + 240, 240, 240, 240, 240, 181, 73, 305, 217, 743, + 745, 746, 747, 884, 665, 885, 823, 824, 653, 825, + 826, 827, 828, 830, 822, 831, 919, 832, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 47, 561, 218, 312, 277, 471, 648, 648, 648, 648, + 648, 648, 648, 294, 294, 294, 294, 294, 294, 294, + 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, + 294, 398, 571, 571, 571, 622, 888, 609, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 913, 913, 913, 913, 913, 913, 913, 913, 913, + 913, 499, -21, -21, 484, 656, 408, 621, 209, 396, + 200, 25, 25, 25, 25, 25, 245, 16, 4, 4, + 4, 4, 339, 123, 123, 123, 123, 119, 119, 119, + 119, 69, 307, 307, 671, 671, 637, 781, 538, 538, + 543, 543, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 24, 627, 857, 300, 300, 300, 300, 177, + 177, 177, 43, 414, 673, 920, 43, 327, 327, 327, + 452, 452, 452, 362, 649, 210, 331, 331, 331, 331, + 210, 331, 331, 493, 493, 493, 485, 446, 642, 365, + 488, 373, 390, 664, 814, 667, 821, 541, 690, 118, + 700, 701, 887, 613, 887, 601, 605, 576, 676, 392, + 850, 48, 181, 539, 444, 639, 749, 361, 751, 320, + 384, 368, 536, 363, 244, 784, 753, 886, 890, 180, + 670, 639, 639, 639, 153, 404, 787, 788, 363, -8, + 603, 603, 603, 603, 833, 789, 603, 603, 603, 603, + 834, 835, 253, 412, 836, 281, 750, 623, 623, 624, + 624, 623, 623, 623, 623, 650, 657, 623, 839, 685, + 685, 624, 647, 624, 650, 657, 842, 842, 842, 842, + 624, 657, 624, 624, 623, 624, 685, 685, 657, 637, + 685, 68, 657, 660, 623, 662, 662, 842, 707, 713, + 624, 624, 608, 685, 685, 685, 608, 657, 842, 612, + 616, 338, 685, 842, 632, 647, 632, 612, 657, 632, + 647, 647, 632, 20, 619, 629, 841, 843, 855, 759, + 606, 611, 848, 849, 845, 847, 838, 646, 696, 715, + 717, 480, 630, 631, 635, 636, 679, 640, 668, 613, + 695, 628, 628, 628, 655, 680, 655, 628, 628, 628, + 628, 628, 628, 628, 628, 900, 675, 688, 669, 641, + 718, 531, 615, 663, 553, 761, 672, 696, 696, 793, + 878, 863, 898, 899, 643, 850, 876, 655, 901, 702, + 230, 567, 851, 791, 687, 689, 655, 852, 655, 763, + 655, 879, 794, 645, 795, 696, 796, 628, 882, 904, + 905, 906, 907, 908, 910, 912, 914, 644, 915, 720, + 626, 864, 344, 846, 676, 684, 691, 721, 797, 380, + 916, 798, 655, 655, 764, 757, 655, 766, 722, 706, + 860, 726, 865, 917, 672, 867, 655, 614, 799, 918, + 380, 634, 625, 858, 651, 727, 856, 883, 854, 768, + 590, 618, 800, 801, 802, 638, 728, 861, 862, 859, + 731, 774, 620, 777, 658, 803, 778, 853, 732, 805, + 806, 877, 654, 695, 686, 659, 661, 652, 780, 807, + 875, 734, 735, 738, 808, 739, 811, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 136, 136, 136, + 136, -2, -2, -2, -2, 0, 0, -2, 0, 0, + 0, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 0, 0, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 575, -21, -21, -21, + -21, 575, -21, -21, -21, -21, -21, -21, -21, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, -21, 575, 575, + 575, -21, 101, -21, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 575, 0, 0, + 575, -21, 575, -21, 575, -21, 575, 575, 575, 575, + 575, 575, -21, -21, -21, -21, -21, -21, 0, 327, + 327, 327, 327, -21, -21, -21, -21, -37, 101, 101, + 101, 101, 101, 101, 327, 327, 452, 452, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 101, -37, + 101, 623, 623, 623, 623, 647, 647, 647, 623, 210, + 210, 210, 623, 0, 0, 0, 0, 0, 0, 623, + 210, 0, 101, 101, 101, 101, 0, 101, 101, 623, + 623, 623, 647, 623, 210, 647, 647, 623, 685, 583, + 583, 583, 583, 380, 363, 0, 623, 623, 647, 647, + 647, 0, 0, 0, 685, 0, 624, 0, 0, 0, + 0, 628, 230, 0, 528, 0, 0, 0, 0, 0, + 0, 643, 528, 592, 592, 0, 644, 628, 628, 628, + 0, 0, 643, 643, 0, 0, 0, 0, 0, 0, + 566, 643, 0, 0, 0, 0, 566, 517, 0, 0, + 517, 0, 380 + ); + + protected $actionDefault = array( + 3,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767, 533, 533, 488,32767,32767, + 32767,32767,32767,32767,32767,32767,32767, 293, 293, 293, + 32767,32767,32767, 521, 521, 521, 521, 521, 521, 521, + 521, 521, 521, 521,32767,32767,32767,32767,32767, 376, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767, 382, 538,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767, 357, 358, + 360, 361, 292, 541, 522, 241, 383, 537, 291, 243, + 321, 492,32767,32767,32767, 323, 120, 252, 197, 491, + 123, 290, 228, 375, 377, 322, 297, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 296, 448, 354, 353, 352, 450,32767, 449, 485, 485, + 488,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767, 319, 476, 475, 320, 446, 324, 447, 326, 451, + 325, 342, 343, 340, 341, 344, 453, 452, 469, 470, + 467, 468, 295, 345, 346, 347, 348, 471, 472, 473, + 474, 276,32767,32767, 532, 532,32767,32767, 333, 334, + 460, 461,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767, 277,32767, 232, 232, 232, 232,32767, + 32767,32767, 232,32767,32767,32767,32767, 328, 329, 327, + 455, 456, 454,32767, 422,32767,32767,32767,32767,32767, + 424,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767, 493,32767,32767,32767,32767,32767, 506, 411, + 32767,32767,32767, 404,32767, 216, 218, 165, 479,32767, + 32767,32767,32767,32767, 511, 338,32767,32767,32767,32767, + 32767, 548,32767, 506,32767,32767,32767,32767,32767,32767, + 351, 330, 331, 332,32767,32767,32767,32767, 510, 504, + 463, 464, 465, 466,32767,32767, 457, 458, 459, 462, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767, 169,32767, 419, 425, + 425,32767,32767,32767,32767, 169,32767,32767,32767,32767, + 32767, 169,32767,32767,32767,32767, 509, 508, 169,32767, + 405, 487, 169, 182,32767, 180, 180,32767, 202, 202, + 32767,32767, 184, 480, 499,32767, 184, 169,32767, 393, + 171, 487,32767,32767, 234,32767, 234, 393, 169, 234, + 32767,32767, 234,32767, 85, 429,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767, 406,32767, + 32767,32767, 372, 373, 482, 495,32767, 496,32767, 404, + 32767, 336, 337, 339, 316,32767, 318, 362, 363, 364, + 365, 366, 367, 368, 370,32767, 409,32767, 412,32767, + 32767,32767, 87, 112, 251,32767, 546, 87, 407,32767, + 32767, 300, 546,32767,32767,32767,32767, 540,32767,32767, + 294,32767,32767,32767, 87, 87, 247,32767, 167,32767, + 530,32767, 547,32767, 504, 408,32767, 335,32767,32767, + 32767,32767,32767,32767,32767,32767,32767, 505,32767,32767, + 32767,32767, 223,32767, 442,32767, 87,32767,32767, 183, + 32767,32767, 298, 242,32767,32767, 539,32767,32767,32767, + 32767,32767,32767,32767,32767,32767, 168,32767,32767,32767, + 185,32767,32767, 504,32767,32767,32767,32767,32767,32767, + 32767, 289,32767,32767,32767,32767,32767,32767,32767, 504, + 32767,32767, 227,32767,32767,32767,32767,32767,32767,32767, + 32767,32767, 85, 60,32767, 270,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767, 125, 125, 3, + 125, 125, 254, 3, 254, 125, 254, 254, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 125, 210, 213, + 202, 202, 162, 125, 125, 262 + ); + + protected $goto = array( + 165, 138, 138, 138, 165, 143, 146, 139, 140, 141, + 148, 186, 167, 162, 162, 162, 162, 143, 143, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 158, 159, 160, 161, 183, 137, 184, 501, 502, 365, + 503, 507, 508, 509, 510, 511, 512, 513, 514, 977, + 142, 163, 144, 145, 147, 170, 175, 185, 202, 251, + 254, 256, 258, 260, 261, 262, 263, 264, 265, 273, + 274, 275, 276, 288, 289, 317, 318, 319, 381, 382, + 383, 557, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 149, 150, 151, 166, + 152, 168, 153, 203, 169, 154, 155, 156, 204, 157, + 135, 634, 575, 760, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, + 575, 575, 575, 575, 575, 575, 575, 1116, 766, 1116, + 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, + 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, + 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, + 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, 1116, + 1116, 1116, 505, 505, 505, 505, 505, 505, 516, 642, + 516, 767, 505, 505, 505, 505, 505, 505, 505, 505, + 505, 505, 517, 797, 517, 898, 898, 1207, 1207, 531, + 589, 617, 862, 862, 862, 862, 173, 857, 863, 1092, + 1091, 176, 177, 178, 390, 391, 392, 393, 172, 201, + 205, 207, 255, 257, 259, 266, 267, 268, 269, 270, + 271, 277, 278, 279, 280, 290, 291, 320, 321, 322, + 396, 397, 398, 399, 174, 179, 252, 253, 180, 181, + 182, 389, 619, 550, 550, 582, 546, 598, 765, 613, + 614, 548, 548, 504, 506, 537, 554, 583, 586, 596, + 603, 884, 866, 864, 866, 668, 828, 519, 893, 888, + 574, 344, 574, 574, 574, 574, 574, 574, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, + 574, 574, 574, 574, 574, 559, 560, 561, 562, 563, + 564, 565, 566, 568, 599, 522, 558, 330, 315, 1219, + 1219, 530, 592, 595, 640, 527, 527, 527, 868, 527, + 600, 347, 406, 530, 530, 1219, 440, 440, 440, 440, + 440, 440, 545, 527, 551, 954, 440, 440, 440, 440, + 440, 440, 440, 440, 440, 440, 1081, 1211, 1081, 905, + 905, 905, 905, 3, 4, 623, 905, 955, 626, 556, + 905, 363, 1185, 581, 1185, 916, 371, 371, 371, 366, + 1081, 1081, 1081, 1081, 790, 1081, 1081, 371, 371, 1184, + 607, 1184, 1177, 371, 1165, 471, 1201, 1201, 1201, 527, + 527, 371, 1233, 544, 576, 527, 527, 618, 1074, 527, + 1204, 790, 790, 378, 1025, 922, 524, 922, 518, 902, + 518, 911, 350, 1183, 975, 400, 528, 543, 786, 669, + 570, 372, 376, 920, 604, 784, 555, 895, 624, 625, + 891, 629, 630, 637, 639, 644, 646, 453, 455, 947, + 645, 673, 1097, 404, 1117, 627, 945, 1199, 1199, 1199, + 1035, 19, 15, 359, 1218, 1218, 1067, 872, 783, 783, + 794, 963, 791, 791, 791, 793, 1072, 925, 782, 456, + 1218, 569, 345, 346, 360, 524, 1077, 1078, 1221, 1162, + 1074, 535, 23, 587, 469, 547, 0, 615, 373, 373, + 373, 0, 0, 1075, 1176, 1075, 0, 0, 542, 0, + 460, 0, 1076, 0, 0, 0, 0, 0, 0, 0, + 0, 13, 0, 0, 0, 373, 0, 0, 622, 388, + 0, 0, 0, 1073, 631, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 521, 541, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 521, + 0, 541, 0, 0, 0, 0, 0, 536, 520, 0, + 525, 443, 0, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 789, 1226 + ); + + protected $gotoCheck = array( + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 56, 66, 5, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 124, 14, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 115, 115, 115, 115, 115, 115, 66, 8, + 66, 15, 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 115, 28, 115, 74, 74, 74, 74, 99, + 39, 39, 66, 66, 66, 66, 26, 66, 66, 122, + 122, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 50, 50, 50, 50, 50, 50, 64, 13, 64, + 64, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 13, 13, 13, 13, 13, 49, 13, 13, 13, + 56, 69, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 10, 46, 123, 123, 140, + 140, 46, 59, 59, 59, 10, 10, 10, 32, 10, + 67, 67, 67, 46, 46, 140, 56, 56, 56, 56, + 56, 56, 10, 10, 107, 95, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 138, 56, 56, + 56, 56, 56, 29, 29, 60, 56, 95, 60, 2, + 56, 60, 116, 2, 116, 81, 12, 12, 12, 45, + 56, 56, 56, 56, 22, 56, 56, 12, 12, 117, + 125, 117, 79, 12, 129, 56, 117, 117, 117, 10, + 10, 12, 12, 10, 10, 10, 10, 48, 79, 10, + 136, 22, 22, 47, 100, 12, 12, 12, 120, 76, + 120, 78, 17, 117, 99, 21, 10, 31, 24, 31, + 31, 11, 16, 82, 31, 23, 10, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 7, 7, 7, + 7, 71, 33, 20, 7, 7, 7, 8, 8, 8, + 33, 33, 33, 33, 139, 139, 111, 68, 22, 22, + 25, 97, 22, 22, 22, 22, 113, 83, 22, 62, + 139, 33, 69, 69, 57, 12, 79, 79, 139, 128, + 79, 57, 33, 63, 106, 57, -1, 33, 121, 121, + 121, -1, -1, 79, 79, 79, -1, -1, 8, -1, + 57, -1, 79, -1, -1, -1, -1, -1, -1, -1, + -1, 57, -1, -1, -1, 121, -1, -1, 12, 121, + -1, -1, -1, 12, 12, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8, 8, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, + -1, 8, -1, -1, -1, -1, -1, 99, 8, -1, + 8, 8, -1, 8, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 8, 8 + ); + + protected $gotoBase = array( + 0, 0, -230, 0, 0, 102, 0, 492, 198, 0, + 42, 144, 113, 286, 156, 209, 132, 149, 0, 0, + 95, 141, 111, 134, 143, 99, 30, 0, 213, -260, + 0, -171, 358, 84, 0, 0, 0, 0, 0, 192, + 0, 0, -24, 0, 0, 389, 342, 170, 174, 288, + -1, 0, 0, 0, 0, 0, 104, 103, 0, 66, + -51, 0, 83, 87, -367, 0, -94, 53, 94, -138, + 0, 139, 0, 0, -57, 0, 157, 0, 155, 100, + 0, 397, 129, 105, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 97, 0, 188, + 160, 0, 0, 0, 0, 0, 77, 368, 311, 0, + 0, 96, 0, 101, 0, -80, 120, 137, 0, 0, + 166, 239, -71, 43, -49, 231, 0, 0, 78, 234, + 0, 0, 0, 0, 0, 0, 175, 0, 380, 200, + 55, 0, 0 + ); + + protected $gotoDefault = array( + -32768, 473, 677, 2, 678, 750, 758, 610, 487, 643, + 488, 523, 1195, 803, 804, 805, 368, 414, 489, 367, + 401, 394, 792, 785, 787, 795, 171, 402, 798, 1, + 800, 529, 836, 1026, 355, 808, 356, 602, 810, 539, + 812, 813, 136, 369, 370, 540, 490, 377, 590, 827, + 272, 374, 829, 357, 830, 839, 358, 470, 465, 571, + 621, 435, 452, 584, 578, 549, 1089, 579, 871, 343, + 879, 674, 887, 890, 491, 572, 901, 457, 909, 1102, + 384, 915, 921, 926, 281, 929, 415, 403, 597, 934, + 935, 7, 939, 635, 636, 8, 303, 962, 611, 976, + 420, 1045, 1047, 492, 493, 533, 464, 515, 538, 494, + 1068, 446, 405, 1071, 495, 496, 436, 437, 1086, 349, + 1170, 348, 454, 314, 1157, 593, 1121, 461, 1210, 1166, + 342, 497, 498, 364, 1189, 379, 1205, 441, 1212, 1220, + 338, 553, 580 + ); + + protected $ruleToNonTerminal = array( + 0, 1, 3, 3, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 8, 9, 10, 10, 11, 12, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 17, 17, 18, 18, 18, 18, 20, 20, 16, 16, + 21, 21, 22, 22, 23, 23, 24, 24, 19, 19, + 25, 27, 27, 28, 29, 29, 31, 30, 30, 30, + 30, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 13, 13, 53, 53, 55, 54, 54, 47, 47, 57, + 57, 58, 58, 14, 15, 15, 15, 61, 61, 61, + 62, 62, 65, 65, 63, 63, 67, 67, 40, 40, + 49, 49, 52, 52, 52, 51, 51, 68, 41, 41, + 41, 41, 69, 69, 70, 70, 71, 71, 38, 38, + 34, 34, 72, 36, 36, 73, 35, 35, 37, 37, + 48, 48, 48, 59, 59, 75, 75, 76, 76, 78, + 78, 78, 77, 77, 60, 60, 79, 79, 79, 80, + 80, 81, 81, 81, 43, 43, 82, 82, 82, 44, + 44, 83, 83, 84, 84, 64, 85, 85, 85, 85, + 90, 90, 91, 91, 92, 92, 92, 92, 92, 93, + 94, 94, 89, 89, 86, 86, 88, 88, 96, 96, + 95, 95, 95, 95, 95, 95, 87, 87, 98, 97, + 97, 45, 45, 39, 39, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 33, 33, 46, 46, 103, 103, 104, 104, 104, 104, + 110, 99, 99, 106, 106, 112, 112, 113, 114, 114, + 114, 114, 114, 114, 66, 66, 56, 56, 56, 100, + 100, 118, 118, 115, 115, 119, 119, 119, 119, 101, + 101, 101, 105, 105, 105, 111, 111, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 26, 26, 26, 26, 26, 26, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 109, + 109, 102, 102, 102, 102, 125, 125, 128, 128, 127, + 127, 129, 129, 50, 50, 50, 50, 131, 131, 130, + 130, 130, 130, 130, 132, 132, 117, 117, 120, 120, + 116, 116, 134, 133, 133, 133, 133, 121, 121, 121, + 121, 108, 108, 122, 122, 122, 122, 74, 135, 135, + 136, 136, 136, 107, 107, 137, 137, 138, 138, 138, + 138, 138, 123, 123, 123, 123, 140, 141, 139, 139, + 139, 139, 139, 139, 139, 142, 142, 142 + ); + + protected $ruleToLength = array( + 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 3, 5, 4, 3, 4, 2, 3, + 1, 1, 7, 8, 6, 7, 3, 1, 3, 1, + 3, 1, 1, 3, 1, 2, 1, 2, 3, 1, + 3, 3, 1, 3, 2, 0, 1, 1, 1, 1, + 1, 3, 5, 8, 3, 5, 9, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 3, 1, 2, 2, + 5, 7, 9, 5, 6, 3, 3, 2, 2, 1, + 1, 1, 0, 2, 8, 0, 4, 1, 3, 0, + 1, 0, 1, 10, 7, 6, 5, 1, 2, 2, + 0, 2, 0, 2, 0, 2, 1, 3, 1, 4, + 1, 4, 1, 1, 4, 1, 3, 3, 3, 4, + 4, 5, 0, 2, 4, 3, 1, 1, 1, 4, + 0, 2, 3, 0, 2, 4, 0, 2, 0, 3, + 1, 2, 1, 1, 0, 1, 3, 4, 6, 1, + 1, 1, 0, 1, 0, 2, 2, 3, 3, 1, + 3, 1, 2, 2, 3, 1, 1, 2, 4, 3, + 1, 1, 3, 2, 0, 1, 3, 3, 9, 3, + 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, + 1, 1, 1, 3, 1, 1, 0, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 3, 3, 1, 0, 1, 1, 3, 3, 4, 4, + 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 3, 5, 4, 3, 4, 4, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 1, 1, 3, 2, 1, 2, 10, 11, + 3, 3, 2, 4, 4, 3, 4, 4, 4, 4, + 7, 3, 2, 0, 4, 1, 3, 2, 2, 4, + 6, 2, 2, 4, 1, 1, 1, 2, 3, 1, + 1, 1, 1, 1, 1, 3, 3, 4, 4, 0, + 2, 1, 0, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, + 1, 3, 1, 4, 3, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 5, 4, 4, 3, 1, + 3, 1, 1, 3, 3, 0, 2, 0, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 6, 4, 3, + 4, 2, 4, 4, 1, 3, 1, 2, 1, 1, + 4, 1, 1, 3, 6, 4, 4, 4, 4, 1, + 4, 0, 1, 1, 3, 1, 1, 4, 3, 1, + 1, 1, 0, 0, 2, 3, 1, 3, 1, 4, + 2, 2, 2, 2, 1, 2, 1, 1, 1, 4, + 3, 3, 3, 6, 3, 1, 1, 1 + ); + + protected function initReduceCallbacks() { + $this->reduceCallbacks = [ + 0 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 1 => function ($stackPos) { + $this->semValue = $this->handleNamespaces($this->semStack[$stackPos-(1-1)]); + }, + 2 => function ($stackPos) { + if (is_array($this->semStack[$stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); } else { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }; + }, + 3 => function ($stackPos) { + $this->semValue = array(); + }, + 4 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 5 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 6 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 7 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 8 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 9 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 10 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 11 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 12 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 13 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 14 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 15 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 16 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 17 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 18 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 19 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 20 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 21 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 22 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 23 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 24 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 25 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 26 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 27 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 28 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 29 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 30 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 31 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 32 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 33 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 34 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 35 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 36 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 37 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 38 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 39 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 40 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 41 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 42 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 43 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 44 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 45 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 46 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 47 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 48 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 49 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 50 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 51 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 52 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 53 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 54 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 55 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 56 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 57 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 58 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 59 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 60 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 61 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 62 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 63 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 64 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 65 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 66 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 67 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 68 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 69 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 70 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 71 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 72 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 73 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 74 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 75 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 76 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 77 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 78 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 79 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 80 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 81 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 82 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 83 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 84 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 85 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 86 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 87 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 88 => function ($stackPos) { + $this->semValue = new Expr\Variable(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 89 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 90 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 91 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 92 => function ($stackPos) { + $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 93 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos-(3-2)], null, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($this->semValue); + }, + 94 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos-(5-2)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, + 95 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, + 96 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 97 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 98 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 99 => function ($stackPos) { + $this->semValue = new Stmt\Const_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 100 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_FUNCTION; + }, + 101 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_CONSTANT; + }, + 102 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(7-3)], $this->startAttributeStack[$stackPos-(7-3)] + $this->endAttributeStack[$stackPos-(7-3)]), $this->semStack[$stackPos-(7-6)], $this->semStack[$stackPos-(7-2)], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 103 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(8-4)], $this->startAttributeStack[$stackPos-(8-4)] + $this->endAttributeStack[$stackPos-(8-4)]), $this->semStack[$stackPos-(8-7)], $this->semStack[$stackPos-(8-2)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 104 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(6-2)], $this->startAttributeStack[$stackPos-(6-2)] + $this->endAttributeStack[$stackPos-(6-2)]), $this->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 105 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(7-3)], $this->startAttributeStack[$stackPos-(7-3)] + $this->endAttributeStack[$stackPos-(7-3)]), $this->semStack[$stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 106 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 107 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 108 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 109 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 110 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 111 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 112 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->checkUseUse($this->semValue, $stackPos-(1-1)); + }, + 113 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->checkUseUse($this->semValue, $stackPos-(3-3)); + }, + 114 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 115 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 116 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; $this->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, + 117 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; $this->semValue->type = $this->semStack[$stackPos-(2-1)]; + }, + 118 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 119 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 120 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 121 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 122 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 123 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 124 => function ($stackPos) { + if (is_array($this->semStack[$stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); } else { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }; + }, + 125 => function ($stackPos) { + $this->semValue = array(); + }, + 126 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 127 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 128 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 129 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 130 => function ($stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 131 => function ($stackPos) { + + if ($this->semStack[$stackPos-(3-2)]) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; $attrs = $this->startAttributeStack[$stackPos-(3-1)]; $stmts = $this->semValue; if (!empty($attrs['comments'])) {$stmts[0]->setAttribute('comments', array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); }; + } else { + $startAttributes = $this->startAttributeStack[$stackPos-(3-1)]; if (isset($startAttributes['comments'])) { $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $this->semValue = null; }; + if (null === $this->semValue) { $this->semValue = array(); } + } + + }, + 132 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos-(5-2)], ['stmts' => is_array($this->semStack[$stackPos-(5-3)]) ? $this->semStack[$stackPos-(5-3)] : array($this->semStack[$stackPos-(5-3)]), 'elseifs' => $this->semStack[$stackPos-(5-4)], 'else' => $this->semStack[$stackPos-(5-5)]], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 133 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos-(8-2)], ['stmts' => $this->semStack[$stackPos-(8-4)], 'elseifs' => $this->semStack[$stackPos-(8-5)], 'else' => $this->semStack[$stackPos-(8-6)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 134 => function ($stackPos) { + $this->semValue = new Stmt\While_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 135 => function ($stackPos) { + $this->semValue = new Stmt\Do_($this->semStack[$stackPos-(5-4)], is_array($this->semStack[$stackPos-(5-2)]) ? $this->semStack[$stackPos-(5-2)] : array($this->semStack[$stackPos-(5-2)]), $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 136 => function ($stackPos) { + $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos-(9-3)], 'cond' => $this->semStack[$stackPos-(9-5)], 'loop' => $this->semStack[$stackPos-(9-7)], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 137 => function ($stackPos) { + $this->semValue = new Stmt\Switch_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 138 => function ($stackPos) { + $this->semValue = new Stmt\Break_(null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 139 => function ($stackPos) { + $this->semValue = new Stmt\Break_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 140 => function ($stackPos) { + $this->semValue = new Stmt\Continue_(null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 141 => function ($stackPos) { + $this->semValue = new Stmt\Continue_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 142 => function ($stackPos) { + $this->semValue = new Stmt\Return_(null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 143 => function ($stackPos) { + $this->semValue = new Stmt\Return_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 144 => function ($stackPos) { + $this->semValue = new Stmt\Global_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 145 => function ($stackPos) { + $this->semValue = new Stmt\Static_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 146 => function ($stackPos) { + $this->semValue = new Stmt\Echo_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 147 => function ($stackPos) { + $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 148 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 149 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 150 => function ($stackPos) { + $this->semValue = new Stmt\Unset_($this->semStack[$stackPos-(5-3)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 151 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos-(7-3)], $this->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos-(7-5)][1], 'stmts' => $this->semStack[$stackPos-(7-7)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 152 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos-(9-3)], $this->semStack[$stackPos-(9-7)][0], ['keyVar' => $this->semStack[$stackPos-(9-5)], 'byRef' => $this->semStack[$stackPos-(9-7)][1], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 153 => function ($stackPos) { + $this->semValue = new Stmt\Declare_($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 154 => function ($stackPos) { + $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-5)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); $this->checkTryCatch($this->semValue); + }, + 155 => function ($stackPos) { + $this->semValue = new Stmt\Throw_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 156 => function ($stackPos) { + $this->semValue = new Stmt\Goto_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 157 => function ($stackPos) { + $this->semValue = new Stmt\Label($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 158 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 159 => function ($stackPos) { + $this->semValue = array(); /* means: no statement */ + }, + 160 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 161 => function ($stackPos) { + $startAttributes = $this->startAttributeStack[$stackPos-(1-1)]; if (isset($startAttributes['comments'])) { $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $this->semValue = null; }; + if ($this->semValue === null) $this->semValue = array(); /* means: no statement */ + }, + 162 => function ($stackPos) { + $this->semValue = array(); + }, + 163 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 164 => function ($stackPos) { + $this->semValue = new Stmt\Catch_(array($this->semStack[$stackPos-(8-3)]), $this->semStack[$stackPos-(8-4)], $this->semStack[$stackPos-(8-7)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 165 => function ($stackPos) { + $this->semValue = null; + }, + 166 => function ($stackPos) { + $this->semValue = new Stmt\Finally_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 167 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 168 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 169 => function ($stackPos) { + $this->semValue = false; + }, + 170 => function ($stackPos) { + $this->semValue = true; + }, + 171 => function ($stackPos) { + $this->semValue = false; + }, + 172 => function ($stackPos) { + $this->semValue = true; + }, + 173 => function ($stackPos) { + $this->semValue = new Stmt\Function_($this->semStack[$stackPos-(10-3)], ['byRef' => $this->semStack[$stackPos-(10-2)], 'params' => $this->semStack[$stackPos-(10-5)], 'returnType' => $this->semStack[$stackPos-(10-7)], 'stmts' => $this->semStack[$stackPos-(10-9)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + }, + 174 => function ($stackPos) { + $this->semValue = new Stmt\Class_($this->semStack[$stackPos-(7-2)], ['type' => $this->semStack[$stackPos-(7-1)], 'extends' => $this->semStack[$stackPos-(7-3)], 'implements' => $this->semStack[$stackPos-(7-4)], 'stmts' => $this->semStack[$stackPos-(7-6)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + $this->checkClass($this->semValue, $stackPos-(7-2)); + }, + 175 => function ($stackPos) { + $this->semValue = new Stmt\Interface_($this->semStack[$stackPos-(6-2)], ['extends' => $this->semStack[$stackPos-(6-3)], 'stmts' => $this->semStack[$stackPos-(6-5)]], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + $this->checkInterface($this->semValue, $stackPos-(6-2)); + }, + 176 => function ($stackPos) { + $this->semValue = new Stmt\Trait_($this->semStack[$stackPos-(5-2)], ['stmts' => $this->semStack[$stackPos-(5-4)]], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 177 => function ($stackPos) { + $this->semValue = 0; + }, + 178 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, + 179 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, + 180 => function ($stackPos) { + $this->semValue = null; + }, + 181 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 182 => function ($stackPos) { + $this->semValue = array(); + }, + 183 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 184 => function ($stackPos) { + $this->semValue = array(); + }, + 185 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 186 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 187 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 188 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 189 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 190 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 191 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 192 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 193 => function ($stackPos) { + $this->semValue = null; + }, + 194 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 195 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 196 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 197 => function ($stackPos) { + $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 198 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 199 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-3)]; + }, + 200 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 201 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(5-3)]; + }, + 202 => function ($stackPos) { + $this->semValue = array(); + }, + 203 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 204 => function ($stackPos) { + $this->semValue = new Stmt\Case_($this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 205 => function ($stackPos) { + $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 206 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 207 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 208 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 209 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 210 => function ($stackPos) { + $this->semValue = array(); + }, + 211 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 212 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(3-2)], is_array($this->semStack[$stackPos-(3-3)]) ? $this->semStack[$stackPos-(3-3)] : array($this->semStack[$stackPos-(3-3)]), $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 213 => function ($stackPos) { + $this->semValue = array(); + }, + 214 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 215 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 216 => function ($stackPos) { + $this->semValue = null; + }, + 217 => function ($stackPos) { + $this->semValue = new Stmt\Else_(is_array($this->semStack[$stackPos-(2-2)]) ? $this->semStack[$stackPos-(2-2)] : array($this->semStack[$stackPos-(2-2)]), $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 218 => function ($stackPos) { + $this->semValue = null; + }, + 219 => function ($stackPos) { + $this->semValue = new Stmt\Else_($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 220 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)], false); + }, + 221 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(2-2)], true); + }, + 222 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)], false); + }, + 223 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 224 => function ($stackPos) { + $this->semValue = array(); + }, + 225 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 226 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 227 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos-(4-4)], null, $this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); $this->checkParam($this->semValue); + }, + 228 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-6)], $this->semStack[$stackPos-(6-1)], $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-3)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); $this->checkParam($this->semValue); + }, + 229 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 230 => function ($stackPos) { + $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 231 => function ($stackPos) { + $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 232 => function ($stackPos) { + $this->semValue = null; + }, + 233 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 234 => function ($stackPos) { + $this->semValue = null; + }, + 235 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 236 => function ($stackPos) { + $this->semValue = array(); + }, + 237 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 238 => function ($stackPos) { + $this->semValue = array(new Node\Arg($this->semStack[$stackPos-(3-2)], false, false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes)); + }, + 239 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 240 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 241 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(1-1)], false, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 242 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], true, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 243 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], false, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 244 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 245 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 246 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 247 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 248 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 249 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 250 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 251 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 252 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 253 => function ($stackPos) { + if ($this->semStack[$stackPos-(2-2)] !== null) { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; } + }, + 254 => function ($stackPos) { + $this->semValue = array(); + }, + 255 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 256 => function ($stackPos) { + $this->semValue = new Stmt\Property($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->checkProperty($this->semValue, $stackPos-(3-1)); + }, + 257 => function ($stackPos) { + $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos-(3-2)], 0, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 258 => function ($stackPos) { + $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos-(9-4)], ['type' => $this->semStack[$stackPos-(9-1)], 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-6)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->checkClassMethod($this->semValue, $stackPos-(9-1)); + }, + 259 => function ($stackPos) { + $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 260 => function ($stackPos) { + $this->semValue = array(); + }, + 261 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 262 => function ($stackPos) { + $this->semValue = array(); + }, + 263 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 264 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 265 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(5-1)][0], $this->semStack[$stackPos-(5-1)][1], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 266 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], null, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 267 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 268 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 269 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); + }, + 270 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 271 => function ($stackPos) { + $this->semValue = array(null, $this->semStack[$stackPos-(1-1)]); + }, + 272 => function ($stackPos) { + $this->semValue = null; + }, + 273 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 274 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 275 => function ($stackPos) { + $this->semValue = 0; + }, + 276 => function ($stackPos) { + $this->semValue = 0; + }, + 277 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 278 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 279 => function ($stackPos) { + $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; + }, + 280 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + }, + 281 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + }, + 282 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + }, + 283 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_STATIC; + }, + 284 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, + 285 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, + 286 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 287 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 288 => function ($stackPos) { + $this->semValue = new Node\VarLikeIdentifier(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 289 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 290 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 291 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 292 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 293 => function ($stackPos) { + $this->semValue = array(); + }, + 294 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 295 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 296 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 297 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 298 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 299 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 300 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 301 => function ($stackPos) { + $this->semValue = new Expr\Clone_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 302 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 303 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 304 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 305 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 306 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 307 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 308 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 309 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 310 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 311 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 312 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 313 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 314 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 315 => function ($stackPos) { + $this->semValue = new Expr\PostInc($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 316 => function ($stackPos) { + $this->semValue = new Expr\PreInc($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 317 => function ($stackPos) { + $this->semValue = new Expr\PostDec($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 318 => function ($stackPos) { + $this->semValue = new Expr\PreDec($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 319 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 320 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 321 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 322 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 323 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 324 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 325 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 326 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 327 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 328 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 329 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 330 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 331 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 332 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 333 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 334 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 335 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 336 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 337 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 338 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 339 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 340 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 341 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 342 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 343 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 344 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 345 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 346 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 347 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 348 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 349 => function ($stackPos) { + $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 350 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 351 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 352 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(5-1)], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 353 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(4-1)], null, $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 354 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 355 => function ($stackPos) { + $this->semValue = new Expr\Isset_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 356 => function ($stackPos) { + $this->semValue = new Expr\Empty_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 357 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 358 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 359 => function ($stackPos) { + $this->semValue = new Expr\Eval_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 360 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 361 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 362 => function ($stackPos) { + $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 363 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos-(2-1)]); + $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos-(2-2)], $attrs); + }, + 364 => function ($stackPos) { + $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 365 => function ($stackPos) { + $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 366 => function ($stackPos) { + $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 367 => function ($stackPos) { + $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 368 => function ($stackPos) { + $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 369 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = strtolower($this->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $this->semValue = new Expr\Exit_($this->semStack[$stackPos-(2-2)], $attrs); + }, + 370 => function ($stackPos) { + $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 371 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 372 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 373 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 374 => function ($stackPos) { + $this->semValue = new Expr\ShellExec($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 375 => function ($stackPos) { + $this->semValue = new Expr\Print_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 376 => function ($stackPos) { + $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 377 => function ($stackPos) { + $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 378 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(10-2)], 'params' => $this->semStack[$stackPos-(10-4)], 'uses' => $this->semStack[$stackPos-(10-6)], 'returnType' => $this->semStack[$stackPos-(10-7)], 'stmts' => $this->semStack[$stackPos-(10-9)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + }, + 379 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(11-3)], 'params' => $this->semStack[$stackPos-(11-5)], 'uses' => $this->semStack[$stackPos-(11-7)], 'returnType' => $this->semStack[$stackPos-(11-8)], 'stmts' => $this->semStack[$stackPos-(11-10)]], $this->startAttributeStack[$stackPos-(11-1)] + $this->endAttributes); + }, + 380 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 381 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 382 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(2-2)], null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 383 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 384 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_LONG; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(4-3)], $attrs); + }, + 385 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_SHORT; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(3-2)], $attrs); + }, + 386 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 387 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(4-1)][0] === "'" || ($this->semStack[$stackPos-(4-1)][1] === "'" && ($this->semStack[$stackPos-(4-1)][0] === 'b' || $this->semStack[$stackPos-(4-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED); + $this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(4-1)]), $attrs), $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 388 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 389 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 390 => function ($stackPos) { + $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos-(7-3)], 'implements' => $this->semStack[$stackPos-(7-4)], 'stmts' => $this->semStack[$stackPos-(7-6)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes), $this->semStack[$stackPos-(7-2)]); + $this->checkClass($this->semValue[0], -1); + }, + 391 => function ($stackPos) { + $this->semValue = new Expr\New_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 392 => function ($stackPos) { + list($class, $ctorArgs) = $this->semStack[$stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 393 => function ($stackPos) { + $this->semValue = array(); + }, + 394 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-3)]; + }, + 395 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 396 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 397 => function ($stackPos) { + $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos-(2-2)], $this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 398 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 399 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 400 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos-(6-1)], $this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 401 => function ($stackPos) { + $this->semValue = $this->fixupPhp5StaticPropCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 402 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 403 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 404 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 405 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 406 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 407 => function ($stackPos) { + $this->semValue = new Name\FullyQualified($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 408 => function ($stackPos) { + $this->semValue = new Name\Relative($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 409 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 410 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 411 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 412 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 413 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 414 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 415 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 416 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 417 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 418 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 419 => function ($stackPos) { + $this->semValue = null; + }, + 420 => function ($stackPos) { + $this->semValue = null; + }, + 421 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 422 => function ($stackPos) { + $this->semValue = array(); + }, + 423 => function ($stackPos) { + $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`', false), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + }, + 424 => function ($stackPos) { + foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', false); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 425 => function ($stackPos) { + $this->semValue = array(); + }, + 426 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 427 => function ($stackPos) { + $this->semValue = $this->parseLNumber($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, true); + }, + 428 => function ($stackPos) { + $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos-(1-1)]), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 429 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED); + $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(1-1)], false), $attrs); + }, + 430 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 431 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 432 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 433 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 434 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 435 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 436 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 437 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 438 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], false); + }, + 439 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(2-1)], '', $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(2-2)] + $this->endAttributeStack[$stackPos-(2-2)], false); + }, + 440 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 441 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 442 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 443 => function ($stackPos) { + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 444 => function ($stackPos) { + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 445 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 446 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 447 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 448 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 449 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 450 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 451 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 452 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 453 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 454 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 455 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 456 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 457 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 458 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 459 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 460 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 461 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 462 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 463 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 464 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 465 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 466 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 467 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 468 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 469 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 470 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 471 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 472 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 473 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 474 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 475 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(5-1)], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 476 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(4-1)], null, $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 477 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 478 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 479 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 480 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 481 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 482 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 483 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + foreach ($this->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos-(3-2)], $attrs); + }, + 484 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); + }, + 485 => function ($stackPos) { + $this->semValue = array(); + }, + 486 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 487 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 488 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 489 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 490 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 491 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 492 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 493 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 494 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 495 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 496 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 497 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 498 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 499 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 500 => function ($stackPos) { + $this->semValue = new Expr\MethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 501 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 502 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 503 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 504 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 505 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 506 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 507 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 508 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 509 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 510 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 511 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 512 => function ($stackPos) { + $var = substr($this->semStack[$stackPos-(1-1)], 1); $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes) : $var; + }, + 513 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 514 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(6-1)], $this->semStack[$stackPos-(6-5)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 515 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 516 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 517 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 518 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 519 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 520 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 521 => function ($stackPos) { + $this->semValue = null; + }, + 522 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 523 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 524 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 525 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 526 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; + }, + 527 => function ($stackPos) { + $this->semValue = new Expr\List_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 528 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 529 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 530 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 531 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 532 => function ($stackPos) { + $this->semValue = null; + }, + 533 => function ($stackPos) { + $this->semValue = array(); + }, + 534 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 535 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 536 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 537 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 538 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 539 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-1)], true, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 540 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 541 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 542 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 543 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 544 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 545 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); + }, + 546 => function ($stackPos) { + $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 547 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 548 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 549 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 550 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 551 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 552 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 553 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-4)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 554 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 555 => function ($stackPos) { + $this->semValue = new Scalar\String_($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 556 => function ($stackPos) { + $this->semValue = $this->parseNumString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 557 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + ]; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php b/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php new file mode 100644 index 0000000..75c8305 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php @@ -0,0 +1,2475 @@ +'", + "T_IS_GREATER_OR_EQUAL", + "T_SL", + "T_SR", + "'+'", + "'-'", + "'.'", + "'*'", + "'/'", + "'%'", + "'!'", + "T_INSTANCEOF", + "'~'", + "T_INC", + "T_DEC", + "T_INT_CAST", + "T_DOUBLE_CAST", + "T_STRING_CAST", + "T_ARRAY_CAST", + "T_OBJECT_CAST", + "T_BOOL_CAST", + "T_UNSET_CAST", + "'@'", + "T_POW", + "'['", + "T_NEW", + "T_CLONE", + "T_EXIT", + "T_IF", + "T_ELSEIF", + "T_ELSE", + "T_ENDIF", + "T_LNUMBER", + "T_DNUMBER", + "T_STRING", + "T_STRING_VARNAME", + "T_VARIABLE", + "T_NUM_STRING", + "T_INLINE_HTML", + "T_ENCAPSED_AND_WHITESPACE", + "T_CONSTANT_ENCAPSED_STRING", + "T_ECHO", + "T_DO", + "T_WHILE", + "T_ENDWHILE", + "T_FOR", + "T_ENDFOR", + "T_FOREACH", + "T_ENDFOREACH", + "T_DECLARE", + "T_ENDDECLARE", + "T_AS", + "T_SWITCH", + "T_ENDSWITCH", + "T_CASE", + "T_DEFAULT", + "T_BREAK", + "T_CONTINUE", + "T_GOTO", + "T_FUNCTION", + "T_FN", + "T_CONST", + "T_RETURN", + "T_TRY", + "T_CATCH", + "T_FINALLY", + "T_THROW", + "T_USE", + "T_INSTEADOF", + "T_GLOBAL", + "T_STATIC", + "T_ABSTRACT", + "T_FINAL", + "T_PRIVATE", + "T_PROTECTED", + "T_PUBLIC", + "T_VAR", + "T_UNSET", + "T_ISSET", + "T_EMPTY", + "T_HALT_COMPILER", + "T_CLASS", + "T_TRAIT", + "T_INTERFACE", + "T_EXTENDS", + "T_IMPLEMENTS", + "T_OBJECT_OPERATOR", + "T_LIST", + "T_ARRAY", + "T_CALLABLE", + "T_CLASS_C", + "T_TRAIT_C", + "T_METHOD_C", + "T_FUNC_C", + "T_LINE", + "T_FILE", + "T_START_HEREDOC", + "T_END_HEREDOC", + "T_DOLLAR_OPEN_CURLY_BRACES", + "T_CURLY_OPEN", + "T_PAAMAYIM_NEKUDOTAYIM", + "T_NAMESPACE", + "T_NS_C", + "T_DIR", + "T_NS_SEPARATOR", + "T_ELLIPSIS", + "';'", + "'{'", + "'}'", + "'('", + "')'", + "'`'", + "']'", + "'\"'", + "'$'" + ); + + protected $tokenToSymbol = array( + 0, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 54, 157, 159, 158, 53, 36, 159, + 153, 154, 51, 48, 7, 49, 50, 52, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 30, 150, + 42, 15, 44, 29, 66, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 68, 159, 156, 35, 159, 155, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 151, 34, 152, 56, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, + 159, 159, 159, 159, 159, 159, 1, 2, 3, 4, + 5, 6, 8, 9, 10, 11, 12, 13, 14, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 31, 32, 33, 37, 38, 39, 40, 41, + 43, 45, 46, 47, 55, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 67, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 159, + 159, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 159, 159, + 159, 159, 159, 159, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149 + ); + + protected $action = array( + 607, 608, 609, 610, 611, 685, 612, 613, 614, 650, + 651, 0, 32, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115,-32767,-32767,-32767,-32767, + 94, 95, 96, 97, 98,-32766,-32766,-32766, 687, 491, + -497, 904, 905, 906, 903, 902, 901, 904, 905, 906, + 903, 902, 901, 615, 938, 940,-32766, 9,-32766,-32766, + -32766,-32766,-32766,-32766,-32766,-32766,-32766, 616, 617, 618, + 619, 620, 621, 622, 333, 1104, 683,-32766,-32766,-32766, + 846, 1103, 119, 623, 624, 625, 626, 627, 628, 629, + 630, 631, 632, 633, 653, 654, 655, 656, 657, 645, + 646, 647, 675, 648, 649, 634, 635, 636, 637, 638, + 639, 640, 677, 678, 679, 680, 681, 682, 641, 642, + 643, 644, 674, 665, 663, 664, 660, 661, 402, 652, + 658, 659, 666, 667, 669, 668, 670, 671, 45, 46, + 421, 47, 48, 662, 673, 672, 27, 49, 50, 233, + 51,-32766,-32766,-32766, 96, 97, 98, 24,-32766,-32766, + -32766, -458, 261, 121, 1023,-32766,-32766,-32766, 1091, 1073, + -32766,-32766,-32766, 1039,-32766,-32766,-32766,-32766,-32766,-32766, + -496,-32766,-32766,-32766, 52, 53,-32766, -497,-32766,-32766, + 54, 687, 55, 231, 232, 56, 57, 58, 59, 60, + 61, 62, 63, 1016, 24, 242, 64, 369,-32766,-32766, + -32766, 226, 1040, 1041, 423, 1076, 1073, -493, 880, 508, + 1039, 436, 1023, -458, 768, 1073, 239, 333, -500,-32766, + -500,-32766,-32766,-32766,-32766, 856, 253, -458, 276, 378, + 372, 786, 68, 1073, -458, 685, -461, 278, 1126, 403, + 289, 1127, 288, 99, 100, 101, 303, 252, 433, 434, + 822,-32766, 69, 261, 237, 850, 851, 435, 436, 102, + 1045, 1046, 1047, 1048, 1042, 1043, 256, 1016, -456, -456, + 306, 444, 1049, 1044, 375, 133, 561, -239, 363, 66, + 237, 268, 692, 273, 278, 422, -137, -137, -137, -4, + 768, 1073, 310, 278, 1035, 757, 687, 362, 37, 20, + 424, -137, 425, -137, 426, -137, 427, -137, 127, 428, + -295, 278, -295, 38, 39, 370, 371, -496, 271, 40, + 429, 277, 687, 65, 261, 1016, 302, 896, 430, 431, + -456, -456, 333, -494, 432, 44, 42, 743, 791, 373, + 374, -457, -234, 562, -456, -456, 375,-32766,-32766,-32766, + 882, -456, -456, 124, -493, 75, 850, 851, 333, -273, + -260, 422, 768, 770, 576, -137, 261, 125,-32766, 278, + 823, 757, 857, 1073, 37, 20, 424, 240, 425, -178, + 426, 589, 427, 393, 503, 428, 687, 235, 241, 38, + 39, 370, 371, 125, 354, 40, 429, 260, 259, 65, + 267, 687, 302, -457, 430, 431, -296, -177, -296, 24, + 432, 305, 365, 700, 791, 373, 374, -457, 120, 118, + 24, 1073, 30, 366, -457, 1039, -460, 850, 851, 687, + 367, 691, 1073, 422, 291, 768, 1039, 333, -83, 770, + 576, -4, 467, 757, 126, 368, 37, 20, 424, -92, + 425, 278, 426, 444, 427, 1016, 375, 428, -219, -219, + -219, 38, 39, 370, 371, 333, 1016, 40, 429, 850, + 851, 65, 435, 436, 302, 236, 430, 431, 225, 708, + -494, 709, 432, 435, 436, 743, 791, 373, 374, 690, + 387, 136, 1117, 578, 68, 413, 238, 8, 33, 278, + 1053, 227, 708, 687, 709, 68, 422, -260, 535, 21, + 278, 770, 576, -219, 550, 551, 757, 687, 116, 37, + 20, 424, 117, 425, 358, 426, -178, 427, 132, 328, + 428, -218, -218, -218, 38, 39, 370, 371, 687, 333, + 40, 429, 122, 768, 65, 383, 384, 302, 123, 430, + 431, 29, 234, 333, -177, 432, 528, 529, 743, 791, + 373, 374, 129, 850, 851, 135, 76, 77, 78, 1092, + 881, 599, 582, 254, 333, 137, 138, 782, 590, 593, + 293, 767, 131, 252, 770, 576, -218, 31, 102, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 43, 252, 422, 558, 768, 687, 690,-32766, + 471, 130, 476, 685, 757, 102, 553, 37, 20, 424, + 526, 425, 688, 426, 272, 427, 910, 1016, 428, 792, + 1128, 793, 38, 39, 370, 583, 269, 570, 40, 429, + 536, 1052, 65, 275, 1055, 302, -415, 541, 270, -81, + 10, 391, 768, 432, 542, 554, 784, 594, 5, 0, + 12, 577, 0, 0, 304, 0, 0, 0, 0, 336, + 342, 0, 0, 0, 0, 0, 0, 422, 0, 0, + 0, 584, 770, 576, 0, 0, 0, 757, 0, 0, + 37, 20, 424, 343, 425, 0, 426, 0, 427, 768, + 0, 428, 0, 0, 0, 38, 39, 370, 347, 387, + 473, 40, 429, 359, 360, 65, 744, 35, 302, 36, + 597, 598, 748, 422, 825, 809, 432, 816, 587, 876, + 877, 806, 817, 757, 746, 804, 37, 20, 424, 885, + 425, 888, 426, 889, 427, 768, 886, 428, 887, 893, + -485, 38, 39, 370, 579, 770, 576, 40, 429, 581, + 585, 65, 586, 588, 302, 592, 286, 287, 352, 353, + 422, 580, 432, 1123, 591, 1125, 703, 790, 702, 712, + 757, 789, 713, 37, 20, 424, 710, 425, 1124, 426, + 788, 427, 768, 1004, 428, 711, 777, 785, 38, 39, + 370, 808, 576, -483, 40, 429, 775, 814, 65, 815, + 1122, 302, 1074, 1067, 1081, 1086, 422, 1089, -237, 432, + -461, -460, -459, 23, 25, 28, 757, 34, 41, 37, + 20, 424, 67, 425, 70, 426, 71, 427, 72, 73, + 428, 74, 128, 134, 38, 39, 370, 139, 770, 576, + 40, 429, 229, 230, 65, 246, 247, 302, 248, 249, + 250, 251, 290, 422, 355, 432, 357, -427, -235, -234, + 14, 15, 16, 757, 17, 19, 37, 20, 424, 325, + 425, 404, 426, 406, 427, 409, 411, 428, 412, 419, + 567, 38, 39, 370, 770, 576, 1027, 40, 429, 977, + 1037, 65, 858, 1008, 302,-32766,-32766,-32766, -92, 13, + 18, 22, 432, 263, 324, 501, 522, 569, 981, 978, + 0, 994, 0, 1036, 1065, 1066,-32766, 1080,-32766,-32766, + -32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767,-32767, 1120, + 532, 770, 576, 1054 + ); + + protected $actionCheck = array( + 2, 3, 4, 5, 6, 78, 8, 9, 10, 11, + 12, 0, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 8, 9, 10, 78, 79, + 7, 114, 115, 116, 117, 118, 119, 114, 115, 116, + 117, 118, 119, 55, 57, 58, 29, 7, 31, 32, + 33, 34, 35, 36, 8, 9, 10, 69, 70, 71, + 72, 73, 74, 75, 114, 1, 78, 8, 9, 10, + 1, 7, 13, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 30, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 2, 3, + 4, 5, 6, 145, 146, 147, 7, 11, 12, 36, + 14, 8, 9, 10, 48, 49, 50, 68, 8, 9, + 10, 68, 29, 7, 1, 8, 9, 10, 1, 80, + 8, 9, 29, 84, 31, 32, 33, 34, 35, 29, + 7, 31, 32, 33, 48, 49, 29, 154, 31, 32, + 54, 78, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 114, 68, 69, 70, 71, 8, 9, + 10, 13, 76, 77, 78, 1, 80, 7, 1, 49, + 84, 132, 1, 130, 1, 80, 7, 114, 154, 29, + 156, 31, 32, 33, 34, 1, 7, 144, 7, 103, + 104, 1, 153, 80, 151, 78, 153, 158, 78, 151, + 114, 81, 7, 51, 52, 53, 7, 55, 122, 123, + 30, 8, 149, 29, 36, 132, 133, 131, 132, 67, + 134, 135, 136, 137, 138, 139, 140, 114, 68, 68, + 7, 145, 146, 147, 148, 13, 78, 154, 125, 153, + 36, 155, 1, 157, 158, 72, 73, 74, 75, 0, + 1, 80, 7, 158, 1, 82, 78, 7, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 151, 96, + 103, 158, 105, 100, 101, 102, 103, 154, 111, 106, + 107, 68, 78, 110, 29, 114, 113, 120, 115, 116, + 130, 130, 114, 7, 121, 68, 68, 124, 125, 126, + 127, 68, 154, 145, 144, 144, 148, 8, 9, 10, + 152, 151, 151, 30, 154, 151, 132, 133, 114, 152, + 7, 72, 1, 150, 151, 152, 29, 149, 29, 158, + 150, 82, 154, 80, 85, 86, 87, 36, 89, 7, + 91, 151, 93, 130, 1, 96, 78, 36, 36, 100, + 101, 102, 103, 149, 105, 106, 107, 130, 130, 110, + 111, 78, 113, 130, 115, 116, 103, 7, 105, 68, + 121, 144, 7, 124, 125, 126, 127, 144, 151, 151, + 68, 80, 7, 7, 151, 84, 153, 132, 133, 78, + 7, 150, 80, 72, 145, 1, 84, 114, 30, 150, + 151, 152, 83, 82, 151, 7, 85, 86, 87, 154, + 89, 158, 91, 145, 93, 114, 148, 96, 97, 98, + 99, 100, 101, 102, 103, 114, 114, 106, 107, 132, + 133, 110, 131, 132, 113, 36, 115, 116, 95, 103, + 154, 105, 121, 131, 132, 124, 125, 126, 127, 80, + 148, 13, 83, 151, 153, 103, 36, 105, 13, 158, + 141, 13, 103, 78, 105, 153, 72, 154, 73, 74, + 158, 150, 151, 152, 73, 74, 82, 78, 15, 85, + 86, 87, 15, 89, 148, 91, 154, 93, 98, 99, + 96, 97, 98, 99, 100, 101, 102, 103, 78, 114, + 106, 107, 15, 1, 110, 103, 104, 113, 15, 115, + 116, 142, 143, 114, 154, 121, 108, 109, 124, 125, + 126, 127, 15, 132, 133, 15, 8, 9, 10, 154, + 150, 151, 30, 30, 114, 15, 15, 36, 30, 30, + 34, 30, 30, 55, 150, 151, 152, 29, 67, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 68, 55, 72, 75, 1, 78, 80, 83, + 83, 68, 87, 78, 82, 67, 92, 85, 86, 87, + 111, 89, 78, 91, 112, 93, 80, 114, 96, 125, + 81, 125, 100, 101, 102, 30, 128, 90, 106, 107, + 88, 141, 110, 128, 141, 113, 144, 94, 129, 95, + 95, 95, 1, 121, 97, 97, 149, 152, 144, -1, + 144, 151, -1, -1, 144, -1, -1, -1, -1, 148, + 148, -1, -1, -1, -1, -1, -1, 72, -1, -1, + -1, 30, 150, 151, -1, -1, -1, 82, -1, -1, + 85, 86, 87, 148, 89, -1, 91, -1, 93, 1, + -1, 96, -1, -1, -1, 100, 101, 102, 148, 148, + 148, 106, 107, 148, 148, 110, 152, 150, 113, 150, + 150, 150, 150, 72, 150, 150, 121, 150, 30, 150, + 150, 150, 150, 82, 150, 150, 85, 86, 87, 150, + 89, 150, 91, 150, 93, 1, 150, 96, 150, 150, + 153, 100, 101, 102, 151, 150, 151, 106, 107, 151, + 151, 110, 151, 151, 113, 151, 151, 151, 151, 151, + 72, 151, 121, 152, 30, 152, 152, 152, 152, 152, + 82, 152, 152, 85, 86, 87, 152, 89, 152, 91, + 152, 93, 1, 152, 96, 152, 152, 152, 100, 101, + 102, 150, 151, 153, 106, 107, 152, 152, 110, 152, + 152, 113, 152, 152, 152, 152, 72, 152, 154, 121, + 153, 153, 153, 153, 153, 153, 82, 153, 153, 85, + 86, 87, 153, 89, 153, 91, 153, 93, 153, 153, + 96, 153, 153, 153, 100, 101, 102, 153, 150, 151, + 106, 107, 153, 153, 110, 153, 153, 113, 153, 153, + 153, 153, 153, 72, 153, 121, 153, 155, 154, 154, + 154, 154, 154, 82, 154, 154, 85, 86, 87, 154, + 89, 154, 91, 154, 93, 154, 154, 96, 154, 154, + 154, 100, 101, 102, 150, 151, 154, 106, 107, 154, + 154, 110, 154, 154, 113, 8, 9, 10, 154, 154, + 154, 154, 121, 154, 154, 154, 154, 154, 154, 154, + -1, 155, -1, 156, 156, 156, 29, 156, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 156, + 156, 150, 151, 157 + ); + + protected $actionBase = array( + 0, 223, 299, 371, 444, 303, 208, 618, -2, -2, + -73, -2, -2, 625, 718, 718, 764, 718, 552, 671, + 811, 811, 811, 228, 113, 113, 113, 254, 361, -40, + 361, 333, 449, 470, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, + 291, 291, 230, 393, 495, 779, 784, 781, 776, 775, + 780, 785, 498, 678, 680, 562, 681, 682, 683, 685, + 782, 804, 777, 783, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 253, 69, 162, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 349, 349, 349, 157, + 210, 150, 200, 211, 143, 27, 917, 917, 917, 917, + 917, -16, -16, -16, -16, 351, 351, 362, 217, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 163, 313, 106, 106, 133, 133, 133, 133, + 133, 133, 221, 305, 234, 347, 369, 523, 806, 167, + 167, 441, 93, 283, 202, 202, 202, 386, 547, 533, + 533, 533, 533, 419, 419, 533, 533, 170, 214, 74, + 211, 211, 277, 211, 211, 211, 409, 409, 409, 452, + 318, 352, 546, 318, 619, 640, 577, 675, 578, 677, + 278, 585, 145, 586, 145, 145, 145, 458, 445, 451, + 774, 291, 522, 291, 291, 291, 291, 722, 291, 291, + 291, 291, 291, 291, 98, 291, 79, 430, 230, 240, + 240, 556, 240, 452, 538, 263, 635, 410, 425, 538, + 538, 538, 636, 637, 336, 363, 198, 638, 382, 402, + 173, 33, 549, 549, 555, 555, 566, 551, 549, 549, + 549, 549, 549, 690, 690, 555, 548, 555, 566, 695, + 555, 551, 551, 555, 555, 549, 555, 690, 551, 156, + 415, 249, 273, 551, 551, 426, 528, 549, 535, 535, + 433, 555, 219, 555, 139, 539, 690, 690, 539, 229, + 551, 231, 590, 591, 529, 527, 553, 245, 553, 553, + 300, 529, 553, 551, 553, 448, 50, 548, 295, 553, + 11, 699, 701, 418, 703, 694, 705, 731, 706, 530, + 524, 526, 719, 720, 708, 692, 691, 561, 582, 513, + 517, 534, 554, 689, 581, 531, 531, 531, 554, 687, + 531, 531, 531, 531, 531, 531, 531, 531, 787, 540, + 545, 723, 537, 541, 576, 543, 623, 520, 582, 582, + 584, 732, 786, 564, 722, 762, 709, 587, 557, 741, + 725, 525, 542, 565, 726, 727, 745, 765, 628, 513, + 766, 641, 563, 643, 582, 644, 531, 670, 617, 788, + 789, 688, 791, 736, 747, 749, 580, 645, 569, 803, + 646, 768, 629, 631, 589, 737, 684, 751, 647, 752, + 754, 649, 592, 572, 734, 573, 733, 272, 729, 632, + 650, 654, 656, 658, 661, 710, 594, 738, 544, 740, + 735, 595, 597, 560, 663, 488, 599, 570, 571, 600, + 714, 558, 550, 601, 602, 769, 664, 728, 604, 665, + 756, 574, 581, 536, 532, 575, 567, 634, 755, 559, + 605, 609, 611, 613, 674, 616, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 136, 136, 136, 136, -2, -2, -2, + 0, 0, -2, 0, 0, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, + 568, 568, -3, 568, 568, -3, 568, 568, 568, 568, + 568, 568, 568, 202, 202, 202, 202, 318, 318, 318, + -67, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, -67, 202, 202, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 419, 419, + 419, 145, 145, 318, 0, 0, 0, 0, 0, 549, + 419, 318, 318, 318, 318, 0, 0, 318, 318, 548, + 145, 0, 0, 0, 0, 0, 0, 0, 549, 549, + 549, 548, 0, 549, 419, 0, 240, 291, 440, 440, + 440, 440, 0, 549, 0, 549, 0, 0, 0, 0, + 0, 0, 551, 0, 690, 0, 0, 0, 0, 555, + 0, 0, 0, 0, 0, 0, 0, 0, 548, 0, + 0, 0, 0, 548, 0, 0, 531, 0, 564, 0, + 0, 531, 531, 531, 564, 564, 0, 0, 0, 564 + ); + + protected $actionDefault = array( + 3,32767,32767,32767,32767,32767,32767,32767,32767, 92, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767, 510, 510, 510, 94, 499,32767, + 499,32767,32767,32767, 314, 314, 314,32767, 454, 454, + 454, 454, 454, 454, 454,32767,32767,32767,32767,32767, + 394,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767, 92,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767, 506,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767, 377, 378, 380, 381, 313, 455, + 509, 259, 505, 312, 130, 270, 261, 211, 243, 310, + 134, 342, 395, 344, 393, 397, 343, 319, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 317, 318, 396, 398, 399, 374, 373, 372, 340, + 316, 341, 345, 316, 347, 346, 363, 364, 361, 362, + 365, 366, 367, 368, 369,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767, 94, + 32767,32767,32767, 293, 354, 355, 250, 250, 250, 250, + 250, 250,32767, 250,32767, 250,32767,32767,32767,32767, + 32767,32767, 448, 371, 349, 350, 348,32767, 426,32767, + 32767,32767,32767,32767, 428,32767, 92,32767,32767,32767, + 337, 339, 420, 508, 320, 507,32767,32767, 94, 414, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 423,32767,32767, 92,32767,32767, 92, 174, 230, 232, + 179,32767, 431,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767,32767, 414, 359, 517,32767, 456,32767, 351, + 352, 353,32767,32767, 456, 456, 456,32767, 456,32767, + 456, 456,32767,32767,32767,32767,32767, 179,32767,32767, + 32767,32767, 94, 429, 429, 92, 92, 92, 92, 424, + 32767, 179, 179,32767,32767,32767,32767,32767, 179, 91, + 91, 91, 91, 179, 179, 91, 194,32767, 192, 192, + 91,32767, 93,32767, 93, 196,32767, 470, 196, 91, + 179, 91, 216, 216, 405, 181, 252, 93, 252, 252, + 93, 405, 252, 179, 252, 91, 91,32767, 91, 252, + 32767,32767,32767, 85,32767,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767, 416,32767, + 436,32767, 449, 468,32767, 357, 358, 360,32767, 458, + 382, 383, 384, 385, 386, 387, 388, 390,32767, 419, + 32767,32767,32767, 87, 121, 269,32767, 515, 87, 417, + 32767, 515,32767,32767,32767,32767,32767,32767,32767,32767, + 32767,32767, 87, 87,32767,32767,32767,32767,32767, 495, + 32767, 516,32767, 456, 418,32767, 356, 432, 475,32767, + 32767, 457,32767,32767,32767,32767, 87,32767,32767,32767, + 32767,32767,32767,32767,32767,32767, 436,32767,32767,32767, + 32767,32767,32767,32767, 456,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 456,32767,32767, 242,32767,32767,32767, 309,32767,32767, + 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, + 32767, 85, 60,32767, 289,32767,32767,32767,32767,32767, + 32767,32767,32767,32767,32767,32767, 136, 136, 3, 272, + 3, 272, 136, 136, 136, 272, 272, 136, 136, 136, + 136, 136, 136, 136, 169, 224, 227, 216, 216, 281, + 136, 136 + ); + + protected $goto = array( + 171, 144, 144, 144, 171, 152, 153, 152, 155, 187, + 172, 168, 168, 168, 168, 169, 169, 169, 169, 169, + 169, 169, 164, 165, 166, 167, 184, 182, 185, 445, + 446, 334, 447, 450, 451, 452, 453, 454, 455, 456, + 457, 924, 141, 145, 146, 147, 170, 148, 149, 143, + 150, 151, 154, 181, 183, 186, 206, 209, 211, 212, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 244, 245, 264, 265, 266, 339, 340, 341, 496, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 156, 203, 157, 173, 174, + 175, 207, 176, 158, 159, 160, 177, 161, 208, 142, + 204, 162, 178, 205, 179, 180, 163, 563, 210, 463, + 210, 516, 516, 1038, 572, 1038, 1038, 1038, 1038, 1038, + 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 468, 468, + 468, 514, 537, 468, 297, 489, 521, 489, 498, 274, + 533, 534, 698, 483, 258, 468, 448, 448, 448, 725, + 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, + 448, 448, 448, 449, 449, 449, 699, 449, 449, 449, + 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, + 1114, 1114, 734, 725, 899, 725, 315, 319, 475, 499, + 500, 502, 1083, 1084, 468, 468, 760, 1114, 761, 900, + 482, 506, 468, 468, 468, 329, 330, 686, 481, 545, + 495, 332, 510, 596, 523, 525, 294, 469, 538, 556, + 559, 835, 566, 574, 831, 765, 729, 717, 864, 494, + 807, 868, 490, 860, 716, 716, 810, 697, 1013, 1105, + 726, 726, 726, 728, 715, 840, 1093, 800, 824, 805, + 805, 803, 805, 595, 313, 460, 833, 828, 459, 3, + 4, 907, 733, 539, 1009, 487, 317, 461, 459, 497, + 892, 575, 972, 474, 843, 557, 890, 1129, 484, 485, + 505, 517, 519, 520, 568, 801, 801, 801, 801, 465, + 855, 795, 802, 1002, 787, 405, 1003, 799, 327, 571, + 356, 1082, 530, 1014, 848, 346, 540, 350, 11, 337, + 337, 280, 281, 283, 493, 344, 284, 345, 285, 348, + 524, 351, 1015, 1069, 1113, 1113, 543, 301, 298, 299, + 721, 560, 838, 838, 1100, 295, 865, 718, 600, 323, + 544, 1113, 1010, 1017, 511, 1005, 869, 849, 849, 849, + 849, 849, 849, 1017, 849, 849, 849, 720, 730, 1116, + 714, 812, 849, 1088, 1088, 909, 465, 398, 513, 414, + 1017, 1017, 1017, 1017, 0, 1079, 1017, 1017, 0, 701, + 0, 0, 0, 0, 0, 1079, 0, 0, 0, 0, + 0, 773, 1090, 1090, 774, 706, 0, 756, 751, 752, + 766, 0, 707, 753, 704, 754, 755, 705, 0, 759, + 0, 1075, 0, 0, 0, 0, 0, 1012, 0, 0, + 0, 480, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 867, 0, 1077, 1077, 867, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 462, 478, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 462, + 0, 478, 0, 0, 316, 0, 0, 466, 386, 0, + 388, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 724, 0, 1121 + ); + + protected $gotoCheck = array( + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 63, 56, 10, + 56, 86, 86, 86, 8, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 10, 10, + 10, 46, 46, 10, 80, 85, 73, 85, 97, 134, + 73, 73, 17, 10, 134, 10, 135, 135, 135, 26, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 137, 137, 137, 18, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 148, 148, 36, 26, 111, 26, 49, 49, 49, 49, + 49, 49, 141, 141, 10, 10, 55, 148, 55, 111, + 10, 10, 10, 10, 10, 69, 69, 5, 39, 69, + 2, 69, 2, 39, 39, 39, 69, 10, 39, 39, + 39, 39, 39, 39, 39, 13, 14, 14, 14, 10, + 40, 14, 136, 94, 26, 26, 14, 16, 92, 146, + 26, 26, 26, 26, 26, 14, 143, 14, 16, 16, + 16, 16, 16, 16, 52, 16, 16, 16, 75, 37, + 37, 14, 14, 54, 14, 53, 65, 65, 75, 7, + 7, 7, 118, 65, 88, 7, 7, 12, 65, 65, + 68, 68, 68, 68, 68, 75, 75, 75, 75, 12, + 90, 75, 75, 67, 67, 65, 67, 76, 76, 76, + 89, 139, 24, 92, 91, 56, 56, 56, 65, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 92, 92, 147, 147, 12, 20, 80, 80, + 30, 12, 85, 85, 85, 11, 96, 28, 82, 19, + 23, 147, 127, 63, 15, 124, 99, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 15, 32, 147, + 15, 79, 63, 8, 8, 114, 12, 71, 72, 122, + 63, 63, 63, 63, -1, 97, 63, 63, -1, 13, + -1, -1, -1, -1, -1, 97, -1, -1, -1, -1, + -1, 63, 97, 97, 63, 13, -1, 13, 13, 13, + 13, -1, 13, 13, 13, 13, 13, 13, -1, 13, + -1, 97, -1, -1, -1, -1, -1, 12, -1, -1, + -1, 8, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 97, -1, 97, 97, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 8, 8, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, + -1, 8, -1, -1, 8, -1, -1, 8, 8, -1, + 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8, -1, 8 + ); + + protected $gotoBase = array( + 0, 0, -358, 0, 0, 207, 0, 274, 114, 0, + -148, 54, 10, 94, -144, -40, 245, 150, 174, 48, + 70, 0, 0, -3, 25, 0, -108, 0, 44, 0, + 52, 0, 3, -23, 0, 0, 183, -331, 0, -359, + 221, 0, 0, 0, 0, 0, 106, 0, 0, 157, + 0, 0, 227, 45, 47, 191, 90, 0, 0, 0, + 0, 0, 0, 111, 0, -95, 0, -26, 43, -193, + 0, -12, -20, -435, 0, 26, 37, 0, 0, 4, + -259, 0, 20, 0, 0, 117, -104, 0, 31, 55, + 46, 53, -64, 0, 216, 0, 40, 143, 0, -10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -34, 0, 0, 7, 0, 0, 0, 30, 0, + 0, 0, -32, 0, -9, 0, 0, -5, 0, 0, + 0, 0, 0, 0, -119, -69, 217, -52, 0, 51, + 0, -102, 0, 226, 0, 0, 223, 77, -67, 0, + 0 + ); + + protected $gotoDefault = array( + -32768, 420, 603, 2, 604, 676, 684, 548, 437, 573, + 438, 464, 335, 758, 913, 778, 740, 741, 742, 320, + 361, 311, 318, 531, 518, 410, 727, 381, 719, 407, + 722, 380, 731, 140, 549, 416, 735, 1, 737, 470, + 769, 308, 745, 309, 552, 747, 477, 749, 750, 314, + 321, 322, 917, 486, 515, 762, 213, 479, 763, 307, + 764, 772, 331, 312, 392, 417, 326, 894, 504, 527, + 376, 395, 512, 507, 488, 1024, 797, 401, 390, 811, + 296, 819, 601, 827, 830, 439, 440, 399, 842, 400, + 853, 847, 1032, 394, 859, 382, 866, 1064, 385, 870, + 228, 873, 255, 546, 349, 878, 879, 6, 884, 564, + 565, 7, 243, 415, 908, 547, 379, 923, 364, 991, + 993, 472, 408, 1006, 389, 555, 418, 1011, 1068, 377, + 441, 396, 282, 300, 257, 442, 458, 262, 443, 397, + 1071, 1078, 338, 1094, 279, 26, 1106, 1115, 292, 492, + 509 + ); + + protected $ruleToNonTerminal = array( + 0, 1, 3, 3, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, + 13, 14, 14, 15, 15, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 20, 20, 21, 21, + 21, 21, 23, 25, 25, 19, 27, 27, 24, 29, + 29, 26, 26, 28, 28, 30, 30, 22, 31, 31, + 32, 34, 35, 35, 36, 37, 37, 39, 38, 38, + 38, 38, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 16, 16, 59, + 59, 62, 62, 61, 60, 60, 53, 64, 64, 65, + 65, 66, 66, 67, 67, 17, 18, 18, 18, 70, + 70, 70, 71, 71, 74, 74, 72, 72, 76, 77, + 77, 47, 47, 55, 55, 58, 58, 58, 57, 78, + 78, 79, 48, 48, 48, 48, 80, 80, 81, 81, + 82, 82, 45, 45, 41, 41, 83, 43, 43, 84, + 42, 42, 44, 44, 54, 54, 54, 54, 68, 68, + 87, 87, 88, 88, 88, 90, 90, 91, 91, 91, + 89, 89, 69, 69, 69, 92, 92, 93, 93, 94, + 94, 94, 50, 95, 95, 96, 51, 98, 98, 99, + 99, 100, 100, 73, 101, 101, 101, 101, 101, 106, + 106, 107, 107, 108, 108, 108, 108, 108, 109, 110, + 110, 105, 105, 102, 102, 104, 104, 112, 112, 111, + 111, 111, 111, 111, 111, 103, 113, 113, 115, 114, + 114, 52, 116, 116, 46, 46, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 123, 117, 117, 122, 122, 125, 126, 126, + 127, 128, 128, 128, 75, 75, 63, 63, 63, 118, + 118, 118, 130, 130, 119, 119, 121, 121, 121, 124, + 124, 135, 135, 135, 86, 137, 137, 137, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 49, 49, 133, 133, 133, 129, + 129, 129, 138, 138, 138, 138, 138, 138, 56, 56, + 56, 97, 97, 97, 97, 141, 140, 132, 132, 132, + 132, 132, 132, 131, 131, 131, 139, 139, 139, 139, + 85, 142, 142, 143, 143, 143, 143, 143, 143, 143, + 136, 145, 145, 144, 144, 146, 146, 146, 146, 146, + 146, 134, 134, 134, 134, 148, 149, 147, 147, 147, + 147, 147, 147, 147, 150, 150, 150, 150 + ); + + protected $ruleToLength = array( + 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 1, 1, 3, + 5, 4, 3, 4, 2, 3, 1, 1, 7, 8, + 6, 7, 2, 3, 1, 2, 3, 1, 2, 3, + 1, 1, 3, 1, 2, 1, 2, 2, 3, 1, + 3, 2, 3, 1, 3, 2, 0, 1, 1, 1, + 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, + 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, + 6, 5, 6, 3, 3, 2, 1, 1, 1, 0, + 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, + 1, 0, 1, 3, 1, 8, 7, 6, 5, 1, + 2, 2, 0, 2, 0, 2, 0, 2, 2, 1, + 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, + 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, + 1, 1, 1, 4, 0, 2, 5, 0, 2, 6, + 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, + 1, 3, 4, 6, 4, 1, 2, 1, 1, 1, + 0, 1, 0, 2, 2, 2, 4, 1, 3, 1, + 2, 2, 2, 3, 1, 1, 2, 3, 1, 1, + 3, 2, 0, 1, 4, 4, 9, 3, 1, 1, + 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, + 3, 2, 3, 1, 0, 1, 1, 3, 3, 3, + 4, 1, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 3, 2, 1, 2, 4, 2, 8, 9, + 8, 9, 7, 3, 2, 0, 4, 2, 1, 3, + 2, 2, 2, 4, 1, 1, 1, 2, 3, 1, + 1, 1, 1, 1, 0, 3, 0, 1, 1, 0, + 1, 1, 3, 3, 3, 4, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 2, 3, 3, 0, 1, 1, 3, 1, 1, + 3, 1, 1, 4, 4, 4, 1, 4, 1, 1, + 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, + 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, + 4, 3, 1, 1, 2, 1, 3, 4, 3, 0, + 1, 1, 1, 3, 1, 3, 1, 4, 2, 2, + 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, + 3, 3, 6, 3, 1, 1, 2, 1 + ); + + protected function initReduceCallbacks() { + $this->reduceCallbacks = [ + 0 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 1 => function ($stackPos) { + $this->semValue = $this->handleNamespaces($this->semStack[$stackPos-(1-1)]); + }, + 2 => function ($stackPos) { + if (is_array($this->semStack[$stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); } else { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }; + }, + 3 => function ($stackPos) { + $this->semValue = array(); + }, + 4 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 5 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 6 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 7 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 8 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 9 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 10 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 11 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 12 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 13 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 14 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 15 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 16 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 17 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 18 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 19 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 20 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 21 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 22 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 23 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 24 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 25 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 26 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 27 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 28 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 29 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 30 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 31 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 32 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 33 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 34 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 35 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 36 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 37 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 38 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 39 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 40 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 41 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 42 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 43 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 44 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 45 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 46 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 47 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 48 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 49 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 50 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 51 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 52 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 53 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 54 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 55 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 56 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 57 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 58 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 59 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 60 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 61 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 62 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 63 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 64 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 65 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 66 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 67 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 68 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 69 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 70 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 71 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 72 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 73 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 74 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 75 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 76 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 77 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 78 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 79 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 80 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 81 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 82 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 83 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 84 => function ($stackPos) { + $this->semValue = new Node\Identifier($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 85 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 86 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 87 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 88 => function ($stackPos) { + $this->semValue = new Expr\Variable(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 89 => function ($stackPos) { + /* nothing */ + }, + 90 => function ($stackPos) { + /* nothing */ + }, + 91 => function ($stackPos) { + /* nothing */ + }, + 92 => function ($stackPos) { + $this->emitError(new Error('A trailing comma is not allowed here', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + }, + 93 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 94 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 95 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 96 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 97 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 98 => function ($stackPos) { + $this->semValue = new Stmt\HaltCompiler($this->lexer->handleHaltCompiler(), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 99 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos-(3-2)], null, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); + $this->checkNamespace($this->semValue); + }, + 100 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_($this->semStack[$stackPos-(5-2)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, + 101 => function ($stackPos) { + $this->semValue = new Stmt\Namespace_(null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); + $this->checkNamespace($this->semValue); + }, + 102 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 103 => function ($stackPos) { + $this->semValue = new Stmt\Use_($this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 104 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 105 => function ($stackPos) { + $this->semValue = new Stmt\Const_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 106 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_FUNCTION; + }, + 107 => function ($stackPos) { + $this->semValue = Stmt\Use_::TYPE_CONSTANT; + }, + 108 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(7-3)], $this->startAttributeStack[$stackPos-(7-3)] + $this->endAttributeStack[$stackPos-(7-3)]), $this->semStack[$stackPos-(7-6)], $this->semStack[$stackPos-(7-2)], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 109 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(8-4)], $this->startAttributeStack[$stackPos-(8-4)] + $this->endAttributeStack[$stackPos-(8-4)]), $this->semStack[$stackPos-(8-7)], $this->semStack[$stackPos-(8-2)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 110 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(6-2)], $this->startAttributeStack[$stackPos-(6-2)] + $this->endAttributeStack[$stackPos-(6-2)]), $this->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 111 => function ($stackPos) { + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$stackPos-(7-3)], $this->startAttributeStack[$stackPos-(7-3)] + $this->endAttributeStack[$stackPos-(7-3)]), $this->semStack[$stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 112 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 113 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 114 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 115 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 116 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 117 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 118 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 119 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 120 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 121 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->checkUseUse($this->semValue, $stackPos-(1-1)); + }, + 122 => function ($stackPos) { + $this->semValue = new Stmt\UseUse($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->checkUseUse($this->semValue, $stackPos-(3-3)); + }, + 123 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 124 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 125 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; $this->semValue->type = Stmt\Use_::TYPE_NORMAL; + }, + 126 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; $this->semValue->type = $this->semStack[$stackPos-(2-1)]; + }, + 127 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 128 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 129 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 130 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 131 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 132 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 133 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 134 => function ($stackPos) { + $this->semValue = new Node\Const_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 135 => function ($stackPos) { + if (is_array($this->semStack[$stackPos-(2-2)])) { $this->semValue = array_merge($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); } else { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }; + }, + 136 => function ($stackPos) { + $this->semValue = array(); + }, + 137 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 138 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 139 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 140 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 141 => function ($stackPos) { + throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 142 => function ($stackPos) { + + if ($this->semStack[$stackPos-(3-2)]) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; $attrs = $this->startAttributeStack[$stackPos-(3-1)]; $stmts = $this->semValue; if (!empty($attrs['comments'])) {$stmts[0]->setAttribute('comments', array_merge($attrs['comments'], $stmts[0]->getAttribute('comments', []))); }; + } else { + $startAttributes = $this->startAttributeStack[$stackPos-(3-1)]; if (isset($startAttributes['comments'])) { $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $this->semValue = null; }; + if (null === $this->semValue) { $this->semValue = array(); } + } + + }, + 143 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos-(7-3)], ['stmts' => is_array($this->semStack[$stackPos-(7-5)]) ? $this->semStack[$stackPos-(7-5)] : array($this->semStack[$stackPos-(7-5)]), 'elseifs' => $this->semStack[$stackPos-(7-6)], 'else' => $this->semStack[$stackPos-(7-7)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 144 => function ($stackPos) { + $this->semValue = new Stmt\If_($this->semStack[$stackPos-(10-3)], ['stmts' => $this->semStack[$stackPos-(10-6)], 'elseifs' => $this->semStack[$stackPos-(10-7)], 'else' => $this->semStack[$stackPos-(10-8)]], $this->startAttributeStack[$stackPos-(10-1)] + $this->endAttributes); + }, + 145 => function ($stackPos) { + $this->semValue = new Stmt\While_($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 146 => function ($stackPos) { + $this->semValue = new Stmt\Do_($this->semStack[$stackPos-(7-5)], is_array($this->semStack[$stackPos-(7-2)]) ? $this->semStack[$stackPos-(7-2)] : array($this->semStack[$stackPos-(7-2)]), $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 147 => function ($stackPos) { + $this->semValue = new Stmt\For_(['init' => $this->semStack[$stackPos-(9-3)], 'cond' => $this->semStack[$stackPos-(9-5)], 'loop' => $this->semStack[$stackPos-(9-7)], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 148 => function ($stackPos) { + $this->semValue = new Stmt\Switch_($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 149 => function ($stackPos) { + $this->semValue = new Stmt\Break_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 150 => function ($stackPos) { + $this->semValue = new Stmt\Continue_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 151 => function ($stackPos) { + $this->semValue = new Stmt\Return_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 152 => function ($stackPos) { + $this->semValue = new Stmt\Global_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 153 => function ($stackPos) { + $this->semValue = new Stmt\Static_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 154 => function ($stackPos) { + $this->semValue = new Stmt\Echo_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 155 => function ($stackPos) { + $this->semValue = new Stmt\InlineHTML($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 156 => function ($stackPos) { + $this->semValue = new Stmt\Expression($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 157 => function ($stackPos) { + $this->semValue = new Stmt\Unset_($this->semStack[$stackPos-(5-3)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 158 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos-(7-3)], $this->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $this->semStack[$stackPos-(7-5)][1], 'stmts' => $this->semStack[$stackPos-(7-7)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + }, + 159 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos-(9-3)], $this->semStack[$stackPos-(9-7)][0], ['keyVar' => $this->semStack[$stackPos-(9-5)], 'byRef' => $this->semStack[$stackPos-(9-7)][1], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 160 => function ($stackPos) { + $this->semValue = new Stmt\Foreach_($this->semStack[$stackPos-(6-3)], new Expr\Error($this->startAttributeStack[$stackPos-(6-4)] + $this->endAttributeStack[$stackPos-(6-4)]), ['stmts' => $this->semStack[$stackPos-(6-6)]], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 161 => function ($stackPos) { + $this->semValue = new Stmt\Declare_($this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 162 => function ($stackPos) { + $this->semValue = new Stmt\TryCatch($this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-5)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); $this->checkTryCatch($this->semValue); + }, + 163 => function ($stackPos) { + $this->semValue = new Stmt\Throw_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 164 => function ($stackPos) { + $this->semValue = new Stmt\Goto_($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 165 => function ($stackPos) { + $this->semValue = new Stmt\Label($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 166 => function ($stackPos) { + $this->semValue = array(); /* means: no statement */ + }, + 167 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 168 => function ($stackPos) { + $startAttributes = $this->startAttributeStack[$stackPos-(1-1)]; if (isset($startAttributes['comments'])) { $this->semValue = new Stmt\Nop($startAttributes + $this->endAttributes); } else { $this->semValue = null; }; + if ($this->semValue === null) $this->semValue = array(); /* means: no statement */ + }, + 169 => function ($stackPos) { + $this->semValue = array(); + }, + 170 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 171 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 172 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 173 => function ($stackPos) { + $this->semValue = new Stmt\Catch_($this->semStack[$stackPos-(8-3)], $this->semStack[$stackPos-(8-4)], $this->semStack[$stackPos-(8-7)], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 174 => function ($stackPos) { + $this->semValue = null; + }, + 175 => function ($stackPos) { + $this->semValue = new Stmt\Finally_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 176 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 177 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 178 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 179 => function ($stackPos) { + $this->semValue = false; + }, + 180 => function ($stackPos) { + $this->semValue = true; + }, + 181 => function ($stackPos) { + $this->semValue = false; + }, + 182 => function ($stackPos) { + $this->semValue = true; + }, + 183 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 184 => function ($stackPos) { + $this->semValue = []; + }, + 185 => function ($stackPos) { + $this->semValue = new Stmt\Function_($this->semStack[$stackPos-(8-3)], ['byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-5)], 'returnType' => $this->semStack[$stackPos-(8-7)], 'stmts' => $this->semStack[$stackPos-(8-8)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 186 => function ($stackPos) { + $this->semValue = new Stmt\Class_($this->semStack[$stackPos-(7-2)], ['type' => $this->semStack[$stackPos-(7-1)], 'extends' => $this->semStack[$stackPos-(7-3)], 'implements' => $this->semStack[$stackPos-(7-4)], 'stmts' => $this->semStack[$stackPos-(7-6)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes); + $this->checkClass($this->semValue, $stackPos-(7-2)); + }, + 187 => function ($stackPos) { + $this->semValue = new Stmt\Interface_($this->semStack[$stackPos-(6-2)], ['extends' => $this->semStack[$stackPos-(6-3)], 'stmts' => $this->semStack[$stackPos-(6-5)]], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + $this->checkInterface($this->semValue, $stackPos-(6-2)); + }, + 188 => function ($stackPos) { + $this->semValue = new Stmt\Trait_($this->semStack[$stackPos-(5-2)], ['stmts' => $this->semStack[$stackPos-(5-4)]], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 189 => function ($stackPos) { + $this->semValue = 0; + }, + 190 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, + 191 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, + 192 => function ($stackPos) { + $this->semValue = null; + }, + 193 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 194 => function ($stackPos) { + $this->semValue = array(); + }, + 195 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 196 => function ($stackPos) { + $this->semValue = array(); + }, + 197 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 198 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 199 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 200 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 201 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 202 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 203 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 204 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 205 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 206 => function ($stackPos) { + $this->semValue = null; + }, + 207 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 208 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 209 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 210 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 211 => function ($stackPos) { + $this->semValue = new Stmt\DeclareDeclare($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 212 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 213 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-3)]; + }, + 214 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 215 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(5-3)]; + }, + 216 => function ($stackPos) { + $this->semValue = array(); + }, + 217 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 218 => function ($stackPos) { + $this->semValue = new Stmt\Case_($this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 219 => function ($stackPos) { + $this->semValue = new Stmt\Case_(null, $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 220 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 221 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 222 => function ($stackPos) { + $this->semValue = is_array($this->semStack[$stackPos-(1-1)]) ? $this->semStack[$stackPos-(1-1)] : array($this->semStack[$stackPos-(1-1)]); + }, + 223 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 224 => function ($stackPos) { + $this->semValue = array(); + }, + 225 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 226 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(5-3)], is_array($this->semStack[$stackPos-(5-5)]) ? $this->semStack[$stackPos-(5-5)] : array($this->semStack[$stackPos-(5-5)]), $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 227 => function ($stackPos) { + $this->semValue = array(); + }, + 228 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 229 => function ($stackPos) { + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 230 => function ($stackPos) { + $this->semValue = null; + }, + 231 => function ($stackPos) { + $this->semValue = new Stmt\Else_(is_array($this->semStack[$stackPos-(2-2)]) ? $this->semStack[$stackPos-(2-2)] : array($this->semStack[$stackPos-(2-2)]), $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 232 => function ($stackPos) { + $this->semValue = null; + }, + 233 => function ($stackPos) { + $this->semValue = new Stmt\Else_($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 234 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)], false); + }, + 235 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(2-2)], true); + }, + 236 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)], false); + }, + 237 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)], false); + }, + 238 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 239 => function ($stackPos) { + $this->semValue = array(); + }, + 240 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 241 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 242 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos-(4-4)], null, $this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); $this->checkParam($this->semValue); + }, + 243 => function ($stackPos) { + $this->semValue = new Node\Param($this->semStack[$stackPos-(6-4)], $this->semStack[$stackPos-(6-6)], $this->semStack[$stackPos-(6-1)], $this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-3)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); $this->checkParam($this->semValue); + }, + 244 => function ($stackPos) { + $this->semValue = new Node\Param(new Expr\Error($this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes), null, $this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-2)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 245 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 246 => function ($stackPos) { + $this->semValue = new Node\NullableType($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 247 => function ($stackPos) { + $this->semValue = $this->handleBuiltinTypes($this->semStack[$stackPos-(1-1)]); + }, + 248 => function ($stackPos) { + $this->semValue = new Node\Identifier('array', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 249 => function ($stackPos) { + $this->semValue = new Node\Identifier('callable', $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 250 => function ($stackPos) { + $this->semValue = null; + }, + 251 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 252 => function ($stackPos) { + $this->semValue = null; + }, + 253 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-2)]; + }, + 254 => function ($stackPos) { + $this->semValue = null; + }, + 255 => function ($stackPos) { + $this->semValue = array(); + }, + 256 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-2)]; + }, + 257 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 258 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 259 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(1-1)], false, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 260 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], true, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 261 => function ($stackPos) { + $this->semValue = new Node\Arg($this->semStack[$stackPos-(2-2)], false, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 262 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 263 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 264 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 265 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 266 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 267 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 268 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 269 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 270 => function ($stackPos) { + $this->semValue = new Stmt\StaticVar($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 271 => function ($stackPos) { + if ($this->semStack[$stackPos-(2-2)] !== null) { $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; } + }, + 272 => function ($stackPos) { + $this->semValue = array(); + }, + 273 => function ($stackPos) { + $startAttributes = $this->lookaheadStartAttributes; if (isset($startAttributes['comments'])) { $nop = new Stmt\Nop($this->createZeroLengthAttributes($startAttributes)); } else { $nop = null; }; + if ($nop !== null) { $this->semStack[$stackPos-(1-1)][] = $nop; } $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 274 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; + $this->semValue = new Stmt\Property($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $attrs, $this->semStack[$stackPos-(4-2)]); $this->checkProperty($this->semValue, $stackPos-(4-1)); + }, + 275 => function ($stackPos) { + $this->semValue = new Stmt\ClassConst($this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-1)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); $this->checkClassConst($this->semValue, $stackPos-(4-1)); + }, + 276 => function ($stackPos) { + $this->semValue = new Stmt\ClassMethod($this->semStack[$stackPos-(9-4)], ['type' => $this->semStack[$stackPos-(9-1)], 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-6)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + $this->checkClassMethod($this->semValue, $stackPos-(9-1)); + }, + 277 => function ($stackPos) { + $this->semValue = new Stmt\TraitUse($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 278 => function ($stackPos) { + $this->semValue = null; /* will be skipped */ + }, + 279 => function ($stackPos) { + $this->semValue = array(); + }, + 280 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 281 => function ($stackPos) { + $this->semValue = array(); + }, + 282 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 283 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Precedence($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 284 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(5-1)][0], $this->semStack[$stackPos-(5-1)][1], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-4)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 285 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], $this->semStack[$stackPos-(4-3)], null, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 286 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 287 => function ($stackPos) { + $this->semValue = new Stmt\TraitUseAdaptation\Alias($this->semStack[$stackPos-(4-1)][0], $this->semStack[$stackPos-(4-1)][1], null, $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 288 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)]); + }, + 289 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 290 => function ($stackPos) { + $this->semValue = array(null, $this->semStack[$stackPos-(1-1)]); + }, + 291 => function ($stackPos) { + $this->semValue = null; + }, + 292 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 293 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 294 => function ($stackPos) { + $this->semValue = 0; + }, + 295 => function ($stackPos) { + $this->semValue = 0; + }, + 296 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 297 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 298 => function ($stackPos) { + $this->checkModifier($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $this->semValue = $this->semStack[$stackPos-(2-1)] | $this->semStack[$stackPos-(2-2)]; + }, + 299 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PUBLIC; + }, + 300 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PROTECTED; + }, + 301 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_PRIVATE; + }, + 302 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_STATIC; + }, + 303 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_ABSTRACT; + }, + 304 => function ($stackPos) { + $this->semValue = Stmt\Class_::MODIFIER_FINAL; + }, + 305 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 306 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 307 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 308 => function ($stackPos) { + $this->semValue = new Node\VarLikeIdentifier(substr($this->semStack[$stackPos-(1-1)], 1), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 309 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(1-1)], null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 310 => function ($stackPos) { + $this->semValue = new Stmt\PropertyProperty($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 311 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 312 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 313 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 314 => function ($stackPos) { + $this->semValue = array(); + }, + 315 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 316 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 317 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 318 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 319 => function ($stackPos) { + $this->semValue = new Expr\Assign($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 320 => function ($stackPos) { + $this->semValue = new Expr\AssignRef($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 321 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 322 => function ($stackPos) { + $this->semValue = new Expr\Clone_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 323 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 324 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 325 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 326 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 327 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 328 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 329 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 330 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 331 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 332 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 333 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 334 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 335 => function ($stackPos) { + $this->semValue = new Expr\AssignOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 336 => function ($stackPos) { + $this->semValue = new Expr\PostInc($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 337 => function ($stackPos) { + $this->semValue = new Expr\PreInc($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 338 => function ($stackPos) { + $this->semValue = new Expr\PostDec($this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 339 => function ($stackPos) { + $this->semValue = new Expr\PreDec($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 340 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 341 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BooleanAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 342 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 343 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 344 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\LogicalXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 345 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseOr($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 346 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseAnd($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 347 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\BitwiseXor($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 348 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Concat($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 349 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Plus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 350 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Minus($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 351 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mul($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 352 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Div($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 353 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Mod($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 354 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftLeft($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 355 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\ShiftRight($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 356 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Pow($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 357 => function ($stackPos) { + $this->semValue = new Expr\UnaryPlus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 358 => function ($stackPos) { + $this->semValue = new Expr\UnaryMinus($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 359 => function ($stackPos) { + $this->semValue = new Expr\BooleanNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 360 => function ($stackPos) { + $this->semValue = new Expr\BitwiseNot($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 361 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Identical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 362 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotIdentical($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 363 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Equal($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 364 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\NotEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 365 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Spaceship($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 366 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Smaller($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 367 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\SmallerOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 368 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Greater($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 369 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\GreaterOrEqual($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 370 => function ($stackPos) { + $this->semValue = new Expr\Instanceof_($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 371 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 372 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(5-1)], $this->semStack[$stackPos-(5-3)], $this->semStack[$stackPos-(5-5)], $this->startAttributeStack[$stackPos-(5-1)] + $this->endAttributes); + }, + 373 => function ($stackPos) { + $this->semValue = new Expr\Ternary($this->semStack[$stackPos-(4-1)], null, $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 374 => function ($stackPos) { + $this->semValue = new Expr\BinaryOp\Coalesce($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 375 => function ($stackPos) { + $this->semValue = new Expr\Isset_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 376 => function ($stackPos) { + $this->semValue = new Expr\Empty_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 377 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 378 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 379 => function ($stackPos) { + $this->semValue = new Expr\Eval_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 380 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 381 => function ($stackPos) { + $this->semValue = new Expr\Include_($this->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 382 => function ($stackPos) { + $this->semValue = new Expr\Cast\Int_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 383 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = $this->getFloatCastKind($this->semStack[$stackPos-(2-1)]); + $this->semValue = new Expr\Cast\Double($this->semStack[$stackPos-(2-2)], $attrs); + }, + 384 => function ($stackPos) { + $this->semValue = new Expr\Cast\String_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 385 => function ($stackPos) { + $this->semValue = new Expr\Cast\Array_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 386 => function ($stackPos) { + $this->semValue = new Expr\Cast\Object_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 387 => function ($stackPos) { + $this->semValue = new Expr\Cast\Bool_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 388 => function ($stackPos) { + $this->semValue = new Expr\Cast\Unset_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 389 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes; + $attrs['kind'] = strtolower($this->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; + $this->semValue = new Expr\Exit_($this->semStack[$stackPos-(2-2)], $attrs); + }, + 390 => function ($stackPos) { + $this->semValue = new Expr\ErrorSuppress($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 391 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 392 => function ($stackPos) { + $this->semValue = new Expr\ShellExec($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 393 => function ($stackPos) { + $this->semValue = new Expr\Print_($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 394 => function ($stackPos) { + $this->semValue = new Expr\Yield_(null, null, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 395 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(2-2)], null, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 396 => function ($stackPos) { + $this->semValue = new Expr\Yield_($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-2)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 397 => function ($stackPos) { + $this->semValue = new Expr\YieldFrom($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 398 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'returnType' => $this->semStack[$stackPos-(8-6)], 'expr' => $this->semStack[$stackPos-(8-8)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 399 => function ($stackPos) { + $this->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'returnType' => $this->semStack[$stackPos-(9-7)], 'expr' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 400 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => false, 'byRef' => $this->semStack[$stackPos-(8-2)], 'params' => $this->semStack[$stackPos-(8-4)], 'uses' => $this->semStack[$stackPos-(8-6)], 'returnType' => $this->semStack[$stackPos-(8-7)], 'stmts' => $this->semStack[$stackPos-(8-8)]], $this->startAttributeStack[$stackPos-(8-1)] + $this->endAttributes); + }, + 401 => function ($stackPos) { + $this->semValue = new Expr\Closure(['static' => true, 'byRef' => $this->semStack[$stackPos-(9-3)], 'params' => $this->semStack[$stackPos-(9-5)], 'uses' => $this->semStack[$stackPos-(9-7)], 'returnType' => $this->semStack[$stackPos-(9-8)], 'stmts' => $this->semStack[$stackPos-(9-9)]], $this->startAttributeStack[$stackPos-(9-1)] + $this->endAttributes); + }, + 402 => function ($stackPos) { + $this->semValue = array(new Stmt\Class_(null, ['type' => 0, 'extends' => $this->semStack[$stackPos-(7-3)], 'implements' => $this->semStack[$stackPos-(7-4)], 'stmts' => $this->semStack[$stackPos-(7-6)]], $this->startAttributeStack[$stackPos-(7-1)] + $this->endAttributes), $this->semStack[$stackPos-(7-2)]); + $this->checkClass($this->semValue[0], -1); + }, + 403 => function ($stackPos) { + $this->semValue = new Expr\New_($this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 404 => function ($stackPos) { + list($class, $ctorArgs) = $this->semStack[$stackPos-(2-2)]; $this->semValue = new Expr\New_($class, $ctorArgs, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 405 => function ($stackPos) { + $this->semValue = array(); + }, + 406 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-3)]; + }, + 407 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 408 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 409 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 410 => function ($stackPos) { + $this->semValue = new Expr\ClosureUse($this->semStack[$stackPos-(2-2)], $this->semStack[$stackPos-(2-1)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 411 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 412 => function ($stackPos) { + $this->semValue = new Expr\FuncCall($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 413 => function ($stackPos) { + $this->semValue = new Expr\StaticCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 414 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 415 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 416 => function ($stackPos) { + $this->semValue = new Name($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 417 => function ($stackPos) { + $this->semValue = new Name\FullyQualified($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 418 => function ($stackPos) { + $this->semValue = new Name\Relative($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 419 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 420 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 421 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; + }, + 422 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 423 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 424 => function ($stackPos) { + $this->semValue = null; + }, + 425 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 426 => function ($stackPos) { + $this->semValue = array(); + }, + 427 => function ($stackPos) { + $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`'), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + }, + 428 => function ($stackPos) { + foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', true); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 429 => function ($stackPos) { + $this->semValue = array(); + }, + 430 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 431 => function ($stackPos) { + $this->semValue = new Expr\ConstFetch($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 432 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 433 => function ($stackPos) { + $this->semValue = new Expr\ClassConstFetch($this->semStack[$stackPos-(3-1)], new Expr\Error($this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)]), $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->errorState = 2; + }, + 434 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_SHORT; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(3-2)], $attrs); + }, + 435 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = Expr\Array_::KIND_LONG; + $this->semValue = new Expr\Array_($this->semStack[$stackPos-(4-3)], $attrs); + }, + 436 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 437 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED); + $this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$stackPos-(1-1)]), $attrs); + }, + 438 => function ($stackPos) { + $this->semValue = $this->parseLNumber($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 439 => function ($stackPos) { + $this->semValue = new Scalar\DNumber(Scalar\DNumber::parse($this->semStack[$stackPos-(1-1)]), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 440 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 441 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\File($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 442 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Dir($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 443 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Class_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 444 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Trait_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 445 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Method($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 446 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Function_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 447 => function ($stackPos) { + $this->semValue = new Scalar\MagicConst\Namespace_($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 448 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 449 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 450 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); + }, + 451 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(2-1)], '', $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(2-2)] + $this->endAttributeStack[$stackPos-(2-2)], true); + }, + 452 => function ($stackPos) { + $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; + foreach ($this->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$stackPos-(3-2)], $attrs); + }, + 453 => function ($stackPos) { + $this->semValue = $this->parseDocString($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-2)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes, $this->startAttributeStack[$stackPos-(3-3)] + $this->endAttributeStack[$stackPos-(3-3)], true); + }, + 454 => function ($stackPos) { + $this->semValue = null; + }, + 455 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 456 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 457 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 458 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 459 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 460 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 461 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 462 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 463 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 464 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 465 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 466 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 467 => function ($stackPos) { + $this->semValue = new Expr\MethodCall($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->semStack[$stackPos-(4-4)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 468 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 469 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 470 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 471 => function ($stackPos) { + $this->semValue = substr($this->semStack[$stackPos-(1-1)], 1); + }, + 472 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(4-3)]; + }, + 473 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 474 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); $this->errorState = 2; + }, + 475 => function ($stackPos) { + $var = $this->semStack[$stackPos-(1-1)]; $this->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes) : $var; + }, + 476 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 477 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 478 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 479 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 480 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 481 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 482 => function ($stackPos) { + $this->semValue = new Expr\StaticPropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 483 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 484 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 485 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 486 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 487 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 488 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 489 => function ($stackPos) { + $this->semValue = new Expr\Error($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); $this->errorState = 2; + }, + 490 => function ($stackPos) { + $this->semValue = new Expr\List_($this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 491 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 492 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 493 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 494 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 495 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 496 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 497 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-1)], true, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 498 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 499 => function ($stackPos) { + $this->semValue = null; + }, + 500 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; $end = count($this->semValue)-1; if ($this->semValue[$end] === null) array_pop($this->semValue); + }, + 501 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos]; + }, + 502 => function ($stackPos) { + /* do nothing -- prevent default action of $$=$this->semStack[$1]. See $551. */ + }, + 503 => function ($stackPos) { + $this->semStack[$stackPos-(3-1)][] = $this->semStack[$stackPos-(3-3)]; $this->semValue = $this->semStack[$stackPos-(3-1)]; + }, + 504 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 505 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(3-3)], $this->semStack[$stackPos-(3-1)], false, $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 506 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(1-1)], null, false, $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 507 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(4-4)], $this->semStack[$stackPos-(4-1)], true, $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 508 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 509 => function ($stackPos) { + $this->semValue = new Expr\ArrayItem($this->semStack[$stackPos-(2-2)], null, false, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes, true, $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 510 => function ($stackPos) { + $this->semValue = null; + }, + 511 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 512 => function ($stackPos) { + $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; + }, + 513 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(1-1)]); + }, + 514 => function ($stackPos) { + $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); + }, + 515 => function ($stackPos) { + $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 516 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 517 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + 518 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + }, + 519 => function ($stackPos) { + $this->semValue = new Expr\PropertyFetch($this->semStack[$stackPos-(3-1)], $this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 520 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 521 => function ($stackPos) { + $this->semValue = new Expr\Variable($this->semStack[$stackPos-(3-2)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + }, + 522 => function ($stackPos) { + $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(6-2)], $this->semStack[$stackPos-(6-4)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + }, + 523 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(3-2)]; + }, + 524 => function ($stackPos) { + $this->semValue = new Scalar\String_($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 525 => function ($stackPos) { + $this->semValue = $this->parseNumString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + }, + 526 => function ($stackPos) { + $this->semValue = $this->parseNumString('-' . $this->semStack[$stackPos-(2-2)], $this->startAttributeStack[$stackPos-(2-1)] + $this->endAttributes); + }, + 527 => function ($stackPos) { + $this->semValue = $this->semStack[$stackPos-(1-1)]; + }, + ]; + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/Parser/Tokens.php b/vendor/nikic/php-parser/lib/PhpParser/Parser/Tokens.php new file mode 100644 index 0000000..50c5bf5 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/Parser/Tokens.php @@ -0,0 +1,146 @@ +lexer = $lexer; + + if (isset($options['throwOnError'])) { + throw new \LogicException( + '"throwOnError" is no longer supported, use "errorHandler" instead'); + } + + $this->initReduceCallbacks(); + } + + /** + * Parses PHP code into a node tree. + * + * If a non-throwing error handler is used, the parser will continue parsing after an error + * occurred and attempt to build a partial AST. + * + * @param string $code The source code to parse + * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults + * to ErrorHandler\Throwing. + * + * @return Node\Stmt[]|null Array of statements (or null non-throwing error handler is used and + * the parser was unable to recover from an error). + */ + public function parse(string $code, ErrorHandler $errorHandler = null) { + $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing; + + $this->lexer->startLexing($code, $this->errorHandler); + $result = $this->doParse(); + + // Clear out some of the interior state, so we don't hold onto unnecessary + // memory between uses of the parser + $this->startAttributeStack = []; + $this->endAttributeStack = []; + $this->semStack = []; + $this->semValue = null; + + return $result; + } + + protected function doParse() { + // We start off with no lookahead-token + $symbol = self::SYMBOL_NONE; + + // The attributes for a node are taken from the first and last token of the node. + // From the first token only the startAttributes are taken and from the last only + // the endAttributes. Both are merged using the array union operator (+). + $startAttributes = []; + $endAttributes = []; + $this->endAttributes = $endAttributes; + + // Keep stack of start and end attributes + $this->startAttributeStack = []; + $this->endAttributeStack = [$endAttributes]; + + // Start off in the initial state and keep a stack of previous states + $state = 0; + $stateStack = [$state]; + + // Semantic value stack (contains values of tokens and semantic action results) + $this->semStack = []; + + // Current position in the stack(s) + $stackPos = 0; + + $this->errorState = 0; + + for (;;) { + //$this->traceNewState($state, $symbol); + + if ($this->actionBase[$state] === 0) { + $rule = $this->actionDefault[$state]; + } else { + if ($symbol === self::SYMBOL_NONE) { + // Fetch the next token id from the lexer and fetch additional info by-ref. + // The end attributes are fetched into a temporary variable and only set once the token is really + // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is + // reduced after a token was read but not yet shifted. + $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes); + + // map the lexer token id to the internally used symbols + $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize + ? $this->tokenToSymbol[$tokenId] + : $this->invalidSymbol; + + if ($symbol === $this->invalidSymbol) { + throw new \RangeException(sprintf( + 'The lexer returned an invalid token (id=%d, value=%s)', + $tokenId, $tokenValue + )); + } + + // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get + // the attributes of the next token, even though they don't contain it themselves. + $this->startAttributeStack[$stackPos+1] = $startAttributes; + $this->endAttributeStack[$stackPos+1] = $endAttributes; + $this->lookaheadStartAttributes = $startAttributes; + + //$this->traceRead($symbol); + } + + $idx = $this->actionBase[$state] + $symbol; + if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) + || ($state < $this->YY2TBLSTATE + && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 + && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol)) + && ($action = $this->action[$idx]) !== $this->defaultAction) { + /* + * >= numNonLeafStates: shift and reduce + * > 0: shift + * = 0: accept + * < 0: reduce + * = -YYUNEXPECTED: error + */ + if ($action > 0) { + /* shift */ + //$this->traceShift($symbol); + + ++$stackPos; + $stateStack[$stackPos] = $state = $action; + $this->semStack[$stackPos] = $tokenValue; + $this->startAttributeStack[$stackPos] = $startAttributes; + $this->endAttributeStack[$stackPos] = $endAttributes; + $this->endAttributes = $endAttributes; + $symbol = self::SYMBOL_NONE; + + if ($this->errorState) { + --$this->errorState; + } + + if ($action < $this->numNonLeafStates) { + continue; + } + + /* $yyn >= numNonLeafStates means shift-and-reduce */ + $rule = $action - $this->numNonLeafStates; + } else { + $rule = -$action; + } + } else { + $rule = $this->actionDefault[$state]; + } + } + + for (;;) { + if ($rule === 0) { + /* accept */ + //$this->traceAccept(); + return $this->semValue; + } elseif ($rule !== $this->unexpectedTokenRule) { + /* reduce */ + //$this->traceReduce($rule); + + try { + $this->reduceCallbacks[$rule]($stackPos); + } catch (Error $e) { + if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) { + $e->setStartLine($startAttributes['startLine']); + } + + $this->emitError($e); + // Can't recover from this type of error + return null; + } + + /* Goto - shift nonterminal */ + $lastEndAttributes = $this->endAttributeStack[$stackPos]; + $stackPos -= $this->ruleToLength[$rule]; + $nonTerminal = $this->ruleToNonTerminal[$rule]; + $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; + if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) { + $state = $this->goto[$idx]; + } else { + $state = $this->gotoDefault[$nonTerminal]; + } + + ++$stackPos; + $stateStack[$stackPos] = $state; + $this->semStack[$stackPos] = $this->semValue; + $this->endAttributeStack[$stackPos] = $lastEndAttributes; + } else { + /* error */ + switch ($this->errorState) { + case 0: + $msg = $this->getErrorMessage($symbol, $state); + $this->emitError(new Error($msg, $startAttributes + $endAttributes)); + // Break missing intentionally + case 1: + case 2: + $this->errorState = 3; + + // Pop until error-expecting state uncovered + while (!( + (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0 + && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) + || ($state < $this->YY2TBLSTATE + && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0 + && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) + ) || ($action = $this->action[$idx]) === $this->defaultAction) { // Not totally sure about this + if ($stackPos <= 0) { + // Could not recover from error + return null; + } + $state = $stateStack[--$stackPos]; + //$this->tracePop($state); + } + + //$this->traceShift($this->errorSymbol); + ++$stackPos; + $stateStack[$stackPos] = $state = $action; + + // We treat the error symbol as being empty, so we reset the end attributes + // to the end attributes of the last non-error symbol + $this->endAttributeStack[$stackPos] = $this->endAttributeStack[$stackPos - 1]; + $this->endAttributes = $this->endAttributeStack[$stackPos - 1]; + break; + + case 3: + if ($symbol === 0) { + // Reached EOF without recovering from error + return null; + } + + //$this->traceDiscard($symbol); + $symbol = self::SYMBOL_NONE; + break 2; + } + } + + if ($state < $this->numNonLeafStates) { + break; + } + + /* >= numNonLeafStates means shift-and-reduce */ + $rule = $state - $this->numNonLeafStates; + } + } + + throw new \RuntimeException('Reached end of parser loop'); + } + + protected function emitError(Error $error) { + $this->errorHandler->handleError($error); + } + + /** + * Format error message including expected tokens. + * + * @param int $symbol Unexpected symbol + * @param int $state State at time of error + * + * @return string Formatted error message + */ + protected function getErrorMessage(int $symbol, int $state) : string { + $expectedString = ''; + if ($expected = $this->getExpectedTokens($state)) { + $expectedString = ', expecting ' . implode(' or ', $expected); + } + + return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; + } + + /** + * Get limited number of expected tokens in given state. + * + * @param int $state State + * + * @return string[] Expected tokens. If too many, an empty array is returned. + */ + protected function getExpectedTokens(int $state) : array { + $expected = []; + + $base = $this->actionBase[$state]; + foreach ($this->symbolToName as $symbol => $name) { + $idx = $base + $symbol; + if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol + || $state < $this->YY2TBLSTATE + && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 + && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol + ) { + if ($this->action[$idx] !== $this->unexpectedTokenRule + && $this->action[$idx] !== $this->defaultAction + && $symbol !== $this->errorSymbol + ) { + if (count($expected) === 4) { + /* Too many expected tokens */ + return []; + } + + $expected[] = $name; + } + } + } + + return $expected; + } + + /* + * Tracing functions used for debugging the parser. + */ + + /* + protected function traceNewState($state, $symbol) { + echo '% State ' . $state + . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n"; + } + + protected function traceRead($symbol) { + echo '% Reading ' . $this->symbolToName[$symbol] . "\n"; + } + + protected function traceShift($symbol) { + echo '% Shift ' . $this->symbolToName[$symbol] . "\n"; + } + + protected function traceAccept() { + echo "% Accepted.\n"; + } + + protected function traceReduce($n) { + echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n"; + } + + protected function tracePop($state) { + echo '% Recovering, uncovered state ' . $state . "\n"; + } + + protected function traceDiscard($symbol) { + echo '% Discard ' . $this->symbolToName[$symbol] . "\n"; + } + */ + + /* + * Helper functions invoked by semantic actions + */ + + /** + * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions. + * + * @param Node\Stmt[] $stmts + * @return Node\Stmt[] + */ + protected function handleNamespaces(array $stmts) : array { + $hasErrored = false; + $style = $this->getNamespacingStyle($stmts); + if (null === $style) { + // not namespaced, nothing to do + return $stmts; + } elseif ('brace' === $style) { + // For braced namespaces we only have to check that there are no invalid statements between the namespaces + $afterFirstNamespace = false; + foreach ($stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + $afterFirstNamespace = true; + } elseif (!$stmt instanceof Node\Stmt\HaltCompiler + && !$stmt instanceof Node\Stmt\Nop + && $afterFirstNamespace && !$hasErrored) { + $this->emitError(new Error( + 'No code may exist outside of namespace {}', $stmt->getAttributes())); + $hasErrored = true; // Avoid one error for every statement + } + } + return $stmts; + } else { + // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts + $resultStmts = []; + $targetStmts =& $resultStmts; + $lastNs = null; + foreach ($stmts as $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + if ($lastNs !== null) { + $this->fixupNamespaceAttributes($lastNs); + } + if ($stmt->stmts === null) { + $stmt->stmts = []; + $targetStmts =& $stmt->stmts; + $resultStmts[] = $stmt; + } else { + // This handles the invalid case of mixed style namespaces + $resultStmts[] = $stmt; + $targetStmts =& $resultStmts; + } + $lastNs = $stmt; + } elseif ($stmt instanceof Node\Stmt\HaltCompiler) { + // __halt_compiler() is not moved into the namespace + $resultStmts[] = $stmt; + } else { + $targetStmts[] = $stmt; + } + } + if ($lastNs !== null) { + $this->fixupNamespaceAttributes($lastNs); + } + return $resultStmts; + } + } + + private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt) { + // We moved the statements into the namespace node, as such the end of the namespace node + // needs to be extended to the end of the statements. + if (empty($stmt->stmts)) { + return; + } + + // We only move the builtin end attributes here. This is the best we can do with the + // knowledge we have. + $endAttributes = ['endLine', 'endFilePos', 'endTokenPos']; + $lastStmt = $stmt->stmts[count($stmt->stmts) - 1]; + foreach ($endAttributes as $endAttribute) { + if ($lastStmt->hasAttribute($endAttribute)) { + $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute)); + } + } + } + + /** + * Determine namespacing style (semicolon or brace) + * + * @param Node[] $stmts Top-level statements. + * + * @return null|string One of "semicolon", "brace" or null (no namespaces) + */ + private function getNamespacingStyle(array $stmts) { + $style = null; + $hasNotAllowedStmts = false; + foreach ($stmts as $i => $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace'; + if (null === $style) { + $style = $currentStyle; + if ($hasNotAllowedStmts) { + $this->emitError(new Error( + 'Namespace declaration statement has to be the very first statement in the script', + $stmt->getLine() // Avoid marking the entire namespace as an error + )); + } + } elseif ($style !== $currentStyle) { + $this->emitError(new Error( + 'Cannot mix bracketed namespace declarations with unbracketed namespace declarations', + $stmt->getLine() // Avoid marking the entire namespace as an error + )); + // Treat like semicolon style for namespace normalization + return 'semicolon'; + } + continue; + } + + /* declare(), __halt_compiler() and nops can be used before a namespace declaration */ + if ($stmt instanceof Node\Stmt\Declare_ + || $stmt instanceof Node\Stmt\HaltCompiler + || $stmt instanceof Node\Stmt\Nop) { + continue; + } + + /* There may be a hashbang line at the very start of the file */ + if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) { + continue; + } + + /* Everything else if forbidden before namespace declarations */ + $hasNotAllowedStmts = true; + } + return $style; + } + + /** + * Fix up parsing of static property calls in PHP 5. + * + * In PHP 5 A::$b[c][d] and A::$b[c][d]() have very different interpretation. The former is + * interpreted as (A::$b)[c][d], while the latter is the same as A::{$b[c][d]}(). We parse the + * latter as the former initially and this method fixes the AST into the correct form when we + * encounter the "()". + * + * @param Node\Expr\StaticPropertyFetch|Node\Expr\ArrayDimFetch $prop + * @param Node\Arg[] $args + * @param array $attributes + * + * @return Expr\StaticCall + */ + protected function fixupPhp5StaticPropCall($prop, array $args, array $attributes) : Expr\StaticCall { + if ($prop instanceof Node\Expr\StaticPropertyFetch) { + $name = $prop->name instanceof VarLikeIdentifier + ? $prop->name->toString() : $prop->name; + $var = new Expr\Variable($name, $prop->name->getAttributes()); + return new Expr\StaticCall($prop->class, $var, $args, $attributes); + } elseif ($prop instanceof Node\Expr\ArrayDimFetch) { + $tmp = $prop; + while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { + $tmp = $tmp->var; + } + + /** @var Expr\StaticPropertyFetch $staticProp */ + $staticProp = $tmp->var; + + // Set start attributes to attributes of innermost node + $tmp = $prop; + $this->fixupStartAttributes($tmp, $staticProp->name); + while ($tmp->var instanceof Node\Expr\ArrayDimFetch) { + $tmp = $tmp->var; + $this->fixupStartAttributes($tmp, $staticProp->name); + } + + $name = $staticProp->name instanceof VarLikeIdentifier + ? $staticProp->name->toString() : $staticProp->name; + $tmp->var = new Expr\Variable($name, $staticProp->name->getAttributes()); + return new Expr\StaticCall($staticProp->class, $prop, $args, $attributes); + } else { + throw new \Exception; + } + } + + protected function fixupStartAttributes(Node $to, Node $from) { + $startAttributes = ['startLine', 'startFilePos', 'startTokenPos']; + foreach ($startAttributes as $startAttribute) { + if ($from->hasAttribute($startAttribute)) { + $to->setAttribute($startAttribute, $from->getAttribute($startAttribute)); + } + } + } + + protected function handleBuiltinTypes(Name $name) { + $scalarTypes = [ + 'bool' => true, + 'int' => true, + 'float' => true, + 'string' => true, + 'iterable' => true, + 'void' => true, + 'object' => true, + ]; + + if (!$name->isUnqualified()) { + return $name; + } + + $lowerName = $name->toLowerString(); + if (!isset($scalarTypes[$lowerName])) { + return $name; + } + + return new Node\Identifier($lowerName, $name->getAttributes()); + } + + /** + * Get combined start and end attributes at a stack location + * + * @param int $pos Stack location + * + * @return array Combined start and end attributes + */ + protected function getAttributesAt(int $pos) : array { + return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos]; + } + + protected function getFloatCastKind(string $cast): int + { + $cast = strtolower($cast); + if (strpos($cast, 'float') !== false) { + return Double::KIND_FLOAT; + } + + if (strpos($cast, 'real') !== false) { + return Double::KIND_REAL; + } + + return Double::KIND_DOUBLE; + } + + protected function parseLNumber($str, $attributes, $allowInvalidOctal = false) { + try { + return LNumber::fromString($str, $attributes, $allowInvalidOctal); + } catch (Error $error) { + $this->emitError($error); + // Use dummy value + return new LNumber(0, $attributes); + } + } + + /** + * Parse a T_NUM_STRING token into either an integer or string node. + * + * @param string $str Number string + * @param array $attributes Attributes + * + * @return LNumber|String_ Integer or string node. + */ + protected function parseNumString(string $str, array $attributes) { + if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { + return new String_($str, $attributes); + } + + $num = +$str; + if (!is_int($num)) { + return new String_($str, $attributes); + } + + return new LNumber($num, $attributes); + } + + protected function stripIndentation( + string $string, int $indentLen, string $indentChar, + bool $newlineAtStart, bool $newlineAtEnd, array $attributes + ) { + if ($indentLen === 0) { + return $string; + } + + $start = $newlineAtStart ? '(?:(?<=\n)|\A)' : '(?<=\n)'; + $end = $newlineAtEnd ? '(?:(?=[\r\n])|\z)' : '(?=[\r\n])'; + $regex = '/' . $start . '([ \t]*)(' . $end . ')?/'; + return preg_replace_callback( + $regex, + function ($matches) use ($indentLen, $indentChar, $attributes) { + $prefix = substr($matches[1], 0, $indentLen); + if (false !== strpos($prefix, $indentChar === " " ? "\t" : " ")) { + $this->emitError(new Error( + 'Invalid indentation - tabs and spaces cannot be mixed', $attributes + )); + } elseif (strlen($prefix) < $indentLen && !isset($matches[2])) { + $this->emitError(new Error( + 'Invalid body indentation level ' . + '(expecting an indentation level of at least ' . $indentLen . ')', + $attributes + )); + } + return substr($matches[0], strlen($prefix)); + }, + $string + ); + } + + protected function parseDocString( + string $startToken, $contents, string $endToken, + array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape + ) { + $kind = strpos($startToken, "'") === false + ? String_::KIND_HEREDOC : String_::KIND_NOWDOC; + + $regex = '/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/'; + $result = preg_match($regex, $startToken, $matches); + assert($result === 1); + $label = $matches[1]; + + $result = preg_match('/\A[ \t]*/', $endToken, $matches); + assert($result === 1); + $indentation = $matches[0]; + + $attributes['kind'] = $kind; + $attributes['docLabel'] = $label; + $attributes['docIndentation'] = $indentation; + + $indentHasSpaces = false !== strpos($indentation, " "); + $indentHasTabs = false !== strpos($indentation, "\t"); + if ($indentHasSpaces && $indentHasTabs) { + $this->emitError(new Error( + 'Invalid indentation - tabs and spaces cannot be mixed', + $endTokenAttributes + )); + + // Proceed processing as if this doc string is not indented + $indentation = ''; + } + + $indentLen = \strlen($indentation); + $indentChar = $indentHasSpaces ? " " : "\t"; + + if (\is_string($contents)) { + if ($contents === '') { + return new String_('', $attributes); + } + + $contents = $this->stripIndentation( + $contents, $indentLen, $indentChar, true, true, $attributes + ); + $contents = preg_replace('~(\r\n|\n|\r)\z~', '', $contents); + + if ($kind === String_::KIND_HEREDOC) { + $contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape); + } + + return new String_($contents, $attributes); + } else { + assert(count($contents) > 0); + if (!$contents[0] instanceof Node\Scalar\EncapsedStringPart) { + // If there is no leading encapsed string part, pretend there is an empty one + $this->stripIndentation( + '', $indentLen, $indentChar, true, false, $contents[0]->getAttributes() + ); + } + + $newContents = []; + foreach ($contents as $i => $part) { + if ($part instanceof Node\Scalar\EncapsedStringPart) { + $isLast = $i === \count($contents) - 1; + $part->value = $this->stripIndentation( + $part->value, $indentLen, $indentChar, + $i === 0, $isLast, $part->getAttributes() + ); + $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); + if ($isLast) { + $part->value = preg_replace('~(\r\n|\n|\r)\z~', '', $part->value); + } + if ('' === $part->value) { + continue; + } + } + $newContents[] = $part; + } + return new Encapsed($newContents, $attributes); + } + } + + /** + * Create attributes for a zero-length node with the given start attributes. + * + * @param array $startAttributes + * @return array + */ + protected function createZeroLengthAttributes(array $startAttributes) { + $attributes = $startAttributes; + if (isset($startAttributes['startLine'])) { + $attributes['endLine'] = $startAttributes['startLine']; + } + if (isset($startAttributes['startTokenPos'])) { + $attributes['endTokenPos'] = $startAttributes['startTokenPos'] - 1; + } + if (isset($startAttributes['startFilePos'])) { + $attributes['endFilePos'] = $startAttributes['startFilePos'] - 1; + } + return $attributes; + } + + protected function checkModifier($a, $b, $modifierPos) { + // Jumping through some hoops here because verifyModifier() is also used elsewhere + try { + Class_::verifyModifier($a, $b); + } catch (Error $error) { + $error->setAttributes($this->getAttributesAt($modifierPos)); + $this->emitError($error); + } + } + + protected function checkParam(Param $node) { + if ($node->variadic && null !== $node->default) { + $this->emitError(new Error( + 'Variadic parameter cannot have a default value', + $node->default->getAttributes() + )); + } + } + + protected function checkTryCatch(TryCatch $node) { + if (empty($node->catches) && null === $node->finally) { + $this->emitError(new Error( + 'Cannot use try without catch or finally', $node->getAttributes() + )); + } + } + + protected function checkNamespace(Namespace_ $node) { + if ($node->name && $node->name->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as namespace name', $node->name), + $node->name->getAttributes() + )); + } + + if (null !== $node->stmts) { + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Namespace_) { + $this->emitError(new Error( + 'Namespace declarations cannot be nested', $stmt->getAttributes() + )); + } + } + } + } + + protected function checkClass(Class_ $node, $namePos) { + if (null !== $node->name && $node->name->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name), + $this->getAttributesAt($namePos) + )); + } + + if ($node->extends && $node->extends->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), + $node->extends->getAttributes() + )); + } + + foreach ($node->implements as $interface) { + if ($interface->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), + $interface->getAttributes() + )); + } + } + } + + protected function checkInterface(Interface_ $node, $namePos) { + if (null !== $node->name && $node->name->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name), + $this->getAttributesAt($namePos) + )); + } + + foreach ($node->extends as $interface) { + if ($interface->isSpecialClassName()) { + $this->emitError(new Error( + sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), + $interface->getAttributes() + )); + } + } + } + + protected function checkClassMethod(ClassMethod $node, $modifierPos) { + if ($node->flags & Class_::MODIFIER_STATIC) { + switch ($node->name->toLowerString()) { + case '__construct': + $this->emitError(new Error( + sprintf('Constructor %s() cannot be static', $node->name), + $this->getAttributesAt($modifierPos))); + break; + case '__destruct': + $this->emitError(new Error( + sprintf('Destructor %s() cannot be static', $node->name), + $this->getAttributesAt($modifierPos))); + break; + case '__clone': + $this->emitError(new Error( + sprintf('Clone method %s() cannot be static', $node->name), + $this->getAttributesAt($modifierPos))); + break; + } + } + } + + protected function checkClassConst(ClassConst $node, $modifierPos) { + if ($node->flags & Class_::MODIFIER_STATIC) { + $this->emitError(new Error( + "Cannot use 'static' as constant modifier", + $this->getAttributesAt($modifierPos))); + } + if ($node->flags & Class_::MODIFIER_ABSTRACT) { + $this->emitError(new Error( + "Cannot use 'abstract' as constant modifier", + $this->getAttributesAt($modifierPos))); + } + if ($node->flags & Class_::MODIFIER_FINAL) { + $this->emitError(new Error( + "Cannot use 'final' as constant modifier", + $this->getAttributesAt($modifierPos))); + } + } + + protected function checkProperty(Property $node, $modifierPos) { + if ($node->flags & Class_::MODIFIER_ABSTRACT) { + $this->emitError(new Error('Properties cannot be declared abstract', + $this->getAttributesAt($modifierPos))); + } + + if ($node->flags & Class_::MODIFIER_FINAL) { + $this->emitError(new Error('Properties cannot be declared final', + $this->getAttributesAt($modifierPos))); + } + } + + protected function checkUseUse(UseUse $node, $namePos) { + if ($node->alias && $node->alias->isSpecialClassName()) { + $this->emitError(new Error( + sprintf( + 'Cannot use %s as %s because \'%2$s\' is a special class name', + $node->name, $node->alias + ), + $this->getAttributesAt($namePos) + )); + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php b/vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php new file mode 100644 index 0000000..f041e7f --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php @@ -0,0 +1,44 @@ +type ? $this->p($node->type) . ' ' : '') + . ($node->byRef ? '&' : '') + . ($node->variadic ? '...' : '') + . $this->p($node->var) + . ($node->default ? ' = ' . $this->p($node->default) : ''); + } + + protected function pArg(Node\Arg $node) { + return ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); + } + + protected function pConst(Node\Const_ $node) { + return $node->name . ' = ' . $this->p($node->value); + } + + protected function pNullableType(Node\NullableType $node) { + return '?' . $this->p($node->type); + } + + protected function pIdentifier(Node\Identifier $node) { + return $node->name; + } + + protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node) { + return '$' . $node->name; + } + + // Names + + protected function pName(Name $node) { + return implode('\\', $node->parts); + } + + protected function pName_FullyQualified(Name\FullyQualified $node) { + return '\\' . implode('\\', $node->parts); + } + + protected function pName_Relative(Name\Relative $node) { + return 'namespace\\' . implode('\\', $node->parts); + } + + // Magic Constants + + protected function pScalar_MagicConst_Class(MagicConst\Class_ $node) { + return '__CLASS__'; + } + + protected function pScalar_MagicConst_Dir(MagicConst\Dir $node) { + return '__DIR__'; + } + + protected function pScalar_MagicConst_File(MagicConst\File $node) { + return '__FILE__'; + } + + protected function pScalar_MagicConst_Function(MagicConst\Function_ $node) { + return '__FUNCTION__'; + } + + protected function pScalar_MagicConst_Line(MagicConst\Line $node) { + return '__LINE__'; + } + + protected function pScalar_MagicConst_Method(MagicConst\Method $node) { + return '__METHOD__'; + } + + protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node) { + return '__NAMESPACE__'; + } + + protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node) { + return '__TRAIT__'; + } + + // Scalars + + protected function pScalar_String(Scalar\String_ $node) { + $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED); + switch ($kind) { + case Scalar\String_::KIND_NOWDOC: + $label = $node->getAttribute('docLabel'); + if ($label && !$this->containsEndLabel($node->value, $label)) { + if ($node->value === '') { + return "<<<'$label'\n$label" . $this->docStringEndToken; + } + + return "<<<'$label'\n$node->value\n$label" + . $this->docStringEndToken; + } + /* break missing intentionally */ + case Scalar\String_::KIND_SINGLE_QUOTED: + return $this->pSingleQuotedString($node->value); + case Scalar\String_::KIND_HEREDOC: + $label = $node->getAttribute('docLabel'); + if ($label && !$this->containsEndLabel($node->value, $label)) { + if ($node->value === '') { + return "<<<$label\n$label" . $this->docStringEndToken; + } + + $escaped = $this->escapeString($node->value, null); + return "<<<$label\n" . $escaped . "\n$label" + . $this->docStringEndToken; + } + /* break missing intentionally */ + case Scalar\String_::KIND_DOUBLE_QUOTED: + return '"' . $this->escapeString($node->value, '"') . '"'; + } + throw new \Exception('Invalid string kind'); + } + + protected function pScalar_Encapsed(Scalar\Encapsed $node) { + if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) { + $label = $node->getAttribute('docLabel'); + if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) { + if (count($node->parts) === 1 + && $node->parts[0] instanceof Scalar\EncapsedStringPart + && $node->parts[0]->value === '' + ) { + return "<<<$label\n$label" . $this->docStringEndToken; + } + + return "<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label" + . $this->docStringEndToken; + } + } + return '"' . $this->pEncapsList($node->parts, '"') . '"'; + } + + protected function pScalar_LNumber(Scalar\LNumber $node) { + if ($node->value === -\PHP_INT_MAX-1) { + // PHP_INT_MIN cannot be represented as a literal, + // because the sign is not part of the literal + return '(-' . \PHP_INT_MAX . '-1)'; + } + + $kind = $node->getAttribute('kind', Scalar\LNumber::KIND_DEC); + if (Scalar\LNumber::KIND_DEC === $kind) { + return (string) $node->value; + } + + if ($node->value < 0) { + $sign = '-'; + $str = (string) -$node->value; + } else { + $sign = ''; + $str = (string) $node->value; + } + switch ($kind) { + case Scalar\LNumber::KIND_BIN: + return $sign . '0b' . base_convert($str, 10, 2); + case Scalar\LNumber::KIND_OCT: + return $sign . '0' . base_convert($str, 10, 8); + case Scalar\LNumber::KIND_HEX: + return $sign . '0x' . base_convert($str, 10, 16); + } + throw new \Exception('Invalid number kind'); + } + + protected function pScalar_DNumber(Scalar\DNumber $node) { + if (!is_finite($node->value)) { + if ($node->value === \INF) { + return '\INF'; + } elseif ($node->value === -\INF) { + return '-\INF'; + } else { + return '\NAN'; + } + } + + // Try to find a short full-precision representation + $stringValue = sprintf('%.16G', $node->value); + if ($node->value !== (double) $stringValue) { + $stringValue = sprintf('%.17G', $node->value); + } + + // %G is locale dependent and there exists no locale-independent alternative. We don't want + // mess with switching locales here, so let's assume that a comma is the only non-standard + // decimal separator we may encounter... + $stringValue = str_replace(',', '.', $stringValue); + + // ensure that number is really printed as float + return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; + } + + protected function pScalar_EncapsedStringPart(Scalar\EncapsedStringPart $node) { + throw new \LogicException('Cannot directly print EncapsedStringPart'); + } + + // Assignments + + protected function pExpr_Assign(Expr\Assign $node) { + return $this->pInfixOp(Expr\Assign::class, $node->var, ' = ', $node->expr); + } + + protected function pExpr_AssignRef(Expr\AssignRef $node) { + return $this->pInfixOp(Expr\AssignRef::class, $node->var, ' =& ', $node->expr); + } + + protected function pExpr_AssignOp_Plus(AssignOp\Plus $node) { + return $this->pInfixOp(AssignOp\Plus::class, $node->var, ' += ', $node->expr); + } + + protected function pExpr_AssignOp_Minus(AssignOp\Minus $node) { + return $this->pInfixOp(AssignOp\Minus::class, $node->var, ' -= ', $node->expr); + } + + protected function pExpr_AssignOp_Mul(AssignOp\Mul $node) { + return $this->pInfixOp(AssignOp\Mul::class, $node->var, ' *= ', $node->expr); + } + + protected function pExpr_AssignOp_Div(AssignOp\Div $node) { + return $this->pInfixOp(AssignOp\Div::class, $node->var, ' /= ', $node->expr); + } + + protected function pExpr_AssignOp_Concat(AssignOp\Concat $node) { + return $this->pInfixOp(AssignOp\Concat::class, $node->var, ' .= ', $node->expr); + } + + protected function pExpr_AssignOp_Mod(AssignOp\Mod $node) { + return $this->pInfixOp(AssignOp\Mod::class, $node->var, ' %= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node) { + return $this->pInfixOp(AssignOp\BitwiseAnd::class, $node->var, ' &= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node) { + return $this->pInfixOp(AssignOp\BitwiseOr::class, $node->var, ' |= ', $node->expr); + } + + protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node) { + return $this->pInfixOp(AssignOp\BitwiseXor::class, $node->var, ' ^= ', $node->expr); + } + + protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node) { + return $this->pInfixOp(AssignOp\ShiftLeft::class, $node->var, ' <<= ', $node->expr); + } + + protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node) { + return $this->pInfixOp(AssignOp\ShiftRight::class, $node->var, ' >>= ', $node->expr); + } + + protected function pExpr_AssignOp_Pow(AssignOp\Pow $node) { + return $this->pInfixOp(AssignOp\Pow::class, $node->var, ' **= ', $node->expr); + } + + protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node) { + return $this->pInfixOp(AssignOp\Coalesce::class, $node->var, ' ??= ', $node->expr); + } + + // Binary expressions + + protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node) { + return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right); + } + + protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node) { + return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right); + } + + protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node) { + return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right); + } + + protected function pExpr_BinaryOp_Div(BinaryOp\Div $node) { + return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right); + } + + protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node) { + return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right); + } + + protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node) { + return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right); + } + + protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node) { + return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right); + } + + protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node) { + return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node) { + return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node) { + return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right); + } + + protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node) { + return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right); + } + + protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node) { + return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right); + } + + protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node) { + return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right); + } + + protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node) { + return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node) { + return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node) { + return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right); + } + + protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node) { + return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right); + } + + protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node) { + return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right); + } + + protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node) { + return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right); + } + + protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node) { + return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right); + } + + protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node) { + return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right); + } + + protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node) { + return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right); + } + + protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node) { + return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right); + } + + protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node) { + return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right); + } + + protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node) { + return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right); + } + + protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node) { + return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right); + } + + protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) { + return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right); + } + + protected function pExpr_Instanceof(Expr\Instanceof_ $node) { + return $this->pInfixOp(Expr\Instanceof_::class, $node->expr, ' instanceof ', $node->class); + } + + // Unary expressions + + protected function pExpr_BooleanNot(Expr\BooleanNot $node) { + return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr); + } + + protected function pExpr_BitwiseNot(Expr\BitwiseNot $node) { + return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr); + } + + protected function pExpr_UnaryMinus(Expr\UnaryMinus $node) { + if ($node->expr instanceof Expr\UnaryMinus || $node->expr instanceof Expr\PreDec) { + // Enforce -(-$expr) instead of --$expr + return '-(' . $this->p($node->expr) . ')'; + } + return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr); + } + + protected function pExpr_UnaryPlus(Expr\UnaryPlus $node) { + if ($node->expr instanceof Expr\UnaryPlus || $node->expr instanceof Expr\PreInc) { + // Enforce +(+$expr) instead of ++$expr + return '+(' . $this->p($node->expr) . ')'; + } + return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr); + } + + protected function pExpr_PreInc(Expr\PreInc $node) { + return $this->pPrefixOp(Expr\PreInc::class, '++', $node->var); + } + + protected function pExpr_PreDec(Expr\PreDec $node) { + return $this->pPrefixOp(Expr\PreDec::class, '--', $node->var); + } + + protected function pExpr_PostInc(Expr\PostInc $node) { + return $this->pPostfixOp(Expr\PostInc::class, $node->var, '++'); + } + + protected function pExpr_PostDec(Expr\PostDec $node) { + return $this->pPostfixOp(Expr\PostDec::class, $node->var, '--'); + } + + protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node) { + return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr); + } + + protected function pExpr_YieldFrom(Expr\YieldFrom $node) { + return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr); + } + + protected function pExpr_Print(Expr\Print_ $node) { + return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr); + } + + // Casts + + protected function pExpr_Cast_Int(Cast\Int_ $node) { + return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr); + } + + protected function pExpr_Cast_Double(Cast\Double $node) { + $kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE); + if ($kind === Cast\Double::KIND_DOUBLE) { + $cast = '(double)'; + } elseif ($kind === Cast\Double::KIND_FLOAT) { + $cast = '(float)'; + } elseif ($kind === Cast\Double::KIND_REAL) { + $cast = '(real)'; + } + return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr); + } + + protected function pExpr_Cast_String(Cast\String_ $node) { + return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr); + } + + protected function pExpr_Cast_Array(Cast\Array_ $node) { + return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr); + } + + protected function pExpr_Cast_Object(Cast\Object_ $node) { + return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr); + } + + protected function pExpr_Cast_Bool(Cast\Bool_ $node) { + return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr); + } + + protected function pExpr_Cast_Unset(Cast\Unset_ $node) { + return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr); + } + + // Function calls and similar constructs + + protected function pExpr_FuncCall(Expr\FuncCall $node) { + return $this->pCallLhs($node->name) + . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + + protected function pExpr_MethodCall(Expr\MethodCall $node) { + return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) + . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + + protected function pExpr_StaticCall(Expr\StaticCall $node) { + return $this->pDereferenceLhs($node->class) . '::' + . ($node->name instanceof Expr + ? ($node->name instanceof Expr\Variable + ? $this->p($node->name) + : '{' . $this->p($node->name) . '}') + : $node->name) + . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + + protected function pExpr_Empty(Expr\Empty_ $node) { + return 'empty(' . $this->p($node->expr) . ')'; + } + + protected function pExpr_Isset(Expr\Isset_ $node) { + return 'isset(' . $this->pCommaSeparated($node->vars) . ')'; + } + + protected function pExpr_Eval(Expr\Eval_ $node) { + return 'eval(' . $this->p($node->expr) . ')'; + } + + protected function pExpr_Include(Expr\Include_ $node) { + static $map = [ + Expr\Include_::TYPE_INCLUDE => 'include', + Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', + Expr\Include_::TYPE_REQUIRE => 'require', + Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once', + ]; + + return $map[$node->type] . ' ' . $this->p($node->expr); + } + + protected function pExpr_List(Expr\List_ $node) { + return 'list(' . $this->pCommaSeparated($node->items) . ')'; + } + + // Other + + protected function pExpr_Error(Expr\Error $node) { + throw new \LogicException('Cannot pretty-print AST with Error nodes'); + } + + protected function pExpr_Variable(Expr\Variable $node) { + if ($node->name instanceof Expr) { + return '${' . $this->p($node->name) . '}'; + } else { + return '$' . $node->name; + } + } + + protected function pExpr_Array(Expr\Array_ $node) { + $syntax = $node->getAttribute('kind', + $this->options['shortArraySyntax'] ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); + if ($syntax === Expr\Array_::KIND_SHORT) { + return '[' . $this->pMaybeMultiline($node->items, true) . ']'; + } else { + return 'array(' . $this->pMaybeMultiline($node->items, true) . ')'; + } + } + + protected function pExpr_ArrayItem(Expr\ArrayItem $node) { + return (null !== $node->key ? $this->p($node->key) . ' => ' : '') + . ($node->byRef ? '&' : '') + . ($node->unpack ? '...' : '') + . $this->p($node->value); + } + + protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node) { + return $this->pDereferenceLhs($node->var) + . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']'; + } + + protected function pExpr_ConstFetch(Expr\ConstFetch $node) { + return $this->p($node->name); + } + + protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node) { + return $this->p($node->class) . '::' . $this->p($node->name); + } + + protected function pExpr_PropertyFetch(Expr\PropertyFetch $node) { + return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name); + } + + protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node) { + return $this->pDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name); + } + + protected function pExpr_ShellExec(Expr\ShellExec $node) { + return '`' . $this->pEncapsList($node->parts, '`') . '`'; + } + + protected function pExpr_Closure(Expr\Closure $node) { + return ($node->static ? 'static ' : '') + . 'function ' . ($node->byRef ? '&' : '') + . '(' . $this->pCommaSeparated($node->params) . ')' + . (!empty($node->uses) ? ' use(' . $this->pCommaSeparated($node->uses) . ')' : '') + . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') + . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) { + return ($node->static ? 'static ' : '') + . 'fn' . ($node->byRef ? '&' : '') + . '(' . $this->pCommaSeparated($node->params) . ')' + . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') + . ' => ' + . $this->p($node->expr); + } + + protected function pExpr_ClosureUse(Expr\ClosureUse $node) { + return ($node->byRef ? '&' : '') . $this->p($node->var); + } + + protected function pExpr_New(Expr\New_ $node) { + if ($node->class instanceof Stmt\Class_) { + $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : ''; + return 'new ' . $this->pClassCommon($node->class, $args); + } + return 'new ' . $this->p($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; + } + + protected function pExpr_Clone(Expr\Clone_ $node) { + return 'clone ' . $this->p($node->expr); + } + + protected function pExpr_Ternary(Expr\Ternary $node) { + // a bit of cheating: we treat the ternary as a binary op where the ?...: part is the operator. + // this is okay because the part between ? and : never needs parentheses. + return $this->pInfixOp(Expr\Ternary::class, + $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else + ); + } + + protected function pExpr_Exit(Expr\Exit_ $node) { + $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE); + return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die') + . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : ''); + } + + protected function pExpr_Yield(Expr\Yield_ $node) { + if ($node->value === null) { + return 'yield'; + } else { + // this is a bit ugly, but currently there is no way to detect whether the parentheses are necessary + return '(yield ' + . ($node->key !== null ? $this->p($node->key) . ' => ' : '') + . $this->p($node->value) + . ')'; + } + } + + // Declarations + + protected function pStmt_Namespace(Stmt\Namespace_ $node) { + if ($this->canUseSemicolonNamespaces) { + return 'namespace ' . $this->p($node->name) . ';' + . $this->nl . $this->pStmts($node->stmts, false); + } else { + return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '') + . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + } + + protected function pStmt_Use(Stmt\Use_ $node) { + return 'use ' . $this->pUseType($node->type) + . $this->pCommaSeparated($node->uses) . ';'; + } + + protected function pStmt_GroupUse(Stmt\GroupUse $node) { + return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) + . '\{' . $this->pCommaSeparated($node->uses) . '};'; + } + + protected function pStmt_UseUse(Stmt\UseUse $node) { + return $this->pUseType($node->type) . $this->p($node->name) + . (null !== $node->alias ? ' as ' . $node->alias : ''); + } + + protected function pUseType($type) { + return $type === Stmt\Use_::TYPE_FUNCTION ? 'function ' + : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : ''); + } + + protected function pStmt_Interface(Stmt\Interface_ $node) { + return 'interface ' . $node->name + . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') + . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Class(Stmt\Class_ $node) { + return $this->pClassCommon($node, ' ' . $node->name); + } + + protected function pStmt_Trait(Stmt\Trait_ $node) { + return 'trait ' . $node->name + . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_TraitUse(Stmt\TraitUse $node) { + return 'use ' . $this->pCommaSeparated($node->traits) + . (empty($node->adaptations) + ? ';' + : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}'); + } + + protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node) { + return $this->p($node->trait) . '::' . $node->method + . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';'; + } + + protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node) { + return (null !== $node->trait ? $this->p($node->trait) . '::' : '') + . $node->method . ' as' + . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '') + . (null !== $node->newName ? ' ' . $node->newName : '') + . ';'; + } + + protected function pStmt_Property(Stmt\Property $node) { + return (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) + . ($node->type ? $this->p($node->type) . ' ' : '') + . $this->pCommaSeparated($node->props) . ';'; + } + + protected function pStmt_PropertyProperty(Stmt\PropertyProperty $node) { + return '$' . $node->name + . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + } + + protected function pStmt_ClassMethod(Stmt\ClassMethod $node) { + return $this->pModifiers($node->flags) + . 'function ' . ($node->byRef ? '&' : '') . $node->name + . '(' . $this->pCommaSeparated($node->params) . ')' + . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') + . (null !== $node->stmts + ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' + : ';'); + } + + protected function pStmt_ClassConst(Stmt\ClassConst $node) { + return $this->pModifiers($node->flags) + . 'const ' . $this->pCommaSeparated($node->consts) . ';'; + } + + protected function pStmt_Function(Stmt\Function_ $node) { + return 'function ' . ($node->byRef ? '&' : '') . $node->name + . '(' . $this->pCommaSeparated($node->params) . ')' + . (null !== $node->returnType ? ' : ' . $this->p($node->returnType) : '') + . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Const(Stmt\Const_ $node) { + return 'const ' . $this->pCommaSeparated($node->consts) . ';'; + } + + protected function pStmt_Declare(Stmt\Declare_ $node) { + return 'declare (' . $this->pCommaSeparated($node->declares) . ')' + . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); + } + + protected function pStmt_DeclareDeclare(Stmt\DeclareDeclare $node) { + return $node->key . '=' . $this->p($node->value); + } + + // Control flow + + protected function pStmt_If(Stmt\If_ $node) { + return 'if (' . $this->p($node->cond) . ') {' + . $this->pStmts($node->stmts) . $this->nl . '}' + . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') + . (null !== $node->else ? ' ' . $this->p($node->else) : ''); + } + + protected function pStmt_ElseIf(Stmt\ElseIf_ $node) { + return 'elseif (' . $this->p($node->cond) . ') {' + . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Else(Stmt\Else_ $node) { + return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_For(Stmt\For_ $node) { + return 'for (' + . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '') + . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '') + . $this->pCommaSeparated($node->loop) + . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Foreach(Stmt\Foreach_ $node) { + return 'foreach (' . $this->p($node->expr) . ' as ' + . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '') + . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' + . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_While(Stmt\While_ $node) { + return 'while (' . $this->p($node->cond) . ') {' + . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Do(Stmt\Do_ $node) { + return 'do {' . $this->pStmts($node->stmts) . $this->nl + . '} while (' . $this->p($node->cond) . ');'; + } + + protected function pStmt_Switch(Stmt\Switch_ $node) { + return 'switch (' . $this->p($node->cond) . ') {' + . $this->pStmts($node->cases) . $this->nl . '}'; + } + + protected function pStmt_Case(Stmt\Case_ $node) { + return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':' + . $this->pStmts($node->stmts); + } + + protected function pStmt_TryCatch(Stmt\TryCatch $node) { + return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' + . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') + . ($node->finally !== null ? ' ' . $this->p($node->finally) : ''); + } + + protected function pStmt_Catch(Stmt\Catch_ $node) { + return 'catch (' . $this->pImplode($node->types, '|') . ' ' + . $this->p($node->var) + . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Finally(Stmt\Finally_ $node) { + return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pStmt_Break(Stmt\Break_ $node) { + return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + } + + protected function pStmt_Continue(Stmt\Continue_ $node) { + return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; + } + + protected function pStmt_Return(Stmt\Return_ $node) { + return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';'; + } + + protected function pStmt_Throw(Stmt\Throw_ $node) { + return 'throw ' . $this->p($node->expr) . ';'; + } + + protected function pStmt_Label(Stmt\Label $node) { + return $node->name . ':'; + } + + protected function pStmt_Goto(Stmt\Goto_ $node) { + return 'goto ' . $node->name . ';'; + } + + // Other + + protected function pStmt_Expression(Stmt\Expression $node) { + return $this->p($node->expr) . ';'; + } + + protected function pStmt_Echo(Stmt\Echo_ $node) { + return 'echo ' . $this->pCommaSeparated($node->exprs) . ';'; + } + + protected function pStmt_Static(Stmt\Static_ $node) { + return 'static ' . $this->pCommaSeparated($node->vars) . ';'; + } + + protected function pStmt_Global(Stmt\Global_ $node) { + return 'global ' . $this->pCommaSeparated($node->vars) . ';'; + } + + protected function pStmt_StaticVar(Stmt\StaticVar $node) { + return $this->p($node->var) + . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); + } + + protected function pStmt_Unset(Stmt\Unset_ $node) { + return 'unset(' . $this->pCommaSeparated($node->vars) . ');'; + } + + protected function pStmt_InlineHTML(Stmt\InlineHTML $node) { + $newline = $node->getAttribute('hasLeadingNewline', true) ? "\n" : ''; + return '?>' . $newline . $node->value . 'remaining; + } + + protected function pStmt_Nop(Stmt\Nop $node) { + return ''; + } + + // Helpers + + protected function pClassCommon(Stmt\Class_ $node, $afterClassToken) { + return $this->pModifiers($node->flags) + . 'class' . $afterClassToken + . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') + . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') + . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; + } + + protected function pObjectProperty($node) { + if ($node instanceof Expr) { + return '{' . $this->p($node) . '}'; + } else { + return $node; + } + } + + protected function pEncapsList(array $encapsList, $quote) { + $return = ''; + foreach ($encapsList as $element) { + if ($element instanceof Scalar\EncapsedStringPart) { + $return .= $this->escapeString($element->value, $quote); + } else { + $return .= '{' . $this->p($element) . '}'; + } + } + + return $return; + } + + protected function pSingleQuotedString(string $string) { + return '\'' . addcslashes($string, '\'\\') . '\''; + } + + protected function escapeString($string, $quote) { + if (null === $quote) { + // For doc strings, don't escape newlines + $escaped = addcslashes($string, "\t\f\v$\\"); + } else { + $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\"); + } + + // Escape other control characters + return preg_replace_callback('/([\0-\10\16-\37])(?=([0-7]?))/', function ($matches) { + $oct = decoct(ord($matches[1])); + if ($matches[2] !== '') { + // If there is a trailing digit, use the full three character form + return '\\' . str_pad($oct, 3, '0', \STR_PAD_LEFT); + } + return '\\' . $oct; + }, $escaped); + } + + protected function containsEndLabel($string, $label, $atStart = true, $atEnd = true) { + $start = $atStart ? '(?:^|[\r\n])' : '[\r\n]'; + $end = $atEnd ? '(?:$|[;\r\n])' : '[;\r\n]'; + return false !== strpos($string, $label) + && preg_match('/' . $start . $label . $end . '/', $string); + } + + protected function encapsedContainsEndLabel(array $parts, $label) { + foreach ($parts as $i => $part) { + $atStart = $i === 0; + $atEnd = $i === count($parts) - 1; + if ($part instanceof Scalar\EncapsedStringPart + && $this->containsEndLabel($part->value, $label, $atStart, $atEnd) + ) { + return true; + } + } + return false; + } + + protected function pDereferenceLhs(Node $node) { + if (!$this->dereferenceLhsRequiresParens($node)) { + return $this->p($node); + } else { + return '(' . $this->p($node) . ')'; + } + } + + protected function pCallLhs(Node $node) { + if (!$this->callLhsRequiresParens($node)) { + return $this->p($node); + } else { + return '(' . $this->p($node) . ')'; + } + } + + /** + * @param Node[] $nodes + * @return bool + */ + private function hasNodeWithComments(array $nodes) { + foreach ($nodes as $node) { + if ($node && $node->getComments()) { + return true; + } + } + return false; + } + + private function pMaybeMultiline(array $nodes, $trailingComma = false) { + if (!$this->hasNodeWithComments($nodes)) { + return $this->pCommaSeparated($nodes); + } else { + return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; + } + } +} diff --git a/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php b/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php new file mode 100644 index 0000000..90f10c4 --- /dev/null +++ b/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php @@ -0,0 +1,1430 @@ + [ 0, 1], + Expr\BitwiseNot::class => [ 10, 1], + Expr\PreInc::class => [ 10, 1], + Expr\PreDec::class => [ 10, 1], + Expr\PostInc::class => [ 10, -1], + Expr\PostDec::class => [ 10, -1], + Expr\UnaryPlus::class => [ 10, 1], + Expr\UnaryMinus::class => [ 10, 1], + Cast\Int_::class => [ 10, 1], + Cast\Double::class => [ 10, 1], + Cast\String_::class => [ 10, 1], + Cast\Array_::class => [ 10, 1], + Cast\Object_::class => [ 10, 1], + Cast\Bool_::class => [ 10, 1], + Cast\Unset_::class => [ 10, 1], + Expr\ErrorSuppress::class => [ 10, 1], + Expr\Instanceof_::class => [ 20, 0], + Expr\BooleanNot::class => [ 30, 1], + BinaryOp\Mul::class => [ 40, -1], + BinaryOp\Div::class => [ 40, -1], + BinaryOp\Mod::class => [ 40, -1], + BinaryOp\Plus::class => [ 50, -1], + BinaryOp\Minus::class => [ 50, -1], + BinaryOp\Concat::class => [ 50, -1], + BinaryOp\ShiftLeft::class => [ 60, -1], + BinaryOp\ShiftRight::class => [ 60, -1], + BinaryOp\Smaller::class => [ 70, 0], + BinaryOp\SmallerOrEqual::class => [ 70, 0], + BinaryOp\Greater::class => [ 70, 0], + BinaryOp\GreaterOrEqual::class => [ 70, 0], + BinaryOp\Equal::class => [ 80, 0], + BinaryOp\NotEqual::class => [ 80, 0], + BinaryOp\Identical::class => [ 80, 0], + BinaryOp\NotIdentical::class => [ 80, 0], + BinaryOp\Spaceship::class => [ 80, 0], + BinaryOp\BitwiseAnd::class => [ 90, -1], + BinaryOp\BitwiseXor::class => [100, -1], + BinaryOp\BitwiseOr::class => [110, -1], + BinaryOp\BooleanAnd::class => [120, -1], + BinaryOp\BooleanOr::class => [130, -1], + BinaryOp\Coalesce::class => [140, 1], + Expr\Ternary::class => [150, -1], + // parser uses %left for assignments, but they really behave as %right + Expr\Assign::class => [160, 1], + Expr\AssignRef::class => [160, 1], + AssignOp\Plus::class => [160, 1], + AssignOp\Minus::class => [160, 1], + AssignOp\Mul::class => [160, 1], + AssignOp\Div::class => [160, 1], + AssignOp\Concat::class => [160, 1], + AssignOp\Mod::class => [160, 1], + AssignOp\BitwiseAnd::class => [160, 1], + AssignOp\BitwiseOr::class => [160, 1], + AssignOp\BitwiseXor::class => [160, 1], + AssignOp\ShiftLeft::class => [160, 1], + AssignOp\ShiftRight::class => [160, 1], + AssignOp\Pow::class => [160, 1], + AssignOp\Coalesce::class => [160, 1], + Expr\YieldFrom::class => [165, 1], + Expr\Print_::class => [168, 1], + BinaryOp\LogicalAnd::class => [170, -1], + BinaryOp\LogicalXor::class => [180, -1], + BinaryOp\LogicalOr::class => [190, -1], + Expr\Include_::class => [200, -1], + ]; + + /** @var int Current indentation level. */ + protected $indentLevel; + /** @var string Newline including current indentation. */ + protected $nl; + /** @var string Token placed at end of doc string to ensure it is followed by a newline. */ + protected $docStringEndToken; + /** @var bool Whether semicolon namespaces can be used (i.e. no global namespace is used) */ + protected $canUseSemicolonNamespaces; + /** @var array Pretty printer options */ + protected $options; + + /** @var TokenStream Original tokens for use in format-preserving pretty print */ + protected $origTokens; + /** @var Internal\Differ Differ for node lists */ + protected $nodeListDiffer; + /** @var bool[] Map determining whether a certain character is a label character */ + protected $labelCharMap; + /** + * @var int[][] Map from token classes and subnode names to FIXUP_* constants. This is used + * during format-preserving prints to place additional parens/braces if necessary. + */ + protected $fixupMap; + /** + * @var int[][] Map from "{$node->getType()}->{$subNode}" to ['left' => $l, 'right' => $r], + * where $l and $r specify the token type that needs to be stripped when removing + * this node. + */ + protected $removalMap; + /** + * @var mixed[] Map from "{$node->getType()}->{$subNode}" to [$find, $beforeToken, $extraLeft, $extraRight]. + * $find is an optional token after which the insertion occurs. $extraLeft/Right + * are optionally added before/after the main insertions. + */ + protected $insertionMap; + /** + * @var string[] Map From "{$node->getType()}->{$subNode}" to string that should be inserted + * between elements of this list subnode. + */ + protected $listInsertionMap; + protected $emptyListInsertionMap; + /** @var int[] Map from "{$node->getType()}->{$subNode}" to token before which the modifiers + * should be reprinted. */ + protected $modifierChangeMap; + + /** + * Creates a pretty printer instance using the given options. + * + * Supported options: + * * bool $shortArraySyntax = false: Whether to use [] instead of array() as the default array + * syntax, if the node does not specify a format. + * + * @param array $options Dictionary of formatting options + */ + public function __construct(array $options = []) { + $this->docStringEndToken = '_DOC_STRING_END_' . mt_rand(); + + $defaultOptions = ['shortArraySyntax' => false]; + $this->options = $options + $defaultOptions; + } + + /** + * Reset pretty printing state. + */ + protected function resetState() { + $this->indentLevel = 0; + $this->nl = "\n"; + $this->origTokens = null; + } + + /** + * Set indentation level + * + * @param int $level Level in number of spaces + */ + protected function setIndentLevel(int $level) { + $this->indentLevel = $level; + $this->nl = "\n" . \str_repeat(' ', $level); + } + + /** + * Increase indentation level. + */ + protected function indent() { + $this->indentLevel += 4; + $this->nl .= ' '; + } + + /** + * Decrease indentation level. + */ + protected function outdent() { + assert($this->indentLevel >= 4); + $this->indentLevel -= 4; + $this->nl = "\n" . str_repeat(' ', $this->indentLevel); + } + + /** + * Pretty prints an array of statements. + * + * @param Node[] $stmts Array of statements + * + * @return string Pretty printed statements + */ + public function prettyPrint(array $stmts) : string { + $this->resetState(); + $this->preprocessNodes($stmts); + + return ltrim($this->handleMagicTokens($this->pStmts($stmts, false))); + } + + /** + * Pretty prints an expression. + * + * @param Expr $node Expression node + * + * @return string Pretty printed node + */ + public function prettyPrintExpr(Expr $node) : string { + $this->resetState(); + return $this->handleMagicTokens($this->p($node)); + } + + /** + * Pretty prints a file of statements (includes the opening prettyPrint($stmts); + + if ($stmts[0] instanceof Stmt\InlineHTML) { + $p = preg_replace('/^<\?php\s+\?>\n?/', '', $p); + } + if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) { + $p = preg_replace('/<\?php$/', '', rtrim($p)); + } + + return $p; + } + + /** + * Preprocesses the top-level nodes to initialize pretty printer state. + * + * @param Node[] $nodes Array of nodes + */ + protected function preprocessNodes(array $nodes) { + /* We can use semicolon-namespaces unless there is a global namespace declaration */ + $this->canUseSemicolonNamespaces = true; + foreach ($nodes as $node) { + if ($node instanceof Stmt\Namespace_ && null === $node->name) { + $this->canUseSemicolonNamespaces = false; + break; + } + } + } + + /** + * Handles (and removes) no-indent and doc-string-end tokens. + * + * @param string $str + * @return string + */ + protected function handleMagicTokens(string $str) : string { + // Replace doc-string-end tokens with nothing or a newline + $str = str_replace($this->docStringEndToken . ";\n", ";\n", $str); + $str = str_replace($this->docStringEndToken, "\n", $str); + + return $str; + } + + /** + * Pretty prints an array of nodes (statements) and indents them optionally. + * + * @param Node[] $nodes Array of nodes + * @param bool $indent Whether to indent the printed nodes + * + * @return string Pretty printed statements + */ + protected function pStmts(array $nodes, bool $indent = true) : string { + if ($indent) { + $this->indent(); + } + + $result = ''; + foreach ($nodes as $node) { + $comments = $node->getComments(); + if ($comments) { + $result .= $this->nl . $this->pComments($comments); + if ($node instanceof Stmt\Nop) { + continue; + } + } + + $result .= $this->nl . $this->p($node); + } + + if ($indent) { + $this->outdent(); + } + + return $result; + } + + /** + * Pretty-print an infix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param Node $leftNode Left-hand side node + * @param string $operatorString String representation of the operator + * @param Node $rightNode Right-hand side node + * + * @return string Pretty printed infix operation + */ + protected function pInfixOp(string $class, Node $leftNode, string $operatorString, Node $rightNode) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + + return $this->pPrec($leftNode, $precedence, $associativity, -1) + . $operatorString + . $this->pPrec($rightNode, $precedence, $associativity, 1); + } + + /** + * Pretty-print a prefix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node + * + * @return string Pretty printed prefix operation + */ + protected function pPrefixOp(string $class, string $operatorString, Node $node) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $operatorString . $this->pPrec($node, $precedence, $associativity, 1); + } + + /** + * Pretty-print a postfix operation while taking precedence into account. + * + * @param string $class Node class of operator + * @param string $operatorString String representation of the operator + * @param Node $node Node + * + * @return string Pretty printed postfix operation + */ + protected function pPostfixOp(string $class, Node $node, string $operatorString) : string { + list($precedence, $associativity) = $this->precedenceMap[$class]; + return $this->pPrec($node, $precedence, $associativity, -1) . $operatorString; + } + + /** + * Prints an expression node with the least amount of parentheses necessary to preserve the meaning. + * + * @param Node $node Node to pretty print + * @param int $parentPrecedence Precedence of the parent operator + * @param int $parentAssociativity Associativity of parent operator + * (-1 is left, 0 is nonassoc, 1 is right) + * @param int $childPosition Position of the node relative to the operator + * (-1 is left, 1 is right) + * + * @return string The pretty printed node + */ + protected function pPrec(Node $node, int $parentPrecedence, int $parentAssociativity, int $childPosition) : string { + $class = \get_class($node); + if (isset($this->precedenceMap[$class])) { + $childPrecedence = $this->precedenceMap[$class][0]; + if ($childPrecedence > $parentPrecedence + || ($parentPrecedence === $childPrecedence && $parentAssociativity !== $childPosition) + ) { + return '(' . $this->p($node) . ')'; + } + } + + return $this->p($node); + } + + /** + * Pretty prints an array of nodes and implodes the printed values. + * + * @param Node[] $nodes Array of Nodes to be printed + * @param string $glue Character to implode with + * + * @return string Imploded pretty printed nodes + */ + protected function pImplode(array $nodes, string $glue = '') : string { + $pNodes = []; + foreach ($nodes as $node) { + if (null === $node) { + $pNodes[] = ''; + } else { + $pNodes[] = $this->p($node); + } + } + + return implode($glue, $pNodes); + } + + /** + * Pretty prints an array of nodes and implodes the printed values with commas. + * + * @param Node[] $nodes Array of Nodes to be printed + * + * @return string Comma separated pretty printed nodes + */ + protected function pCommaSeparated(array $nodes) : string { + return $this->pImplode($nodes, ', '); + } + + /** + * Pretty prints a comma-separated list of nodes in multiline style, including comments. + * + * The result includes a leading newline and one level of indentation (same as pStmts). + * + * @param Node[] $nodes Array of Nodes to be printed + * @param bool $trailingComma Whether to use a trailing comma + * + * @return string Comma separated pretty printed nodes in multiline style + */ + protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma) : string { + $this->indent(); + + $result = ''; + $lastIdx = count($nodes) - 1; + foreach ($nodes as $idx => $node) { + if ($node !== null) { + $comments = $node->getComments(); + if ($comments) { + $result .= $this->nl . $this->pComments($comments); + } + + $result .= $this->nl . $this->p($node); + } else { + $result .= $this->nl; + } + if ($trailingComma || $idx !== $lastIdx) { + $result .= ','; + } + } + + $this->outdent(); + return $result; + } + + /** + * Prints reformatted text of the passed comments. + * + * @param Comment[] $comments List of comments + * + * @return string Reformatted text of comments + */ + protected function pComments(array $comments) : string { + $formattedComments = []; + + foreach ($comments as $comment) { + $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText()); + } + + return implode($this->nl, $formattedComments); + } + + /** + * Perform a format-preserving pretty print of an AST. + * + * The format preservation is best effort. For some changes to the AST the formatting will not + * be preserved (at least not locally). + * + * In order to use this method a number of prerequisites must be satisfied: + * * The startTokenPos and endTokenPos attributes in the lexer must be enabled. + * * The CloningVisitor must be run on the AST prior to modification. + * * The original tokens must be provided, using the getTokens() method on the lexer. + * + * @param Node[] $stmts Modified AST with links to original AST + * @param Node[] $origStmts Original AST with token offset information + * @param array $origTokens Tokens of the original code + * + * @return string + */ + public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens) : string { + $this->initializeNodeListDiffer(); + $this->initializeLabelCharMap(); + $this->initializeFixupMap(); + $this->initializeRemovalMap(); + $this->initializeInsertionMap(); + $this->initializeListInsertionMap(); + $this->initializeEmptyListInsertionMap(); + $this->initializeModifierChangeMap(); + + $this->resetState(); + $this->origTokens = new TokenStream($origTokens); + + $this->preprocessNodes($stmts); + + $pos = 0; + $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null); + if (null !== $result) { + $result .= $this->origTokens->getTokenCode($pos, count($origTokens), 0); + } else { + // Fallback + // TODO Add pStmts($stmts, false); + } + + return ltrim($this->handleMagicTokens($result)); + } + + protected function pFallback(Node $node) { + return $this->{'p' . $node->getType()}($node); + } + + /** + * Pretty prints a node. + * + * This method also handles formatting preservation for nodes. + * + * @param Node $node Node to be pretty printed + * @param bool $parentFormatPreserved Whether parent node has preserved formatting + * + * @return string Pretty printed node + */ + protected function p(Node $node, $parentFormatPreserved = false) : string { + // No orig tokens means this is a normal pretty print without preservation of formatting + if (!$this->origTokens) { + return $this->{'p' . $node->getType()}($node); + } + + /** @var Node $origNode */ + $origNode = $node->getAttribute('origNode'); + if (null === $origNode) { + return $this->pFallback($node); + } + + $class = \get_class($node); + \assert($class === \get_class($origNode)); + + $startPos = $origNode->getStartTokenPos(); + $endPos = $origNode->getEndTokenPos(); + \assert($startPos >= 0 && $endPos >= 0); + + $fallbackNode = $node; + if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) { + // Normalize node structure of anonymous classes + $node = PrintableNewAnonClassNode::fromNewNode($node); + $origNode = PrintableNewAnonClassNode::fromNewNode($origNode); + } + + // InlineHTML node does not contain closing and opening PHP tags. If the parent formatting + // is not preserved, then we need to use the fallback code to make sure the tags are + // printed. + if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) { + return $this->pFallback($fallbackNode); + } + + $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos); + + $type = $node->getType(); + $fixupInfo = $this->fixupMap[$class] ?? null; + + $result = ''; + $pos = $startPos; + foreach ($node->getSubNodeNames() as $subNodeName) { + $subNode = $node->$subNodeName; + $origSubNode = $origNode->$subNodeName; + + if ((!$subNode instanceof Node && $subNode !== null) + || (!$origSubNode instanceof Node && $origSubNode !== null) + ) { + if ($subNode === $origSubNode) { + // Unchanged, can reuse old code + continue; + } + + if (is_array($subNode) && is_array($origSubNode)) { + // Array subnode changed, we might be able to reconstruct it + $listResult = $this->pArray( + $subNode, $origSubNode, $pos, $indentAdjustment, $type, $subNodeName, + $fixupInfo[$subNodeName] ?? null + ); + if (null === $listResult) { + return $this->pFallback($fallbackNode); + } + + $result .= $listResult; + continue; + } + + if (is_int($subNode) && is_int($origSubNode)) { + // Check if this is a modifier change + $key = $type . '->' . $subNodeName; + if (!isset($this->modifierChangeMap[$key])) { + return $this->pFallback($fallbackNode); + } + + $findToken = $this->modifierChangeMap[$key]; + $result .= $this->pModifiers($subNode); + $pos = $this->origTokens->findRight($pos, $findToken); + continue; + } + + // If a non-node, non-array subnode changed, we don't be able to do a partial + // reconstructions, as we don't have enough offset information. Pretty print the + // whole node instead. + return $this->pFallback($fallbackNode); + } + + $extraLeft = ''; + $extraRight = ''; + if ($origSubNode !== null) { + $subStartPos = $origSubNode->getStartTokenPos(); + $subEndPos = $origSubNode->getEndTokenPos(); + \assert($subStartPos >= 0 && $subEndPos >= 0); + } else { + if ($subNode === null) { + // Both null, nothing to do + continue; + } + + // A node has been inserted, check if we have insertion information for it + $key = $type . '->' . $subNodeName; + if (!isset($this->insertionMap[$key])) { + return $this->pFallback($fallbackNode); + } + + list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key]; + if (null !== $findToken) { + $subStartPos = $this->origTokens->findRight($pos, $findToken) + + (int) !$beforeToken; + } else { + $subStartPos = $pos; + } + + if (null === $extraLeft && null !== $extraRight) { + // If inserting on the right only, skipping whitespace looks better + $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos); + } + $subEndPos = $subStartPos - 1; + } + + if (null === $subNode) { + // A node has been removed, check if we have removal information for it + $key = $type . '->' . $subNodeName; + if (!isset($this->removalMap[$key])) { + return $this->pFallback($fallbackNode); + } + + // Adjust positions to account for additional tokens that must be skipped + $removalInfo = $this->removalMap[$key]; + if (isset($removalInfo['left'])) { + $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1; + } + if (isset($removalInfo['right'])) { + $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1; + } + } + + $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment); + + if (null !== $subNode) { + $result .= $extraLeft; + + $origIndentLevel = $this->indentLevel; + $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment); + + // If it's the same node that was previously in this position, it certainly doesn't + // need fixup. It's important to check this here, because our fixup checks are more + // conservative than strictly necessary. + if (isset($fixupInfo[$subNodeName]) + && $subNode->getAttribute('origNode') !== $origSubNode + ) { + $fixup = $fixupInfo[$subNodeName]; + $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos); + } else { + $res = $this->p($subNode, true); + } + + $this->safeAppend($result, $res); + $this->setIndentLevel($origIndentLevel); + + $result .= $extraRight; + } + + $pos = $subEndPos + 1; + } + + $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); + return $result; + } + + /** + * Perform a format-preserving pretty print of an array. + * + * @param array $nodes New nodes + * @param array $origNodes Original nodes + * @param int $pos Current token position (updated by reference) + * @param int $indentAdjustment Adjustment for indentation + * @param string $parentNodeType Type of the containing node. + * @param string $subNodeName Name of array subnode. + * @param null|int $fixup Fixup information for array item nodes + * + * @return null|string Result of pretty print or null if cannot preserve formatting + */ + protected function pArray( + array $nodes, array $origNodes, int &$pos, int $indentAdjustment, + string $parentNodeType, string $subNodeName, $fixup + ) { + $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes); + + $mapKey = $parentNodeType . '->' . $subNodeName; + $insertStr = $this->listInsertionMap[$mapKey] ?? null; + + $beforeFirstKeepOrReplace = true; + $delayedAdd = []; + $lastElemIndentLevel = $this->indentLevel; + + $insertNewline = false; + if ($insertStr === "\n") { + $insertStr = ''; + $insertNewline = true; + } + + if ($subNodeName === 'stmts' && \count($origNodes) === 1 && \count($nodes) !== 1) { + $startPos = $origNodes[0]->getStartTokenPos(); + $endPos = $origNodes[0]->getEndTokenPos(); + \assert($startPos >= 0 && $endPos >= 0); + if (!$this->origTokens->haveBraces($startPos, $endPos)) { + // This was a single statement without braces, but either additional statements + // have been added, or the single statement has been removed. This requires the + // addition of braces. For now fall back. + // TODO: Try to preserve formatting + return null; + } + } + + $result = ''; + foreach ($diff as $i => $diffElem) { + $diffType = $diffElem->type; + /** @var Node|null $arrItem */ + $arrItem = $diffElem->new; + /** @var Node|null $origArrItem */ + $origArrItem = $diffElem->old; + + if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { + $beforeFirstKeepOrReplace = false; + + if ($origArrItem === null || $arrItem === null) { + // We can only handle the case where both are null + if ($origArrItem === $arrItem) { + continue; + } + return null; + } + + if (!$arrItem instanceof Node || !$origArrItem instanceof Node) { + // We can only deal with nodes. This can occur for Names, which use string arrays. + return null; + } + + $itemStartPos = $origArrItem->getStartTokenPos(); + $itemEndPos = $origArrItem->getEndTokenPos(); + \assert($itemStartPos >= 0 && $itemEndPos >= 0); + + if ($itemEndPos < $itemStartPos) { + // End can be before start for Nop nodes, because offsets refer to non-whitespace + // locations, which for an "empty" node might result in an inverted order. + assert($origArrItem instanceof Stmt\Nop); + continue; + } + + $origIndentLevel = $this->indentLevel; + $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment; + $this->setIndentLevel($lastElemIndentLevel); + + $comments = $arrItem->getComments(); + $origComments = $origArrItem->getComments(); + $commentStartPos = $origComments ? $origComments[0]->getTokenPos() : $itemStartPos; + \assert($commentStartPos >= 0); + + $commentsChanged = $comments !== $origComments; + if ($commentsChanged) { + // Remove old comments + $itemStartPos = $commentStartPos; + } + + if (!empty($delayedAdd)) { + $result .= $this->origTokens->getTokenCode( + $pos, $commentStartPos, $indentAdjustment); + + /** @var Node $delayedAddNode */ + foreach ($delayedAdd as $delayedAddNode) { + if ($insertNewline) { + $delayedAddComments = $delayedAddNode->getComments(); + if ($delayedAddComments) { + $result .= $this->pComments($delayedAddComments) . $this->nl; + } + } + + $this->safeAppend($result, $this->p($delayedAddNode, true)); + + if ($insertNewline) { + $result .= $insertStr . $this->nl; + } else { + $result .= $insertStr; + } + } + + $result .= $this->origTokens->getTokenCode( + $commentStartPos, $itemStartPos, $indentAdjustment); + + $delayedAdd = []; + } else { + $result .= $this->origTokens->getTokenCode( + $pos, $itemStartPos, $indentAdjustment); + } + + if ($commentsChanged && $comments) { + // Add new comments + $result .= $this->pComments($comments) . $this->nl; + } + } elseif ($diffType === DiffElem::TYPE_ADD) { + if (null === $insertStr) { + // We don't have insertion information for this list type + return null; + } + + if ($insertStr === ', ' && $this->isMultiline($origNodes)) { + $insertStr = ','; + $insertNewline = true; + } + + if ($beforeFirstKeepOrReplace) { + // Will be inserted at the next "replace" or "keep" element + $delayedAdd[] = $arrItem; + continue; + } + + $itemStartPos = $pos; + $itemEndPos = $pos - 1; + + $origIndentLevel = $this->indentLevel; + $this->setIndentLevel($lastElemIndentLevel); + + if ($insertNewline) { + $comments = $arrItem->getComments(); + if ($comments) { + $result .= $this->nl . $this->pComments($comments); + } + $result .= $insertStr . $this->nl; + } else { + $result .= $insertStr; + } + } elseif ($diffType === DiffElem::TYPE_REMOVE) { + if ($i === 0) { + // TODO Handle removal at the start + return null; + } + + if (!$origArrItem instanceof Node) { + // We only support removal for nodes + return null; + } + + $itemEndPos = $origArrItem->getEndTokenPos(); + \assert($itemEndPos >= 0); + + $pos = $itemEndPos + 1; + continue; + } else { + throw new \Exception("Shouldn't happen"); + } + + if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) { + $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos); + } else { + $res = $this->p($arrItem, true); + } + $this->safeAppend($result, $res); + + $this->setIndentLevel($origIndentLevel); + $pos = $itemEndPos + 1; + } + + if (!empty($delayedAdd)) { + if (!isset($this->emptyListInsertionMap[$mapKey])) { + return null; + } + + list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey]; + if (null !== $findToken) { + $insertPos = $this->origTokens->findRight($pos, $findToken) + 1; + $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment); + $pos = $insertPos; + } + + $first = true; + $result .= $extraLeft; + foreach ($delayedAdd as $delayedAddNode) { + if (!$first) { + $result .= $insertStr; + } + $result .= $this->p($delayedAddNode, true); + $first = false; + } + $result .= $extraRight; + } + + return $result; + } + + /** + * Print node with fixups. + * + * Fixups here refer to the addition of extra parentheses, braces or other characters, that + * are required to preserve program semantics in a certain context (e.g. to maintain precedence + * or because only certain expressions are allowed in certain places). + * + * @param int $fixup Fixup type + * @param Node $subNode Subnode to print + * @param string|null $parentClass Class of parent node + * @param int $subStartPos Original start pos of subnode + * @param int $subEndPos Original end pos of subnode + * + * @return string Result of fixed-up print of subnode + */ + protected function pFixup(int $fixup, Node $subNode, $parentClass, int $subStartPos, int $subEndPos) : string { + switch ($fixup) { + case self::FIXUP_PREC_LEFT: + case self::FIXUP_PREC_RIGHT: + if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { + list($precedence, $associativity) = $this->precedenceMap[$parentClass]; + return $this->pPrec($subNode, $precedence, $associativity, + $fixup === self::FIXUP_PREC_LEFT ? -1 : 1); + } + break; + case self::FIXUP_CALL_LHS: + if ($this->callLhsRequiresParens($subNode) + && !$this->origTokens->haveParens($subStartPos, $subEndPos) + ) { + return '(' . $this->p($subNode) . ')'; + } + break; + case self::FIXUP_DEREF_LHS: + if ($this->dereferenceLhsRequiresParens($subNode) + && !$this->origTokens->haveParens($subStartPos, $subEndPos) + ) { + return '(' . $this->p($subNode) . ')'; + } + break; + case self::FIXUP_BRACED_NAME: + case self::FIXUP_VAR_BRACED_NAME: + if ($subNode instanceof Expr + && !$this->origTokens->haveBraces($subStartPos, $subEndPos) + ) { + return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '') + . '{' . $this->p($subNode) . '}'; + } + break; + case self::FIXUP_ENCAPSED: + if (!$subNode instanceof Scalar\EncapsedStringPart + && !$this->origTokens->haveBraces($subStartPos, $subEndPos) + ) { + return '{' . $this->p($subNode) . '}'; + } + break; + default: + throw new \Exception('Cannot happen'); + } + + // Nothing special to do + return $this->p($subNode); + } + + /** + * Appends to a string, ensuring whitespace between label characters. + * + * Example: "echo" and "$x" result in "echo$x", but "echo" and "x" result in "echo x". + * Without safeAppend the result would be "echox", which does not preserve semantics. + * + * @param string $str + * @param string $append + */ + protected function safeAppend(string &$str, string $append) { + if ($str === "") { + $str = $append; + return; + } + + if ($append === "") { + return; + } + + if (!$this->labelCharMap[$append[0]] + || !$this->labelCharMap[$str[\strlen($str) - 1]]) { + $str .= $append; + } else { + $str .= " " . $append; + } + } + + /** + * Determines whether the LHS of a call must be wrapped in parenthesis. + * + * @param Node $node LHS of a call + * + * @return bool Whether parentheses are required + */ + protected function callLhsRequiresParens(Node $node) : bool { + return !($node instanceof Node\Name + || $node instanceof Expr\Variable + || $node instanceof Expr\ArrayDimFetch + || $node instanceof Expr\FuncCall + || $node instanceof Expr\MethodCall + || $node instanceof Expr\StaticCall + || $node instanceof Expr\Array_); + } + + /** + * Determines whether the LHS of a dereferencing operation must be wrapped in parenthesis. + * + * @param Node $node LHS of dereferencing operation + * + * @return bool Whether parentheses are required + */ + protected function dereferenceLhsRequiresParens(Node $node) : bool { + return !($node instanceof Expr\Variable + || $node instanceof Node\Name + || $node instanceof Expr\ArrayDimFetch + || $node instanceof Expr\PropertyFetch + || $node instanceof Expr\StaticPropertyFetch + || $node instanceof Expr\FuncCall + || $node instanceof Expr\MethodCall + || $node instanceof Expr\StaticCall + || $node instanceof Expr\Array_ + || $node instanceof Scalar\String_ + || $node instanceof Expr\ConstFetch + || $node instanceof Expr\ClassConstFetch); + } + + /** + * Print modifiers, including trailing whitespace. + * + * @param int $modifiers Modifier mask to print + * + * @return string Printed modifiers + */ + protected function pModifiers(int $modifiers) { + return ($modifiers & Stmt\Class_::MODIFIER_PUBLIC ? 'public ' : '') + . ($modifiers & Stmt\Class_::MODIFIER_PROTECTED ? 'protected ' : '') + . ($modifiers & Stmt\Class_::MODIFIER_PRIVATE ? 'private ' : '') + . ($modifiers & Stmt\Class_::MODIFIER_STATIC ? 'static ' : '') + . ($modifiers & Stmt\Class_::MODIFIER_ABSTRACT ? 'abstract ' : '') + . ($modifiers & Stmt\Class_::MODIFIER_FINAL ? 'final ' : ''); + } + + /** + * Determine whether a list of nodes uses multiline formatting. + * + * @param (Node|null)[] $nodes Node list + * + * @return bool Whether multiline formatting is used + */ + protected function isMultiline(array $nodes) : bool { + if (\count($nodes) < 2) { + return false; + } + + $pos = -1; + foreach ($nodes as $node) { + if (null === $node) { + continue; + } + + $endPos = $node->getEndTokenPos() + 1; + if ($pos >= 0) { + $text = $this->origTokens->getTokenCode($pos, $endPos, 0); + if (false === strpos($text, "\n")) { + // We require that a newline is present between *every* item. If the formatting + // is inconsistent, with only some items having newlines, we don't consider it + // as multiline + return false; + } + } + $pos = $endPos; + } + + return true; + } + + /** + * Lazily initializes label char map. + * + * The label char map determines whether a certain character may occur in a label. + */ + protected function initializeLabelCharMap() { + if ($this->labelCharMap) return; + + $this->labelCharMap = []; + for ($i = 0; $i < 256; $i++) { + // Since PHP 7.1 The lower range is 0x80. However, we also want to support code for + // older versions. + $this->labelCharMap[chr($i)] = $i >= 0x7f || ctype_alnum($i); + } + } + + /** + * Lazily initializes node list differ. + * + * The node list differ is used to determine differences between two array subnodes. + */ + protected function initializeNodeListDiffer() { + if ($this->nodeListDiffer) return; + + $this->nodeListDiffer = new Internal\Differ(function ($a, $b) { + if ($a instanceof Node && $b instanceof Node) { + return $a === $b->getAttribute('origNode'); + } + // Can happen for array destructuring + return $a === null && $b === null; + }); + } + + /** + * Lazily initializes fixup map. + * + * The fixup map is used to determine whether a certain subnode of a certain node may require + * some kind of "fixup" operation, e.g. the addition of parenthesis or braces. + */ + protected function initializeFixupMap() { + if ($this->fixupMap) return; + + $this->fixupMap = [ + Expr\PreInc::class => ['var' => self::FIXUP_PREC_RIGHT], + Expr\PreDec::class => ['var' => self::FIXUP_PREC_RIGHT], + Expr\PostInc::class => ['var' => self::FIXUP_PREC_LEFT], + Expr\PostDec::class => ['var' => self::FIXUP_PREC_LEFT], + Expr\Instanceof_::class => [ + 'expr' => self::FIXUP_PREC_LEFT, + 'class' => self::FIXUP_PREC_RIGHT, + ], + Expr\Ternary::class => [ + 'cond' => self::FIXUP_PREC_LEFT, + 'else' => self::FIXUP_PREC_RIGHT, + ], + + Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], + Expr\StaticCall::class => ['class' => self::FIXUP_DEREF_LHS], + Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], + Expr\MethodCall::class => [ + 'var' => self::FIXUP_DEREF_LHS, + 'name' => self::FIXUP_BRACED_NAME, + ], + Expr\StaticPropertyFetch::class => [ + 'class' => self::FIXUP_DEREF_LHS, + 'name' => self::FIXUP_VAR_BRACED_NAME, + ], + Expr\PropertyFetch::class => [ + 'var' => self::FIXUP_DEREF_LHS, + 'name' => self::FIXUP_BRACED_NAME, + ], + Scalar\Encapsed::class => [ + 'parts' => self::FIXUP_ENCAPSED, + ], + ]; + + $binaryOps = [ + BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class, + BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class, + BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class, + BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class, + BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class, + BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class, + BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class, + BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class, + BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, + ]; + foreach ($binaryOps as $binaryOp) { + $this->fixupMap[$binaryOp] = [ + 'left' => self::FIXUP_PREC_LEFT, + 'right' => self::FIXUP_PREC_RIGHT + ]; + } + + $assignOps = [ + Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class, + AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class, + AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class, + AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class + ]; + foreach ($assignOps as $assignOp) { + $this->fixupMap[$assignOp] = [ + 'var' => self::FIXUP_PREC_LEFT, + 'expr' => self::FIXUP_PREC_RIGHT, + ]; + } + + $prefixOps = [ + Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class, + Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class, + Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class, + Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class, + ]; + foreach ($prefixOps as $prefixOp) { + $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_RIGHT]; + } + } + + /** + * Lazily initializes the removal map. + * + * The removal map is used to determine which additional tokens should be returned when a + * certain node is replaced by null. + */ + protected function initializeRemovalMap() { + if ($this->removalMap) return; + + $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE]; + $stripLeft = ['left' => \T_WHITESPACE]; + $stripRight = ['right' => \T_WHITESPACE]; + $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW]; + $stripColon = ['left' => ':']; + $stripEquals = ['left' => '=']; + $this->removalMap = [ + 'Expr_ArrayDimFetch->dim' => $stripBoth, + 'Expr_ArrayItem->key' => $stripDoubleArrow, + 'Expr_ArrowFunction->returnType' => $stripColon, + 'Expr_Closure->returnType' => $stripColon, + 'Expr_Exit->expr' => $stripBoth, + 'Expr_Ternary->if' => $stripBoth, + 'Expr_Yield->key' => $stripDoubleArrow, + 'Expr_Yield->value' => $stripBoth, + 'Param->type' => $stripRight, + 'Param->default' => $stripEquals, + 'Stmt_Break->num' => $stripBoth, + 'Stmt_ClassMethod->returnType' => $stripColon, + 'Stmt_Class->extends' => ['left' => \T_EXTENDS], + 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], + 'Stmt_Continue->num' => $stripBoth, + 'Stmt_Foreach->keyVar' => $stripDoubleArrow, + 'Stmt_Function->returnType' => $stripColon, + 'Stmt_If->else' => $stripLeft, + 'Stmt_Namespace->name' => $stripLeft, + 'Stmt_Property->type' => $stripRight, + 'Stmt_PropertyProperty->default' => $stripEquals, + 'Stmt_Return->expr' => $stripBoth, + 'Stmt_StaticVar->default' => $stripEquals, + 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, + 'Stmt_TryCatch->finally' => $stripLeft, + // 'Stmt_Case->cond': Replace with "default" + // 'Stmt_Class->name': Unclear what to do + // 'Stmt_Declare->stmts': Not a plain node + // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node + ]; + } + + protected function initializeInsertionMap() { + if ($this->insertionMap) return; + + // TODO: "yield" where both key and value are inserted doesn't work + // [$find, $beforeToken, $extraLeft, $extraRight] + $this->insertionMap = [ + 'Expr_ArrayDimFetch->dim' => ['[', false, null, null], + 'Expr_ArrayItem->key' => [null, false, null, ' => '], + 'Expr_ArrowFunction->returnType' => [')', false, ' : ', null], + 'Expr_Closure->returnType' => [')', false, ' : ', null], + 'Expr_Ternary->if' => ['?', false, ' ', ' '], + 'Expr_Yield->key' => [\T_YIELD, false, null, ' => '], + 'Expr_Yield->value' => [\T_YIELD, false, ' ', null], + 'Param->type' => [null, false, null, ' '], + 'Param->default' => [null, false, ' = ', null], + 'Stmt_Break->num' => [\T_BREAK, false, ' ', null], + 'Stmt_ClassMethod->returnType' => [')', false, ' : ', null], + 'Stmt_Class->extends' => [null, false, ' extends ', null], + 'Expr_PrintableNewAnonClass->extends' => [null, ' extends ', null], + 'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null], + 'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '], + 'Stmt_Function->returnType' => [')', false, ' : ', null], + 'Stmt_If->else' => [null, false, ' ', null], + 'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null], + 'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '], + 'Stmt_PropertyProperty->default' => [null, false, ' = ', null], + 'Stmt_Return->expr' => [\T_RETURN, false, ' ', null], + 'Stmt_StaticVar->default' => [null, false, ' = ', null], + //'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, false, ' ', null], // TODO + 'Stmt_TryCatch->finally' => [null, false, ' ', null], + + // 'Expr_Exit->expr': Complicated due to optional () + // 'Stmt_Case->cond': Conversion from default to case + // 'Stmt_Class->name': Unclear + // 'Stmt_Declare->stmts': Not a proper node + // 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node + ]; + } + + protected function initializeListInsertionMap() { + if ($this->listInsertionMap) return; + + $this->listInsertionMap = [ + // special + //'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully + //'Scalar_Encapsed->parts' => '', + 'Stmt_Catch->types' => '|', + 'Stmt_If->elseifs' => ' ', + 'Stmt_TryCatch->catches' => ' ', + + // comma-separated lists + 'Expr_Array->items' => ', ', + 'Expr_ArrowFunction->params' => ', ', + 'Expr_Closure->params' => ', ', + 'Expr_Closure->uses' => ', ', + 'Expr_FuncCall->args' => ', ', + 'Expr_Isset->vars' => ', ', + 'Expr_List->items' => ', ', + 'Expr_MethodCall->args' => ', ', + 'Expr_New->args' => ', ', + 'Expr_PrintableNewAnonClass->args' => ', ', + 'Expr_StaticCall->args' => ', ', + 'Stmt_ClassConst->consts' => ', ', + 'Stmt_ClassMethod->params' => ', ', + 'Stmt_Class->implements' => ', ', + 'Expr_PrintableNewAnonClass->implements' => ', ', + 'Stmt_Const->consts' => ', ', + 'Stmt_Declare->declares' => ', ', + 'Stmt_Echo->exprs' => ', ', + 'Stmt_For->init' => ', ', + 'Stmt_For->cond' => ', ', + 'Stmt_For->loop' => ', ', + 'Stmt_Function->params' => ', ', + 'Stmt_Global->vars' => ', ', + 'Stmt_GroupUse->uses' => ', ', + 'Stmt_Interface->extends' => ', ', + 'Stmt_Property->props' => ', ', + 'Stmt_StaticVar->vars' => ', ', + 'Stmt_TraitUse->traits' => ', ', + 'Stmt_TraitUseAdaptation_Precedence->insteadof' => ', ', + 'Stmt_Unset->vars' => ', ', + 'Stmt_Use->uses' => ', ', + + // statement lists + 'Expr_Closure->stmts' => "\n", + 'Stmt_Case->stmts' => "\n", + 'Stmt_Catch->stmts' => "\n", + 'Stmt_Class->stmts' => "\n", + 'Expr_PrintableNewAnonClass->stmts' => "\n", + 'Stmt_Interface->stmts' => "\n", + 'Stmt_Trait->stmts' => "\n", + 'Stmt_ClassMethod->stmts' => "\n", + 'Stmt_Declare->stmts' => "\n", + 'Stmt_Do->stmts' => "\n", + 'Stmt_ElseIf->stmts' => "\n", + 'Stmt_Else->stmts' => "\n", + 'Stmt_Finally->stmts' => "\n", + 'Stmt_Foreach->stmts' => "\n", + 'Stmt_For->stmts' => "\n", + 'Stmt_Function->stmts' => "\n", + 'Stmt_If->stmts' => "\n", + 'Stmt_Namespace->stmts' => "\n", + 'Stmt_Switch->cases' => "\n", + 'Stmt_TraitUse->adaptations' => "\n", + 'Stmt_TryCatch->stmts' => "\n", + 'Stmt_While->stmts' => "\n", + + // dummy for top-level context + 'File->stmts' => "\n", + ]; + } + + protected function initializeEmptyListInsertionMap() { + if ($this->emptyListInsertionMap) return; + + // TODO Insertion into empty statement lists. + + // [$find, $extraLeft, $extraRight] + $this->emptyListInsertionMap = [ + 'Expr_ArrowFunction->params' => ['(', '', ''], + 'Expr_Closure->uses' => [')', ' use(', ')'], + 'Expr_Closure->params' => ['(', '', ''], + 'Expr_FuncCall->args' => ['(', '', ''], + 'Expr_MethodCall->args' => ['(', '', ''], + 'Expr_New->args' => ['(', '', ''], + 'Expr_PrintableNewAnonClass->args' => ['(', '', ''], + 'Expr_PrintableNewAnonClass->implements' => [null, ' implements ', ''], + 'Expr_StaticCall->args' => ['(', '', ''], + 'Stmt_Class->implements' => [null, ' implements ', ''], + 'Stmt_ClassMethod->params' => ['(', '', ''], + 'Stmt_Interface->extends' => [null, ' extends ', ''], + 'Stmt_Function->params' => ['(', '', ''], + + /* These cannot be empty to start with: + * Expr_Isset->vars + * Stmt_Catch->types + * Stmt_Const->consts + * Stmt_ClassConst->consts + * Stmt_Declare->declares + * Stmt_Echo->exprs + * Stmt_Global->vars + * Stmt_GroupUse->uses + * Stmt_Property->props + * Stmt_StaticVar->vars + * Stmt_TraitUse->traits + * Stmt_TraitUseAdaptation_Precedence->insteadof + * Stmt_Unset->vars + * Stmt_Use->uses + */ + + /* TODO + * Stmt_If->elseifs + * Stmt_TryCatch->catches + * Expr_Array->items + * Expr_List->items + * Stmt_For->init + * Stmt_For->cond + * Stmt_For->loop + */ + ]; + } + + protected function initializeModifierChangeMap() { + if ($this->modifierChangeMap) return; + + $this->modifierChangeMap = [ + 'Stmt_ClassConst->flags' => \T_CONST, + 'Stmt_ClassMethod->flags' => \T_FUNCTION, + 'Stmt_Class->flags' => \T_CLASS, + 'Stmt_Property->flags' => \T_VARIABLE, + //'Stmt_TraitUseAdaptation_Alias->newModifier' => 0, // TODO + ]; + + // List of integer subnodes that are not modifiers: + // Expr_Include->type + // Stmt_GroupUse->type + // Stmt_Use->type + // Stmt_UseUse->type + } +} diff --git a/vendor/nikic/php-parser/phpunit.xml.dist b/vendor/nikic/php-parser/phpunit.xml.dist new file mode 100644 index 0000000..5271264 --- /dev/null +++ b/vendor/nikic/php-parser/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + ./test/ + + + + + + ./lib/PhpParser/ + + + diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/ClassTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/ClassTest.php new file mode 100644 index 0000000..2cfa787 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/ClassTest.php @@ -0,0 +1,153 @@ +createClassBuilder('SomeLogger') + ->extend('BaseLogger') + ->implement('Namespaced\Logger', new Name('SomeInterface')) + ->implement('\Fully\Qualified', 'namespace\NamespaceRelative') + ->getNode() + ; + + $this->assertEquals( + new Stmt\Class_('SomeLogger', [ + 'extends' => new Name('BaseLogger'), + 'implements' => [ + new Name('Namespaced\Logger'), + new Name('SomeInterface'), + new Name\FullyQualified('Fully\Qualified'), + new Name\Relative('NamespaceRelative'), + ], + ]), + $node + ); + } + + public function testAbstract() { + $node = $this->createClassBuilder('Test') + ->makeAbstract() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Class_('Test', [ + 'flags' => Stmt\Class_::MODIFIER_ABSTRACT + ]), + $node + ); + } + + public function testFinal() { + $node = $this->createClassBuilder('Test') + ->makeFinal() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Class_('Test', [ + 'flags' => Stmt\Class_::MODIFIER_FINAL + ]), + $node + ); + } + + public function testStatementOrder() { + $method = new Stmt\ClassMethod('testMethod'); + $property = new Stmt\Property( + Stmt\Class_::MODIFIER_PUBLIC, + [new Stmt\PropertyProperty('testProperty')] + ); + $const = new Stmt\ClassConst([ + new Node\Const_('TEST_CONST', new Node\Scalar\String_('ABC')) + ]); + $use = new Stmt\TraitUse([new Name('SomeTrait')]); + + $node = $this->createClassBuilder('Test') + ->addStmt($method) + ->addStmt($property) + ->addStmts([$const, $use]) + ->getNode() + ; + + $this->assertEquals( + new Stmt\Class_('Test', [ + 'stmts' => [$use, $const, $property, $method] + ]), + $node + ); + } + + public function testDocComment() { + $docComment = <<<'DOC' +/** + * Test + */ +DOC; + $class = $this->createClassBuilder('Test') + ->setDocComment($docComment) + ->getNode(); + + $this->assertEquals( + new Stmt\Class_('Test', [], [ + 'comments' => [ + new Comment\Doc($docComment) + ] + ]), + $class + ); + + $class = $this->createClassBuilder('Test') + ->setDocComment(new Comment\Doc($docComment)) + ->getNode(); + + $this->assertEquals( + new Stmt\Class_('Test', [], [ + 'comments' => [ + new Comment\Doc($docComment) + ] + ]), + $class + ); + } + + public function testInvalidStmtError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Unexpected node of type "Stmt_Echo"'); + $this->createClassBuilder('Test') + ->addStmt(new Stmt\Echo_([])) + ; + } + + public function testInvalidDocComment() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); + $this->createClassBuilder('Test') + ->setDocComment(new Comment('Test')); + } + + public function testEmptyName() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Name cannot be empty'); + $this->createClassBuilder('Test') + ->extend(''); + } + + public function testInvalidName() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Name must be a string or an instance of Node\Name'); + $this->createClassBuilder('Test') + ->extend(['Foo']); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/FunctionTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/FunctionTest.php new file mode 100644 index 0000000..c17045b --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/FunctionTest.php @@ -0,0 +1,114 @@ +createFunctionBuilder('test') + ->makeReturnByRef() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Function_('test', [ + 'byRef' => true + ]), + $node + ); + } + + public function testParams() { + $param1 = new Node\Param(new Variable('test1')); + $param2 = new Node\Param(new Variable('test2')); + $param3 = new Node\Param(new Variable('test3')); + + $node = $this->createFunctionBuilder('test') + ->addParam($param1) + ->addParams([$param2, $param3]) + ->getNode() + ; + + $this->assertEquals( + new Stmt\Function_('test', [ + 'params' => [$param1, $param2, $param3] + ]), + $node + ); + } + + public function testStmts() { + $stmt1 = new Print_(new String_('test1')); + $stmt2 = new Print_(new String_('test2')); + $stmt3 = new Print_(new String_('test3')); + + $node = $this->createFunctionBuilder('test') + ->addStmt($stmt1) + ->addStmts([$stmt2, $stmt3]) + ->getNode() + ; + + $this->assertEquals( + new Stmt\Function_('test', [ + 'stmts' => [ + new Stmt\Expression($stmt1), + new Stmt\Expression($stmt2), + new Stmt\Expression($stmt3), + ] + ]), + $node + ); + } + + public function testDocComment() { + $node = $this->createFunctionBuilder('test') + ->setDocComment('/** Test */') + ->getNode(); + + $this->assertEquals(new Stmt\Function_('test', [], [ + 'comments' => [new Comment\Doc('/** Test */')] + ]), $node); + } + + public function testReturnType() { + $node = $this->createFunctionBuilder('test') + ->setReturnType('void') + ->getNode(); + + $this->assertEquals(new Stmt\Function_('test', [ + 'returnType' => 'void' + ], []), $node); + } + + public function testInvalidNullableVoidType() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('void type cannot be nullable'); + $this->createFunctionBuilder('test')->setReturnType('?void'); + } + + public function testInvalidParamError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected parameter node, got "Name"'); + $this->createFunctionBuilder('test') + ->addParam(new Node\Name('foo')) + ; + } + + public function testAddNonStmt() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected statement or expression node'); + $this->createFunctionBuilder('test') + ->addStmt(new Node\Name('Test')); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/InterfaceTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/InterfaceTest.php new file mode 100644 index 0000000..63ce6b9 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/InterfaceTest.php @@ -0,0 +1,100 @@ +prettyPrint([$node]); + } + + public function testEmpty() { + $contract = $this->createInterfaceBuilder()->getNode(); + $this->assertInstanceOf(Stmt\Interface_::class, $contract); + $this->assertEquals(new Node\Identifier('Contract'), $contract->name); + } + + public function testExtending() { + $contract = $this->createInterfaceBuilder() + ->extend('Space\Root1', 'Root2')->getNode(); + $this->assertEquals( + new Stmt\Interface_('Contract', [ + 'extends' => [ + new Node\Name('Space\Root1'), + new Node\Name('Root2') + ], + ]), $contract + ); + } + + public function testAddMethod() { + $method = new Stmt\ClassMethod('doSomething'); + $contract = $this->createInterfaceBuilder()->addStmt($method)->getNode(); + $this->assertSame([$method], $contract->stmts); + } + + public function testAddConst() { + $const = new Stmt\ClassConst([ + new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458.0)) + ]); + $contract = $this->createInterfaceBuilder()->addStmt($const)->getNode(); + $this->assertSame(299792458.0, $contract->stmts[0]->consts[0]->value->value); + } + + public function testOrder() { + $const = new Stmt\ClassConst([ + new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458)) + ]); + $method = new Stmt\ClassMethod('doSomething'); + $contract = $this->createInterfaceBuilder() + ->addStmt($method) + ->addStmt($const) + ->getNode() + ; + + $this->assertInstanceOf(Stmt\ClassConst::class, $contract->stmts[0]); + $this->assertInstanceOf(Stmt\ClassMethod::class, $contract->stmts[1]); + } + + public function testDocComment() { + $node = $this->createInterfaceBuilder() + ->setDocComment('/** Test */') + ->getNode(); + + $this->assertEquals(new Stmt\Interface_('Contract', [], [ + 'comments' => [new Comment\Doc('/** Test */')] + ]), $node); + } + + public function testInvalidStmtError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Unexpected node of type "Stmt_PropertyProperty"'); + $this->createInterfaceBuilder()->addStmt(new Stmt\PropertyProperty('invalid')); + } + + public function testFullFunctional() { + $const = new Stmt\ClassConst([ + new Node\Const_('SPEED_OF_LIGHT', new DNumber(299792458)) + ]); + $method = new Stmt\ClassMethod('doSomething'); + $contract = $this->createInterfaceBuilder() + ->addStmt($method) + ->addStmt($const) + ->getNode() + ; + + eval($this->dump($contract)); + + $this->assertTrue(interface_exists('Contract', false)); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/MethodTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/MethodTest.php new file mode 100644 index 0000000..529f035 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/MethodTest.php @@ -0,0 +1,162 @@ +createMethodBuilder('test') + ->makePublic() + ->makeAbstract() + ->makeStatic() + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'flags' => Stmt\Class_::MODIFIER_PUBLIC + | Stmt\Class_::MODIFIER_ABSTRACT + | Stmt\Class_::MODIFIER_STATIC, + 'stmts' => null, + ]), + $node + ); + + $node = $this->createMethodBuilder('test') + ->makeProtected() + ->makeFinal() + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'flags' => Stmt\Class_::MODIFIER_PROTECTED + | Stmt\Class_::MODIFIER_FINAL + ]), + $node + ); + + $node = $this->createMethodBuilder('test') + ->makePrivate() + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'type' => Stmt\Class_::MODIFIER_PRIVATE + ]), + $node + ); + } + + public function testReturnByRef() { + $node = $this->createMethodBuilder('test') + ->makeReturnByRef() + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'byRef' => true + ]), + $node + ); + } + + public function testParams() { + $param1 = new Node\Param(new Variable('test1')); + $param2 = new Node\Param(new Variable('test2')); + $param3 = new Node\Param(new Variable('test3')); + + $node = $this->createMethodBuilder('test') + ->addParam($param1) + ->addParams([$param2, $param3]) + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'params' => [$param1, $param2, $param3] + ]), + $node + ); + } + + public function testStmts() { + $stmt1 = new Print_(new String_('test1')); + $stmt2 = new Print_(new String_('test2')); + $stmt3 = new Print_(new String_('test3')); + + $node = $this->createMethodBuilder('test') + ->addStmt($stmt1) + ->addStmts([$stmt2, $stmt3]) + ->getNode() + ; + + $this->assertEquals( + new Stmt\ClassMethod('test', [ + 'stmts' => [ + new Stmt\Expression($stmt1), + new Stmt\Expression($stmt2), + new Stmt\Expression($stmt3), + ] + ]), + $node + ); + } + public function testDocComment() { + $node = $this->createMethodBuilder('test') + ->setDocComment('/** Test */') + ->getNode(); + + $this->assertEquals(new Stmt\ClassMethod('test', [], [ + 'comments' => [new Comment\Doc('/** Test */')] + ]), $node); + } + + public function testReturnType() { + $node = $this->createMethodBuilder('test') + ->setReturnType('bool') + ->getNode(); + $this->assertEquals(new Stmt\ClassMethod('test', [ + 'returnType' => 'bool' + ], []), $node); + } + + public function testAddStmtToAbstractMethodError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot add statements to an abstract method'); + $this->createMethodBuilder('test') + ->makeAbstract() + ->addStmt(new Print_(new String_('test'))) + ; + } + + public function testMakeMethodWithStmtsAbstractError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot make method with statements abstract'); + $this->createMethodBuilder('test') + ->addStmt(new Print_(new String_('test'))) + ->makeAbstract() + ; + } + + public function testInvalidParamError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected parameter node, got "Name"'); + $this->createMethodBuilder('test') + ->addParam(new Node\Name('foo')) + ; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/NamespaceTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/NamespaceTest.php new file mode 100644 index 0000000..689001b --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/NamespaceTest.php @@ -0,0 +1,46 @@ + [$docComment]] + ); + + $node = $this->createNamespaceBuilder('Name\Space') + ->addStmt($stmt1) + ->addStmts([$stmt2, $stmt3]) + ->setDocComment($docComment) + ->getNode() + ; + $this->assertEquals($expected, $node); + + $node = $this->createNamespaceBuilder(new Node\Name(['Name', 'Space'])) + ->setDocComment($docComment) + ->addStmts([$stmt1, $stmt2]) + ->addStmt($stmt3) + ->getNode() + ; + $this->assertEquals($expected, $node); + + $node = $this->createNamespaceBuilder(null)->getNode(); + $this->assertNull($node->name); + $this->assertEmpty($node->stmts); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/ParamTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/ParamTest.php new file mode 100644 index 0000000..c9cbfca --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/ParamTest.php @@ -0,0 +1,166 @@ +createParamBuilder('test') + ->setDefault($value) + ->getNode() + ; + + $this->assertEquals($expectedValueNode, $node->default); + } + + public function provideTestDefaultValues() { + return [ + [ + null, + new Expr\ConstFetch(new Node\Name('null')) + ], + [ + true, + new Expr\ConstFetch(new Node\Name('true')) + ], + [ + false, + new Expr\ConstFetch(new Node\Name('false')) + ], + [ + 31415, + new Scalar\LNumber(31415) + ], + [ + 3.1415, + new Scalar\DNumber(3.1415) + ], + [ + 'Hallo World', + new Scalar\String_('Hallo World') + ], + [ + [1, 2, 3], + new Expr\Array_([ + new Expr\ArrayItem(new Scalar\LNumber(1)), + new Expr\ArrayItem(new Scalar\LNumber(2)), + new Expr\ArrayItem(new Scalar\LNumber(3)), + ]) + ], + [ + ['foo' => 'bar', 'bar' => 'foo'], + new Expr\Array_([ + new Expr\ArrayItem( + new Scalar\String_('bar'), + new Scalar\String_('foo') + ), + new Expr\ArrayItem( + new Scalar\String_('foo'), + new Scalar\String_('bar') + ), + ]) + ], + [ + new Scalar\MagicConst\Dir, + new Scalar\MagicConst\Dir + ] + ]; + } + + /** + * @dataProvider provideTestTypes + */ + public function testTypes($typeHint, $expectedType) { + $node = $this->createParamBuilder('test') + ->setTypeHint($typeHint) + ->getNode() + ; + $type = $node->type; + + /* Manually implement comparison to avoid __toString stupidity */ + if ($expectedType instanceof Node\NullableType) { + $this->assertInstanceOf(get_class($expectedType), $type); + $expectedType = $expectedType->type; + $type = $type->type; + } + + $this->assertInstanceOf(get_class($expectedType), $type); + $this->assertEquals($expectedType, $type); + } + + public function provideTestTypes() { + return [ + ['array', new Node\Identifier('array')], + ['callable', new Node\Identifier('callable')], + ['bool', new Node\Identifier('bool')], + ['int', new Node\Identifier('int')], + ['float', new Node\Identifier('float')], + ['string', new Node\Identifier('string')], + ['iterable', new Node\Identifier('iterable')], + ['object', new Node\Identifier('object')], + ['Array', new Node\Identifier('array')], + ['CALLABLE', new Node\Identifier('callable')], + ['Some\Class', new Node\Name('Some\Class')], + ['\Foo', new Node\Name\FullyQualified('Foo')], + ['self', new Node\Name('self')], + ['?array', new Node\NullableType(new Node\Identifier('array'))], + ['?Some\Class', new Node\NullableType(new Node\Name('Some\Class'))], + [new Node\Name('Some\Class'), new Node\Name('Some\Class')], + [ + new Node\NullableType(new Node\Identifier('int')), + new Node\NullableType(new Node\Identifier('int')) + ], + [ + new Node\NullableType(new Node\Name('Some\Class')), + new Node\NullableType(new Node\Name('Some\Class')) + ], + ]; + } + + public function testVoidTypeError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Parameter type cannot be void'); + $this->createParamBuilder('test')->setType('void'); + } + + public function testInvalidTypeError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Type must be a string, or an instance of Name, Identifier or NullableType'); + $this->createParamBuilder('test')->setType(new \stdClass); + } + + public function testByRef() { + $node = $this->createParamBuilder('test') + ->makeByRef() + ->getNode() + ; + + $this->assertEquals( + new Node\Param(new Expr\Variable('test'), null, null, true), + $node + ); + } + + public function testVariadic() { + $node = $this->createParamBuilder('test') + ->makeVariadic() + ->getNode() + ; + + $this->assertEquals( + new Node\Param(new Expr\Variable('test'), null, null, false, true), + $node + ); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/PropertyTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/PropertyTest.php new file mode 100644 index 0000000..a15cd43 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/PropertyTest.php @@ -0,0 +1,147 @@ +createPropertyBuilder('test') + ->makePrivate() + ->makeStatic() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Property( + Stmt\Class_::MODIFIER_PRIVATE + | Stmt\Class_::MODIFIER_STATIC, + [ + new Stmt\PropertyProperty('test') + ] + ), + $node + ); + + $node = $this->createPropertyBuilder('test') + ->makeProtected() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Property( + Stmt\Class_::MODIFIER_PROTECTED, + [ + new Stmt\PropertyProperty('test') + ] + ), + $node + ); + + $node = $this->createPropertyBuilder('test') + ->makePublic() + ->getNode() + ; + + $this->assertEquals( + new Stmt\Property( + Stmt\Class_::MODIFIER_PUBLIC, + [ + new Stmt\PropertyProperty('test') + ] + ), + $node + ); + } + + public function testDocComment() { + $node = $this->createPropertyBuilder('test') + ->setDocComment('/** Test */') + ->getNode(); + + $this->assertEquals(new Stmt\Property( + Stmt\Class_::MODIFIER_PUBLIC, + [ + new Stmt\PropertyProperty('test') + ], + [ + 'comments' => [new Comment\Doc('/** Test */')] + ] + ), $node); + } + + /** + * @dataProvider provideTestDefaultValues + */ + public function testDefaultValues($value, $expectedValueNode) { + $node = $this->createPropertyBuilder('test') + ->setDefault($value) + ->getNode() + ; + + $this->assertEquals($expectedValueNode, $node->props[0]->default); + } + + public function provideTestDefaultValues() { + return [ + [ + null, + new Expr\ConstFetch(new Name('null')) + ], + [ + true, + new Expr\ConstFetch(new Name('true')) + ], + [ + false, + new Expr\ConstFetch(new Name('false')) + ], + [ + 31415, + new Scalar\LNumber(31415) + ], + [ + 3.1415, + new Scalar\DNumber(3.1415) + ], + [ + 'Hallo World', + new Scalar\String_('Hallo World') + ], + [ + [1, 2, 3], + new Expr\Array_([ + new Expr\ArrayItem(new Scalar\LNumber(1)), + new Expr\ArrayItem(new Scalar\LNumber(2)), + new Expr\ArrayItem(new Scalar\LNumber(3)), + ]) + ], + [ + ['foo' => 'bar', 'bar' => 'foo'], + new Expr\Array_([ + new Expr\ArrayItem( + new Scalar\String_('bar'), + new Scalar\String_('foo') + ), + new Expr\ArrayItem( + new Scalar\String_('foo'), + new Scalar\String_('bar') + ), + ]) + ], + [ + new Scalar\MagicConst\Dir, + new Scalar\MagicConst\Dir + ] + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/TraitTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitTest.php new file mode 100644 index 0000000..2f2bd6b --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitTest.php @@ -0,0 +1,91 @@ +createTraitBuilder('TestTrait') + ->setDocComment('/** Nice trait */') + ->addStmt($method1) + ->addStmts([$method2, $method3]) + ->addStmt($prop) + ->addStmt($use) + ->getNode(); + $this->assertEquals(new Stmt\Trait_('TestTrait', [ + 'stmts' => [$use, $prop, $method1, $method2, $method3] + ], [ + 'comments' => [ + new Comment\Doc('/** Nice trait */') + ] + ]), $trait); + } + + public function testInvalidStmtError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Unexpected node of type "Stmt_Echo"'); + $this->createTraitBuilder('Test') + ->addStmt(new Stmt\Echo_([])) + ; + } + + public function testGetMethods() { + $methods = [ + new ClassMethod('foo'), + new ClassMethod('bar'), + new ClassMethod('fooBar'), + ]; + $trait = new Stmt\Trait_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $methods[0], + new ClassConst([]), + $methods[1], + new Property(0, []), + $methods[2], + ] + ]); + + $this->assertSame($methods, $trait->getMethods()); + } + + public function testGetProperties() + { + $properties = [ + new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('foo')]), + new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('bar')]), + ]; + $trait = new Stmt\Trait_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $properties[0], + new ClassConst([]), + $properties[1], + new ClassMethod('fooBar'), + ] + ]); + + $this->assertSame($properties, $trait->getProperties()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseAdaptationTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseAdaptationTest.php new file mode 100644 index 0000000..4961ccf --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseAdaptationTest.php @@ -0,0 +1,106 @@ +createTraitUseAdaptationBuilder(null, 'foo'); + + $this->assertEquals( + new Stmt\TraitUseAdaptation\Alias(null, 'foo', null, 'bar'), + (clone $builder)->as('bar')->getNode() + ); + + $this->assertEquals( + new Stmt\TraitUseAdaptation\Alias(null, 'foo', Class_::MODIFIER_PUBLIC, null), + (clone $builder)->makePublic()->getNode() + ); + + $this->assertEquals( + new Stmt\TraitUseAdaptation\Alias(null, 'foo', Class_::MODIFIER_PROTECTED, null), + (clone $builder)->makeProtected()->getNode() + ); + + $this->assertEquals( + new Stmt\TraitUseAdaptation\Alias(null, 'foo', Class_::MODIFIER_PRIVATE, null), + (clone $builder)->makePrivate()->getNode() + ); + } + + public function testInsteadof() { + $node = $this->createTraitUseAdaptationBuilder('SomeTrait', 'foo') + ->insteadof('AnotherTrait') + ->getNode() + ; + + $this->assertEquals( + new Stmt\TraitUseAdaptation\Precedence( + new Name('SomeTrait'), + 'foo', + [new Name('AnotherTrait')] + ), + $node + ); + } + + public function testAsOnNotAlias() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot set alias for not alias adaptation buider'); + $this->createTraitUseAdaptationBuilder('Test', 'foo') + ->insteadof('AnotherTrait') + ->as('bar') + ; + } + + public function testInsteadofOnNotPrecedence() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot add overwritten traits for not precedence adaptation buider'); + $this->createTraitUseAdaptationBuilder('Test', 'foo') + ->as('bar') + ->insteadof('AnotherTrait') + ; + } + + public function testInsteadofWithoutTrait() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Precedence adaptation must have trait'); + $this->createTraitUseAdaptationBuilder(null, 'foo') + ->insteadof('AnotherTrait') + ; + } + + public function testMakeOnNotAlias() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot set access modifier for not alias adaptation buider'); + $this->createTraitUseAdaptationBuilder('Test', 'foo') + ->insteadof('AnotherTrait') + ->makePublic() + ; + } + + public function testMultipleMake() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Multiple access type modifiers are not allowed'); + $this->createTraitUseAdaptationBuilder(null, 'foo') + ->makePrivate() + ->makePublic() + ; + } + + public function testUndefinedType() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Type of adaptation is not defined'); + $this->createTraitUseAdaptationBuilder(null, 'foo') + ->getNode() + ; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseTest.php new file mode 100644 index 0000000..8d20dfb --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/TraitUseTest.php @@ -0,0 +1,52 @@ +createTraitUseBuilder('SomeTrait') + ->and('AnotherTrait') + ->getNode() + ; + + $this->assertEquals( + new Stmt\TraitUse([ + new Name('SomeTrait'), + new Name('AnotherTrait') + ]), + $node + ); + } + + public function testWith() { + $node = $this->createTraitUseBuilder('SomeTrait') + ->with(new Stmt\TraitUseAdaptation\Alias(null, 'foo', null, 'bar')) + ->with((new TraitUseAdaptation(null, 'test'))->as('baz')) + ->getNode() + ; + + $this->assertEquals( + new Stmt\TraitUse([new Name('SomeTrait')], [ + new Stmt\TraitUseAdaptation\Alias(null, 'foo', null, 'bar'), + new Stmt\TraitUseAdaptation\Alias(null, 'test', null, 'baz') + ]), + $node + ); + } + + public function testInvalidAdaptationNode() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Adaptation must have type TraitUseAdaptation'); + $this->createTraitUseBuilder('Test') + ->with(new Stmt\Echo_([])) + ; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Builder/UseTest.php b/vendor/nikic/php-parser/test/PhpParser/Builder/UseTest.php new file mode 100644 index 0000000..f17da59 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Builder/UseTest.php @@ -0,0 +1,36 @@ +createUseBuilder('Foo\Bar')->getNode(); + $this->assertEquals(new Stmt\Use_([ + new Stmt\UseUse(new Name('Foo\Bar'), null) + ]), $node); + + $node = $this->createUseBuilder(new Name('Foo\Bar'))->as('XYZ')->getNode(); + $this->assertEquals(new Stmt\Use_([ + new Stmt\UseUse(new Name('Foo\Bar'), 'XYZ') + ]), $node); + + $node = $this->createUseBuilder('foo\bar', Stmt\Use_::TYPE_FUNCTION)->as('foo')->getNode(); + $this->assertEquals(new Stmt\Use_([ + new Stmt\UseUse(new Name('foo\bar'), 'foo') + ], Stmt\Use_::TYPE_FUNCTION), $node); + + $node = $this->createUseBuilder('foo\BAR', Stmt\Use_::TYPE_CONSTANT)->as('FOO')->getNode(); + $this->assertEquals(new Stmt\Use_([ + new Stmt\UseUse(new Name('foo\BAR'), 'FOO') + ], Stmt\Use_::TYPE_CONSTANT), $node); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/BuilderFactoryTest.php b/vendor/nikic/php-parser/test/PhpParser/BuilderFactoryTest.php new file mode 100644 index 0000000..a80e1c5 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/BuilderFactoryTest.php @@ -0,0 +1,327 @@ +assertInstanceOf($className, $factory->$methodName('test')); + } + + public function provideTestFactory() { + return [ + ['namespace', Builder\Namespace_::class], + ['class', Builder\Class_::class], + ['interface', Builder\Interface_::class], + ['trait', Builder\Trait_::class], + ['method', Builder\Method::class], + ['function', Builder\Function_::class], + ['property', Builder\Property::class], + ['param', Builder\Param::class], + ['use', Builder\Use_::class], + ['useFunction', Builder\Use_::class], + ['useConst', Builder\Use_::class], + ]; + } + + public function testVal() { + // This method is a wrapper around BuilderHelpers::normalizeValue(), + // which is already tested elsewhere + $factory = new BuilderFactory(); + $this->assertEquals( + new String_("foo"), + $factory->val("foo") + ); + } + + public function testConcat() { + $factory = new BuilderFactory(); + $varA = new Expr\Variable('a'); + $varB = new Expr\Variable('b'); + $varC = new Expr\Variable('c'); + + $this->assertEquals( + new Concat($varA, $varB), + $factory->concat($varA, $varB) + ); + $this->assertEquals( + new Concat(new Concat($varA, $varB), $varC), + $factory->concat($varA, $varB, $varC) + ); + $this->assertEquals( + new Concat(new Concat(new String_("a"), $varB), new String_("c")), + $factory->concat("a", $varB, "c") + ); + } + + public function testConcatOneError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected at least two expressions'); + (new BuilderFactory())->concat("a"); + } + + public function testConcatInvalidExpr() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected string or Expr'); + (new BuilderFactory())->concat("a", 42); + } + + public function testArgs() { + $factory = new BuilderFactory(); + $unpack = new Arg(new Expr\Variable('c'), false, true); + $this->assertEquals( + [ + new Arg(new Expr\Variable('a')), + new Arg(new String_('b')), + $unpack + ], + $factory->args([new Expr\Variable('a'), 'b', $unpack]) + ); + } + + public function testCalls() { + $factory = new BuilderFactory(); + + // Simple function call + $this->assertEquals( + new Expr\FuncCall( + new Name('var_dump'), + [new Arg(new String_('str'))] + ), + $factory->funcCall('var_dump', ['str']) + ); + // Dynamic function call + $this->assertEquals( + new Expr\FuncCall(new Expr\Variable('fn')), + $factory->funcCall(new Expr\Variable('fn')) + ); + + // Simple method call + $this->assertEquals( + new Expr\MethodCall( + new Expr\Variable('obj'), + new Identifier('method'), + [new Arg(new LNumber(42))] + ), + $factory->methodCall(new Expr\Variable('obj'), 'method', [42]) + ); + // Explicitly pass Identifier node + $this->assertEquals( + new Expr\MethodCall( + new Expr\Variable('obj'), + new Identifier('method') + ), + $factory->methodCall(new Expr\Variable('obj'), new Identifier('method')) + ); + // Dynamic method call + $this->assertEquals( + new Expr\MethodCall( + new Expr\Variable('obj'), + new Expr\Variable('method') + ), + $factory->methodCall(new Expr\Variable('obj'), new Expr\Variable('method')) + ); + + // Simple static method call + $this->assertEquals( + new Expr\StaticCall( + new Name\FullyQualified('Foo'), + new Identifier('bar'), + [new Arg(new Expr\Variable('baz'))] + ), + $factory->staticCall('\Foo', 'bar', [new Expr\Variable('baz')]) + ); + // Dynamic static method call + $this->assertEquals( + new Expr\StaticCall( + new Expr\Variable('foo'), + new Expr\Variable('bar') + ), + $factory->staticCall(new Expr\Variable('foo'), new Expr\Variable('bar')) + ); + + // Simple new call + $this->assertEquals( + new Expr\New_(new Name\FullyQualified('stdClass')), + $factory->new('\stdClass') + ); + // Dynamic new call + $this->assertEquals( + new Expr\New_( + new Expr\Variable('foo'), + [new Arg(new String_('bar'))] + ), + $factory->new(new Expr\Variable('foo'), ['bar']) + ); + } + + public function testConstFetches() { + $factory = new BuilderFactory(); + $this->assertEquals( + new Expr\ConstFetch(new Name('FOO')), + $factory->constFetch('FOO') + ); + $this->assertEquals( + new Expr\ClassConstFetch(new Name('Foo'), new Identifier('BAR')), + $factory->classConstFetch('Foo', 'BAR') + ); + $this->assertEquals( + new Expr\ClassConstFetch(new Expr\Variable('foo'), new Identifier('BAR')), + $factory->classConstFetch(new Expr\Variable('foo'), 'BAR') + ); + } + + public function testVar() { + $factory = new BuilderFactory(); + $this->assertEquals( + new Expr\Variable("foo"), + $factory->var("foo") + ); + $this->assertEquals( + new Expr\Variable(new Expr\Variable("foo")), + $factory->var($factory->var("foo")) + ); + } + + public function testPropertyFetch() { + $f = new BuilderFactory(); + $this->assertEquals( + new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'), + $f->propertyFetch($f->var('foo'), 'bar') + ); + $this->assertEquals( + new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'), + $f->propertyFetch($f->var('foo'), new Identifier('bar')) + ); + $this->assertEquals( + new Expr\PropertyFetch(new Expr\Variable('foo'), new Expr\Variable('bar')), + $f->propertyFetch($f->var('foo'), $f->var('bar')) + ); + } + + public function testInvalidIdentifier() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected string or instance of Node\Identifier'); + (new BuilderFactory())->classConstFetch('Foo', new Expr\Variable('foo')); + } + + public function testInvalidIdentifierOrExpr() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Expected string or instance of Node\Identifier or Node\Expr'); + (new BuilderFactory())->staticCall('Foo', new Name('bar')); + } + + public function testInvalidNameOrExpr() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr'); + (new BuilderFactory())->funcCall(new Node\Stmt\Return_()); + } + + public function testInvalidVar() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Variable name must be string or Expr'); + (new BuilderFactory())->var(new Node\Stmt\Return_()); + } + + public function testIntegration() { + $factory = new BuilderFactory; + $node = $factory->namespace('Name\Space') + ->addStmt($factory->use('Foo\Bar\SomeOtherClass')) + ->addStmt($factory->use('Foo\Bar')->as('A')) + ->addStmt($factory->useFunction('strlen')) + ->addStmt($factory->useConst('PHP_VERSION')) + ->addStmt($factory + ->class('SomeClass') + ->extend('SomeOtherClass') + ->implement('A\Few', '\Interfaces') + ->makeAbstract() + + ->addStmt($factory->useTrait('FirstTrait')) + + ->addStmt($factory->useTrait('SecondTrait', 'ThirdTrait') + ->and('AnotherTrait') + ->with($factory->traitUseAdaptation('foo')->as('bar')) + ->with($factory->traitUseAdaptation('AnotherTrait', 'baz')->as('test')) + ->with($factory->traitUseAdaptation('AnotherTrait', 'func')->insteadof('SecondTrait'))) + + ->addStmt($factory->method('firstMethod')) + + ->addStmt($factory->method('someMethod') + ->makePublic() + ->makeAbstract() + ->addParam($factory->param('someParam')->setType('SomeClass')) + ->setDocComment('/** + * This method does something. + * + * @param SomeClass And takes a parameter + */')) + + ->addStmt($factory->method('anotherMethod') + ->makeProtected() + ->addParam($factory->param('someParam')->setDefault('test')) + ->addStmt(new Expr\Print_(new Expr\Variable('someParam')))) + + ->addStmt($factory->property('someProperty')->makeProtected()) + ->addStmt($factory->property('anotherProperty') + ->makePrivate() + ->setDefault([1, 2, 3]))) + ->getNode() + ; + + $expected = <<<'EOC' +prettyPrintFile($stmts); + + $this->assertEquals( + str_replace("\r\n", "\n", $expected), + str_replace("\r\n", "\n", $generated) + ); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/CodeParsingTest.php b/vendor/nikic/php-parser/test/PhpParser/CodeParsingTest.php new file mode 100644 index 0000000..24e93dd --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/CodeParsingTest.php @@ -0,0 +1,119 @@ +createParsers($modes); + list($stmts5, $output5) = $this->getParseOutput($parser5, $code, $modes); + list($stmts7, $output7) = $this->getParseOutput($parser7, $code, $modes); + + if (isset($modes['php5'])) { + $this->assertSame($expected, $output5, $name); + $this->assertNotSame($expected, $output7, $name); + } elseif (isset($modes['php7'])) { + $this->assertNotSame($expected, $output5, $name); + $this->assertSame($expected, $output7, $name); + } else { + $this->assertSame($expected, $output5, $name); + $this->assertSame($expected, $output7, $name); + } + + $this->checkAttributes($stmts5); + $this->checkAttributes($stmts7); + } + + public function createParsers(array $modes) { + $lexer = new Lexer\Emulative(['usedAttributes' => [ + 'startLine', 'endLine', + 'startFilePos', 'endFilePos', + 'startTokenPos', 'endTokenPos', + 'comments' + ]]); + + return [ + new Parser\Php5($lexer), + new Parser\Php7($lexer), + ]; + } + + // Must be public for updateTests.php + public function getParseOutput(Parser $parser, $code, array $modes) { + $dumpPositions = isset($modes['positions']); + + $errors = new ErrorHandler\Collecting; + $stmts = $parser->parse($code, $errors); + + $output = ''; + foreach ($errors->getErrors() as $error) { + $output .= $this->formatErrorMessage($error, $code) . "\n"; + } + + if (null !== $stmts) { + $dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]); + $output .= $dumper->dump($stmts, $code); + } + + return [$stmts, canonicalize($output)]; + } + + public function provideTestParse() { + return $this->getTests(__DIR__ . '/../code/parser', 'test'); + } + + private function formatErrorMessage(Error $e, $code) { + if ($e->hasColumnInfo()) { + return $e->getMessageWithColumnInfo($code); + } + + return $e->getMessage(); + } + + private function checkAttributes($stmts) { + if ($stmts === null) { + return; + } + + $traverser = new NodeTraverser(); + $traverser->addVisitor(new class extends NodeVisitorAbstract { + public function enterNode(Node $node) { + $startLine = $node->getStartLine(); + $endLine = $node->getEndLine(); + $startFilePos = $node->getStartFilePos(); + $endFilePos = $node->getEndFilePos(); + $startTokenPos = $node->getStartTokenPos(); + $endTokenPos = $node->getEndTokenPos(); + if ($startLine < 0 || $endLine < 0 || + $startFilePos < 0 || $endFilePos < 0 || + $startTokenPos < 0 || $endTokenPos < 0 + ) { + throw new \Exception('Missing location information on ' . $node->getType()); + } + + if ($endLine < $startLine || + $endFilePos < $startFilePos || + $endTokenPos < $startTokenPos + ) { + // Nops and error can have inverted order, if they are empty + if (!$node instanceof Stmt\Nop && !$node instanceof Expr\Error) { + throw new \Exception('End < start on ' . $node->getType()); + } + } + } + }); + $traverser->traverse($stmts); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/CodeTestAbstract.php b/vendor/nikic/php-parser/test/PhpParser/CodeTestAbstract.php new file mode 100644 index 0000000..f5f4087 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/CodeTestAbstract.php @@ -0,0 +1,26 @@ + $fileContents) { + list($name, $tests) = $parser->parseTest($fileContents, $chunksPerTest); + + // first part is the name + $name .= ' (' . $fileName . ')'; + $shortName = ltrim(str_replace($directory, '', $fileName), '/\\'); + + // multiple sections possible with always two forming a pair + foreach ($tests as $i => list($mode, $parts)) { + $dataSetName = $shortName . (count($parts) > 1 ? '#' . $i : ''); + $allTests[$dataSetName] = array_merge([$name], $parts, [$mode]); + } + } + + return $allTests; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/CodeTestParser.php b/vendor/nikic/php-parser/test/PhpParser/CodeTestParser.php new file mode 100644 index 0000000..f63dc92 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/CodeTestParser.php @@ -0,0 +1,68 @@ + $chunk) { + $lastPart = array_pop($chunk); + list($lastPart, $mode) = $this->extractMode($lastPart); + $tests[] = [$mode, array_merge($chunk, [$lastPart])]; + } + + return [$name, $tests]; + } + + public function reconstructTest($name, array $tests) { + $result = $name; + foreach ($tests as list($mode, $parts)) { + $lastPart = array_pop($parts); + foreach ($parts as $part) { + $result .= "\n-----\n$part"; + } + + $result .= "\n-----\n"; + if (null !== $mode) { + $result .= "!!$mode\n"; + } + $result .= $lastPart; + } + return $result; + } + + private function extractMode($expected) { + $firstNewLine = strpos($expected, "\n"); + if (false === $firstNewLine) { + $firstNewLine = strlen($expected); + } + + $firstLine = substr($expected, 0, $firstNewLine); + if (0 !== strpos($firstLine, '!!')) { + return [$expected, null]; + } + + $expected = (string) substr($expected, $firstNewLine + 1); + return [$expected, substr($firstLine, 2)]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/CommentTest.php b/vendor/nikic/php-parser/test/PhpParser/CommentTest.php new file mode 100644 index 0000000..409841a --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/CommentTest.php @@ -0,0 +1,74 @@ +assertSame('/* Some comment */', $comment->getText()); + $this->assertSame('/* Some comment */', (string) $comment); + $this->assertSame(1, $comment->getLine()); + $this->assertSame(10, $comment->getFilePos()); + $this->assertSame(2, $comment->getTokenPos()); + } + + /** + * @dataProvider provideTestReformatting + */ + public function testReformatting($commentText, $reformattedText) { + $comment = new Comment($commentText); + $this->assertSame($reformattedText, $comment->getReformattedText()); + } + + public function provideTestReformatting() { + return [ + ['// Some text' . "\n", '// Some text'], + ['/* Some text */', '/* Some text */'], + [ + '/** + * Some text. + * Some more text. + */', + '/** + * Some text. + * Some more text. + */' + ], + [ + '/* + Some text. + Some more text. + */', + '/* + Some text. + Some more text. +*/' + ], + [ + '/* Some text. + More text. + Even more text. */', + '/* Some text. + More text. + Even more text. */' + ], + [ + '/* Some text. + More text. + Indented text. */', + '/* Some text. + More text. + Indented text. */', + ], + // invalid comment -> no reformatting + [ + 'hallo + world', + 'hallo + world', + ], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/ConstExprEvaluatorTest.php b/vendor/nikic/php-parser/test/PhpParser/ConstExprEvaluatorTest.php new file mode 100644 index 0000000..02d992e --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/ConstExprEvaluatorTest.php @@ -0,0 +1,130 @@ +parse('expr; + $evaluator = new ConstExprEvaluator(); + $this->assertSame($expected, $evaluator->evaluateDirectly($expr)); + } + + public function provideTestEvaluate() { + return [ + ['1', 1], + ['1.0', 1.0], + ['"foo"', "foo"], + ['[0, 1]', [0, 1]], + ['["foo" => "bar"]', ["foo" => "bar"]], + ['NULL', null], + ['False', false], + ['true', true], + ['+1', 1], + ['-1', -1], + ['~0', -1], + ['!true', false], + ['[0][0]', 0], + ['"a"[0]', "a"], + ['true ? 1 : (1/0)', 1], + ['false ? (1/0) : 1', 1], + ['42 ?: (1/0)', 42], + ['false ?: 42', 42], + ['false ?? 42', false], + ['null ?? 42', 42], + ['[0][0] ?? 42', 0], + ['[][0] ?? 42', 42], + ['0b11 & 0b10', 0b10], + ['0b11 | 0b10', 0b11], + ['0b11 ^ 0b10', 0b01], + ['1 << 2', 4], + ['4 >> 2', 1], + ['"a" . "b"', "ab"], + ['4 + 2', 6], + ['4 - 2', 2], + ['4 * 2', 8], + ['4 / 2', 2], + ['4 % 2', 0], + ['4 ** 2', 16], + ['1 == 1.0', true], + ['1 != 1.0', false], + ['1 < 2.0', true], + ['1 <= 2.0', true], + ['1 > 2.0', false], + ['1 >= 2.0', false], + ['1 <=> 2.0', -1], + ['1 === 1.0', false], + ['1 !== 1.0', true], + ['true && true', true], + ['true and true', true], + ['false && (1/0)', false], + ['false and (1/0)', false], + ['false || false', false], + ['false or false', false], + ['true || (1/0)', true], + ['true or (1/0)', true], + ['true xor false', true], + ]; + } + + public function testEvaluateFails() { + $this->expectException(ConstExprEvaluationException::class); + $this->expectExceptionMessage('Expression of type Expr_Variable cannot be evaluated'); + $evaluator = new ConstExprEvaluator(); + $evaluator->evaluateDirectly(new Expr\Variable('a')); + } + + public function testEvaluateFallback() { + $evaluator = new ConstExprEvaluator(function(Expr $expr) { + if ($expr instanceof Scalar\MagicConst\Line) { + return 42; + } + throw new ConstExprEvaluationException(); + }); + $expr = new Expr\BinaryOp\Plus( + new Scalar\LNumber(8), + new Scalar\MagicConst\Line() + ); + $this->assertSame(50, $evaluator->evaluateDirectly($expr)); + } + + /** + * @dataProvider provideTestEvaluateSilently + */ + public function testEvaluateSilently($expr, $exception, $msg) { + $evaluator = new ConstExprEvaluator(); + + try { + $evaluator->evaluateSilently($expr); + } catch (ConstExprEvaluationException $e) { + $this->assertSame( + 'An error occurred during constant expression evaluation', + $e->getMessage() + ); + + $prev = $e->getPrevious(); + $this->assertInstanceOf($exception, $prev); + $this->assertSame($msg, $prev->getMessage()); + } + } + + public function provideTestEvaluateSilently() { + return [ + [ + new Expr\BinaryOp\Mod(new Scalar\LNumber(42), new Scalar\LNumber(0)), + \Error::class, + 'Modulo by zero' + ], + [ + new Expr\BinaryOp\Div(new Scalar\LNumber(42), new Scalar\LNumber(0)), + \ErrorException::class, + 'Division by zero' + ], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/CollectingTest.php b/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/CollectingTest.php new file mode 100644 index 0000000..a20101a --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/CollectingTest.php @@ -0,0 +1,23 @@ +assertFalse($errorHandler->hasErrors()); + $this->assertEmpty($errorHandler->getErrors()); + + $errorHandler->handleError($e1 = new Error('Test 1')); + $errorHandler->handleError($e2 = new Error('Test 2')); + $this->assertTrue($errorHandler->hasErrors()); + $this->assertSame([$e1, $e2], $errorHandler->getErrors()); + + $errorHandler->clearErrors(); + $this->assertFalse($errorHandler->hasErrors()); + $this->assertEmpty($errorHandler->getErrors()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/ThrowingTest.php b/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/ThrowingTest.php new file mode 100644 index 0000000..be641ec --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/ErrorHandler/ThrowingTest.php @@ -0,0 +1,15 @@ +expectException(Error::class); + $this->expectExceptionMessage('Test'); + $errorHandler = new Throwing(); + $errorHandler->handleError(new Error('Test')); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/ErrorTest.php b/vendor/nikic/php-parser/test/PhpParser/ErrorTest.php new file mode 100644 index 0000000..cc2d3fa --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/ErrorTest.php @@ -0,0 +1,104 @@ + 10, + 'endLine' => 11, + ]; + $error = new Error('Some error', $attributes); + + $this->assertSame('Some error', $error->getRawMessage()); + $this->assertSame($attributes, $error->getAttributes()); + $this->assertSame(10, $error->getStartLine()); + $this->assertSame(11, $error->getEndLine()); + $this->assertSame('Some error on line 10', $error->getMessage()); + + return $error; + } + + /** + * @depends testConstruct + */ + public function testSetMessageAndLine(Error $error) { + $error->setRawMessage('Some other error'); + $this->assertSame('Some other error', $error->getRawMessage()); + + $error->setStartLine(15); + $this->assertSame(15, $error->getStartLine()); + $this->assertSame('Some other error on line 15', $error->getMessage()); + } + + public function testUnknownLine() { + $error = new Error('Some error'); + + $this->assertSame(-1, $error->getStartLine()); + $this->assertSame(-1, $error->getEndLine()); + $this->assertSame('Some error on unknown line', $error->getMessage()); + } + + /** @dataProvider provideTestColumnInfo */ + public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn) { + $error = new Error('Some error', [ + 'startFilePos' => $startPos, + 'endFilePos' => $endPos, + ]); + + $this->assertTrue($error->hasColumnInfo()); + $this->assertSame($startColumn, $error->getStartColumn($code)); + $this->assertSame($endColumn, $error->getEndColumn($code)); + + } + + public function provideTestColumnInfo() { + return [ + // Error at "bar" + ["assertFalse($error->hasColumnInfo()); + try { + $error->getStartColumn(''); + $this->fail('Expected RuntimeException'); + } catch (\RuntimeException $e) { + $this->assertSame('Error does not have column information', $e->getMessage()); + } + try { + $error->getEndColumn(''); + $this->fail('Expected RuntimeException'); + } catch (\RuntimeException $e) { + $this->assertSame('Error does not have column information', $e->getMessage()); + } + } + + public function testInvalidPosInfo() { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Invalid position information'); + $error = new Error('Some error', [ + 'startFilePos' => 10, + 'endFilePos' => 11, + ]); + $error->getStartColumn('code'); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Internal/DifferTest.php b/vendor/nikic/php-parser/test/PhpParser/Internal/DifferTest.php new file mode 100644 index 0000000..6fac3fd --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Internal/DifferTest.php @@ -0,0 +1,65 @@ +type) { + case DiffElem::TYPE_KEEP: + $diffStr .= $diffElem->old; + break; + case DiffElem::TYPE_REMOVE: + $diffStr .= '-' . $diffElem->old; + break; + case DiffElem::TYPE_ADD: + $diffStr .= '+' . $diffElem->new; + break; + case DiffElem::TYPE_REPLACE: + $diffStr .= '/' . $diffElem->old . $diffElem->new; + break; + default: + assert(false); + break; + } + } + return $diffStr; + } + + /** @dataProvider provideTestDiff */ + public function testDiff($oldStr, $newStr, $expectedDiffStr) { + $differ = new Differ(function($a, $b) { return $a === $b; }); + $diff = $differ->diff(str_split($oldStr), str_split($newStr)); + $this->assertSame($expectedDiffStr, $this->formatDiffString($diff)); + } + + public function provideTestDiff() { + return [ + ['abc', 'abc', 'abc'], + ['abc', 'abcdef', 'abc+d+e+f'], + ['abcdef', 'abc', 'abc-d-e-f'], + ['abcdef', 'abcxyzdef', 'abc+x+y+zdef'], + ['axyzb', 'ab', 'a-x-y-zb'], + ['abcdef', 'abxyef', 'ab-c-d+x+yef'], + ['abcdef', 'cdefab', '-a-bcdef+a+b'], + ]; + } + + /** @dataProvider provideTestDiffWithReplacements */ + public function testDiffWithReplacements($oldStr, $newStr, $expectedDiffStr) { + $differ = new Differ(function($a, $b) { return $a === $b; }); + $diff = $differ->diffWithReplacements(str_split($oldStr), str_split($newStr)); + $this->assertSame($expectedDiffStr, $this->formatDiffString($diff)); + } + + public function provideTestDiffWithReplacements() { + return [ + ['abcde', 'axyze', 'a/bx/cy/dze'], + ['abcde', 'xbcdy', '/axbcd/ey'], + ['abcde', 'axye', 'a-b-c-d+x+ye'], + ['abcde', 'axyzue', 'a-b-c-d+x+y+z+ue'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/JsonDecoderTest.php b/vendor/nikic/php-parser/test/PhpParser/JsonDecoderTest.php new file mode 100644 index 0000000..d5cb059 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/JsonDecoderTest.php @@ -0,0 +1,43 @@ +parse($code); + $json = json_encode($stmts); + + $jsonDecoder = new JsonDecoder(); + $decodedStmts = $jsonDecoder->decode($json); + $this->assertEquals($stmts, $decodedStmts); + } + + /** @dataProvider provideTestDecodingError */ + public function testDecodingError($json, $expectedMessage) { + $jsonDecoder = new JsonDecoder(); + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage($expectedMessage); + $jsonDecoder->decode($json); + } + + public function provideTestDecodingError() { + return [ + ['???', 'JSON decoding error: Syntax error'], + ['{"nodeType":123}', 'Node type must be a string'], + ['{"nodeType":"Name","attributes":123}', 'Attributes must be an array'], + ['{"nodeType":"Comment"}', 'Comment must have text'], + ['{"nodeType":"xxx"}', 'Unknown node type "xxx"'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Lexer/EmulativeTest.php b/vendor/nikic/php-parser/test/PhpParser/Lexer/EmulativeTest.php new file mode 100644 index 0000000..2a39bea --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Lexer/EmulativeTest.php @@ -0,0 +1,258 @@ +getLexer(); + $lexer->startLexing('assertSame($expectedToken, $lexer->getNextToken()); + $this->assertSame(0, $lexer->getNextToken()); + } + + /** + * @dataProvider provideTestReplaceKeywords + */ + public function testNoReplaceKeywordsAfterObjectOperator(string $keyword) { + $lexer = $this->getLexer(); + $lexer->startLexing('' . $keyword); + + $this->assertSame(Tokens::T_OBJECT_OPERATOR, $lexer->getNextToken()); + $this->assertSame(Tokens::T_STRING, $lexer->getNextToken()); + $this->assertSame(0, $lexer->getNextToken()); + } + + /** + * @dataProvider provideTestReplaceKeywords + */ + public function testNoReplaceKeywordsAfterObjectOperatorWithSpaces(string $keyword) { + $lexer = $this->getLexer(); + $lexer->startLexing(' ' . $keyword); + + $this->assertSame(Tokens::T_OBJECT_OPERATOR, $lexer->getNextToken()); + $this->assertSame(Tokens::T_STRING, $lexer->getNextToken()); + $this->assertSame(0, $lexer->getNextToken()); + } + + public function provideTestReplaceKeywords() { + return [ + // PHP 7.4 + ['fn', Tokens::T_FN], + + // PHP 5.5 + ['finally', Tokens::T_FINALLY], + ['yield', Tokens::T_YIELD], + + // PHP 5.4 + ['callable', Tokens::T_CALLABLE], + ['insteadof', Tokens::T_INSTEADOF], + ['trait', Tokens::T_TRAIT], + ['__TRAIT__', Tokens::T_TRAIT_C], + + // PHP 5.3 + ['__DIR__', Tokens::T_DIR], + ['goto', Tokens::T_GOTO], + ['namespace', Tokens::T_NAMESPACE], + ['__NAMESPACE__', Tokens::T_NS_C], + ]; + } + + /** + * @dataProvider provideTestLexNewFeatures + */ + public function testLexNewFeatures($code, array $expectedTokens) { + $lexer = $this->getLexer(); + $lexer->startLexing('getNextToken($text)) { + $tokens[] = [$token, $text]; + } + $this->assertSame($expectedTokens, $tokens); + } + + /** + * @dataProvider provideTestLexNewFeatures + */ + public function testLeaveStuffAloneInStrings($code) { + $stringifiedToken = '"' . addcslashes($code, '"\\') . '"'; + + $lexer = $this->getLexer(); + $lexer->startLexing('assertSame(Tokens::T_CONSTANT_ENCAPSED_STRING, $lexer->getNextToken($text)); + $this->assertSame($stringifiedToken, $text); + $this->assertSame(0, $lexer->getNextToken()); + } + + /** + * @dataProvider provideTestLexNewFeatures + */ + public function testErrorAfterEmulation($code) { + $errorHandler = new ErrorHandler\Collecting; + $lexer = $this->getLexer(); + $lexer->startLexing('getErrors(); + $this->assertCount(1, $errors); + + $error = $errors[0]; + $this->assertSame('Unexpected null byte', $error->getRawMessage()); + + $attrs = $error->getAttributes(); + $expPos = strlen('assertSame($expPos, $attrs['startFilePos']); + $this->assertSame($expPos, $attrs['endFilePos']); + $this->assertSame($expLine, $attrs['startLine']); + $this->assertSame($expLine, $attrs['endLine']); + } + + public function provideTestLexNewFeatures() { + return [ + ['yield from', [ + [Tokens::T_YIELD_FROM, 'yield from'], + ]], + ["yield\r\nfrom", [ + [Tokens::T_YIELD_FROM, "yield\r\nfrom"], + ]], + ['...', [ + [Tokens::T_ELLIPSIS, '...'], + ]], + ['**', [ + [Tokens::T_POW, '**'], + ]], + ['**=', [ + [Tokens::T_POW_EQUAL, '**='], + ]], + ['??', [ + [Tokens::T_COALESCE, '??'], + ]], + ['<=>', [ + [Tokens::T_SPACESHIP, '<=>'], + ]], + ['0b1010110', [ + [Tokens::T_LNUMBER, '0b1010110'], + ]], + ['0b1011010101001010110101010010101011010101010101101011001110111100', [ + [Tokens::T_DNUMBER, '0b1011010101001010110101010010101011010101010101101011001110111100'], + ]], + ['\\', [ + [Tokens::T_NS_SEPARATOR, '\\'], + ]], + ["<<<'NOWDOC'\nNOWDOC;\n", [ + [Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"], + [Tokens::T_END_HEREDOC, 'NOWDOC'], + [ord(';'), ';'], + ]], + ["<<<'NOWDOC'\nFoobar\nNOWDOC;\n", [ + [Tokens::T_START_HEREDOC, "<<<'NOWDOC'\n"], + [Tokens::T_ENCAPSED_AND_WHITESPACE, "Foobar\n"], + [Tokens::T_END_HEREDOC, 'NOWDOC'], + [ord(';'), ';'], + ]], + + // PHP 7.3: Flexible heredoc/nowdoc + ["<<markTestSkipped('HHVM does not throw warnings from token_get_all()'); + } + + $errorHandler = new ErrorHandler\Collecting(); + $lexer = $this->getLexer(['usedAttributes' => [ + 'comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos' + ]]); + $lexer->startLexing($code, $errorHandler); + $errors = $errorHandler->getErrors(); + + $this->assertCount(count($messages), $errors); + for ($i = 0; $i < count($messages); $i++) { + $this->assertSame($messages[$i], $errors[$i]->getMessageWithColumnInfo($code)); + } + } + + public function provideTestError() { + return [ + ["getLexer($options); + $lexer->startLexing($code); + while ($id = $lexer->getNextToken($value, $startAttributes, $endAttributes)) { + $token = array_shift($tokens); + + $this->assertSame($token[0], $id); + $this->assertSame($token[1], $value); + $this->assertEquals($token[2], $startAttributes); + $this->assertEquals($token[3], $endAttributes); + } + } + + public function provideTestLex() { + return [ + // tests conversion of closing PHP tag and drop of whitespace and opening tags + [ + 'plaintext', + [], + [ + [ + Tokens::T_STRING, 'tokens', + ['startLine' => 1], ['endLine' => 1] + ], + [ + ord(';'), '?>', + ['startLine' => 1], ['endLine' => 1] + ], + [ + Tokens::T_INLINE_HTML, 'plaintext', + ['startLine' => 1, 'hasLeadingNewline' => false], + ['endLine' => 1] + ], + ] + ], + // tests line numbers + [ + ' 2], ['endLine' => 2] + ], + [ + Tokens::T_STRING, 'token', + ['startLine' => 2], ['endLine' => 2] + ], + [ + ord('$'), '$', + [ + 'startLine' => 3, + 'comments' => [ + new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14, 5), + ] + ], + ['endLine' => 3] + ], + ] + ], + // tests comment extraction + [ + ' 2, + 'comments' => [ + new Comment('/* comment */', 1, 6, 1), + new Comment('// comment' . "\n", 1, 20, 3), + new Comment\Doc('/** docComment 1 */', 2, 31, 4), + new Comment\Doc('/** docComment 2 */', 2, 50, 5), + ], + ], + ['endLine' => 2] + ], + ] + ], + // tests differing start and end line + [ + ' 1], ['endLine' => 2] + ], + ] + ], + // tests exact file offsets + [ + ' ['startFilePos', 'endFilePos']], + [ + [ + Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"', + ['startFilePos' => 6], ['endFilePos' => 8] + ], + [ + ord(';'), ';', + ['startFilePos' => 9], ['endFilePos' => 9] + ], + [ + Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"', + ['startFilePos' => 18], ['endFilePos' => 20] + ], + [ + ord(';'), ';', + ['startFilePos' => 21], ['endFilePos' => 21] + ], + ] + ], + // tests token offsets + [ + ' ['startTokenPos', 'endTokenPos']], + [ + [ + Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"', + ['startTokenPos' => 1], ['endTokenPos' => 1] + ], + [ + ord(';'), ';', + ['startTokenPos' => 2], ['endTokenPos' => 2] + ], + [ + Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"', + ['startTokenPos' => 5], ['endTokenPos' => 5] + ], + [ + ord(';'), ';', + ['startTokenPos' => 6], ['endTokenPos' => 6] + ], + ] + ], + // tests all attributes being disabled + [ + ' []], + [ + [ + Tokens::T_VARIABLE, '$bar', + [], [] + ], + [ + ord(';'), ';', + [], [] + ] + ] + ], + // tests no tokens + [ + '', + [], + [] + ], + ]; + } + + /** + * @dataProvider provideTestHaltCompiler + */ + public function testHandleHaltCompiler($code, $remaining) { + $lexer = $this->getLexer(); + $lexer->startLexing($code); + + while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken()); + + $this->assertSame($remaining, $lexer->handleHaltCompiler()); + $this->assertSame(0, $lexer->getNextToken()); + } + + public function provideTestHaltCompiler() { + return [ + ['Remaining Text', 'Remaining Text'], + //array('expectException(Error::class); + $this->expectExceptionMessage('__HALT_COMPILER must be followed by "();"'); + $lexer = $this->getLexer(); + $lexer->startLexing('getNextToken()); + $lexer->handleHaltCompiler(); + } + + public function testGetTokens() { + $code = 'getLexer(); + $lexer->startLexing($code); + $this->assertSame($expectedTokens, $lexer->getTokens()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NameContextTest.php b/vendor/nikic/php-parser/test/PhpParser/NameContextTest.php new file mode 100644 index 0000000..ee8458b --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NameContextTest.php @@ -0,0 +1,65 @@ +startNamespace(new Name('NS')); + $nameContext->addAlias(new Name('Foo'), 'Foo', Use_::TYPE_NORMAL); + $nameContext->addAlias(new Name('Foo\Bar'), 'Alias', Use_::TYPE_NORMAL); + $nameContext->addAlias(new Name('Foo\fn'), 'fn', Use_::TYPE_FUNCTION); + $nameContext->addAlias(new Name('Foo\CN'), 'CN', Use_::TYPE_CONSTANT); + + $possibleNames = $nameContext->getPossibleNames($name, $type); + $possibleNames = array_map(function (Name $name) { + return $name->toCodeString(); + }, $possibleNames); + + $this->assertSame($expectedPossibleNames, $possibleNames); + + // Here the last name is always the shortest one + $expectedShortName = $expectedPossibleNames[count($expectedPossibleNames) - 1]; + $this->assertSame( + $expectedShortName, + $nameContext->getShortName($name, $type)->toCodeString() + ); + } + + public function provideTestGetPossibleNames() { + return [ + [Use_::TYPE_NORMAL, 'Test', ['\Test']], + [Use_::TYPE_NORMAL, 'Test\Namespaced', ['\Test\Namespaced']], + [Use_::TYPE_NORMAL, 'NS\Test', ['\NS\Test', 'Test']], + [Use_::TYPE_NORMAL, 'ns\Test', ['\ns\Test', 'Test']], + [Use_::TYPE_NORMAL, 'NS\Foo\Bar', ['\NS\Foo\Bar']], + [Use_::TYPE_NORMAL, 'ns\foo\Bar', ['\ns\foo\Bar']], + [Use_::TYPE_NORMAL, 'Foo', ['\Foo', 'Foo']], + [Use_::TYPE_NORMAL, 'Foo\Bar', ['\Foo\Bar', 'Foo\Bar', 'Alias']], + [Use_::TYPE_NORMAL, 'Foo\Bar\Baz', ['\Foo\Bar\Baz', 'Foo\Bar\Baz', 'Alias\Baz']], + [Use_::TYPE_NORMAL, 'Foo\fn\Bar', ['\Foo\fn\Bar', 'Foo\fn\Bar']], + [Use_::TYPE_FUNCTION, 'Foo\fn\bar', ['\Foo\fn\bar', 'Foo\fn\bar']], + [Use_::TYPE_FUNCTION, 'Foo\fn', ['\Foo\fn', 'Foo\fn', 'fn']], + [Use_::TYPE_FUNCTION, 'Foo\FN', ['\Foo\FN', 'Foo\FN', 'fn']], + [Use_::TYPE_CONSTANT, 'Foo\CN\BAR', ['\Foo\CN\BAR', 'Foo\CN\BAR']], + [Use_::TYPE_CONSTANT, 'Foo\CN', ['\Foo\CN', 'Foo\CN', 'CN']], + [Use_::TYPE_CONSTANT, 'foo\CN', ['\foo\CN', 'Foo\CN', 'CN']], + [Use_::TYPE_CONSTANT, 'foo\cn', ['\foo\cn', 'Foo\cn']], + // self/parent/static must not be fully qualified + [Use_::TYPE_NORMAL, 'self', ['self']], + [Use_::TYPE_NORMAL, 'parent', ['parent']], + [Use_::TYPE_NORMAL, 'static', ['static']], + // true/false/null do not need to be fully qualified, even in namespaces + [Use_::TYPE_CONSTANT, 'true', ['\true', 'true']], + [Use_::TYPE_CONSTANT, 'false', ['\false', 'false']], + [Use_::TYPE_CONSTANT, 'null', ['\null', 'null']], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/IdentifierTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/IdentifierTest.php new file mode 100644 index 0000000..2bd58fc --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/IdentifierTest.php @@ -0,0 +1,29 @@ +assertSame('Foo', (string) $identifier); + $this->assertSame('Foo', $identifier->toString()); + $this->assertSame('foo', $identifier->toLowerString()); + } + + /** @dataProvider provideTestIsSpecialClassName */ + public function testIsSpecialClassName($identifier, $expected) { + $identifier = new Identifier($identifier); + $this->assertSame($expected, $identifier->isSpecialClassName()); + } + + public function provideTestIsSpecialClassName() { + return [ + ['self', true], + ['PARENT', true], + ['Static', true], + ['other', false], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/NameTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/NameTest.php new file mode 100644 index 0000000..5e69ebb --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/NameTest.php @@ -0,0 +1,157 @@ +assertSame(['foo', 'bar'], $name->parts); + + $name = new Name('foo\bar'); + $this->assertSame(['foo', 'bar'], $name->parts); + + $name = new Name($name); + $this->assertSame(['foo', 'bar'], $name->parts); + } + + public function testGet() { + $name = new Name('foo'); + $this->assertSame('foo', $name->getFirst()); + $this->assertSame('foo', $name->getLast()); + + $name = new Name('foo\bar'); + $this->assertSame('foo', $name->getFirst()); + $this->assertSame('bar', $name->getLast()); + } + + public function testToString() { + $name = new Name('Foo\Bar'); + + $this->assertSame('Foo\Bar', (string) $name); + $this->assertSame('Foo\Bar', $name->toString()); + $this->assertSame('foo\bar', $name->toLowerString()); + } + + public function testSlice() { + $name = new Name('foo\bar\baz'); + $this->assertEquals(new Name('foo\bar\baz'), $name->slice(0)); + $this->assertEquals(new Name('bar\baz'), $name->slice(1)); + $this->assertNull($name->slice(3)); + $this->assertEquals(new Name('foo\bar\baz'), $name->slice(-3)); + $this->assertEquals(new Name('bar\baz'), $name->slice(-2)); + $this->assertEquals(new Name('foo\bar'), $name->slice(0, -1)); + $this->assertNull($name->slice(0, -3)); + $this->assertEquals(new Name('bar'), $name->slice(1, -1)); + $this->assertNull($name->slice(1, -2)); + $this->assertEquals(new Name('bar'), $name->slice(-2, 1)); + $this->assertEquals(new Name('bar'), $name->slice(-2, -1)); + $this->assertNull($name->slice(-2, -2)); + } + + public function testSliceOffsetTooLarge() { + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Offset 4 is out of bounds'); + (new Name('foo\bar\baz'))->slice(4); + } + + public function testSliceOffsetTooSmall() { + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Offset -4 is out of bounds'); + (new Name('foo\bar\baz'))->slice(-4); + } + + public function testSliceLengthTooLarge() { + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Length 4 is out of bounds'); + (new Name('foo\bar\baz'))->slice(0, 4); + } + + public function testSliceLengthTooSmall() { + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Length -4 is out of bounds'); + (new Name('foo\bar\baz'))->slice(0, -4); + } + + public function testConcat() { + $this->assertEquals(new Name('foo\bar\baz'), Name::concat('foo', 'bar\baz')); + $this->assertEquals( + new Name\FullyQualified('foo\bar'), + Name\FullyQualified::concat(['foo'], new Name('bar')) + ); + + $attributes = ['foo' => 'bar']; + $this->assertEquals( + new Name\Relative('foo\bar\baz', $attributes), + Name\Relative::concat(new Name\FullyQualified('foo\bar'), 'baz', $attributes) + ); + + $this->assertEquals(new Name('foo'), Name::concat(null, 'foo')); + $this->assertEquals(new Name('foo'), Name::concat('foo', null)); + $this->assertNull(Name::concat(null, null)); + } + + public function testNameTypes() { + $name = new Name('foo'); + $this->assertTrue($name->isUnqualified()); + $this->assertFalse($name->isQualified()); + $this->assertFalse($name->isFullyQualified()); + $this->assertFalse($name->isRelative()); + $this->assertSame('foo', $name->toCodeString()); + + $name = new Name('foo\bar'); + $this->assertFalse($name->isUnqualified()); + $this->assertTrue($name->isQualified()); + $this->assertFalse($name->isFullyQualified()); + $this->assertFalse($name->isRelative()); + $this->assertSame('foo\bar', $name->toCodeString()); + + $name = new Name\FullyQualified('foo'); + $this->assertFalse($name->isUnqualified()); + $this->assertFalse($name->isQualified()); + $this->assertTrue($name->isFullyQualified()); + $this->assertFalse($name->isRelative()); + $this->assertSame('\foo', $name->toCodeString()); + + $name = new Name\Relative('foo'); + $this->assertFalse($name->isUnqualified()); + $this->assertFalse($name->isQualified()); + $this->assertFalse($name->isFullyQualified()); + $this->assertTrue($name->isRelative()); + $this->assertSame('namespace\foo', $name->toCodeString()); + } + + public function testInvalidArg() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Expected string, array of parts or Name instance'); + Name::concat('foo', new \stdClass); + } + + public function testInvalidEmptyString() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Name cannot be empty'); + new Name(''); + } + + public function testInvalidEmptyArray() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Name cannot be empty'); + new Name([]); + } + + /** @dataProvider provideTestIsSpecialClassName */ + public function testIsSpecialClassName($name, $expected) { + $name = new Name($name); + $this->assertSame($expected, $name->isSpecialClassName()); + } + + public function provideTestIsSpecialClassName() { + return [ + ['self', true], + ['PARENT', true], + ['Static', true], + ['self\not', false], + ['not\self', false], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/MagicConstTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/MagicConstTest.php new file mode 100644 index 0000000..c8ae433 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/MagicConstTest.php @@ -0,0 +1,26 @@ +assertSame($name, $magicConst->getName()); + } + + public function provideTestGetName() { + return [ + [new MagicConst\Class_, '__CLASS__'], + [new MagicConst\Dir, '__DIR__'], + [new MagicConst\File, '__FILE__'], + [new MagicConst\Function_, '__FUNCTION__'], + [new MagicConst\Line, '__LINE__'], + [new MagicConst\Method, '__METHOD__'], + [new MagicConst\Namespace_, '__NAMESPACE__'], + [new MagicConst\Trait_, '__TRAIT__'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/StringTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/StringTest.php new file mode 100644 index 0000000..814a775 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Scalar/StringTest.php @@ -0,0 +1,61 @@ +assertSame( + $expected, + String_::parseEscapeSequences($string, $quote) + ); + } + + /** + * @dataProvider provideTestParse + */ + public function testCreate($expected, $string) { + $this->assertSame( + $expected, + String_::parse($string) + ); + } + + public function provideTestParseEscapeSequences() { + return [ + ['"', '\\"', '"'], + ['\\"', '\\"', '`'], + ['\\"\\`', '\\"\\`', null], + ["\\\$\n\r\t\f\v", '\\\\\$\n\r\t\f\v', null], + ["\x1B", '\e', null], + [chr(255), '\xFF', null], + [chr(255), '\377', null], + [chr(0), '\400', null], + ["\0", '\0', null], + ['\xFF', '\\\\xFF', null], + ]; + } + + public function provideTestParse() { + $tests = [ + ['A', '\'A\''], + ['A', 'b\'A\''], + ['A', '"A"'], + ['A', 'b"A"'], + ['\\', '\'\\\\\''], + ['\'', '\'\\\'\''], + ]; + + foreach ($this->provideTestParseEscapeSequences() as $i => $test) { + // skip second and third tests, they aren't for double quotes + if ($i !== 1 && $i !== 2) { + $tests[] = [$test[0], '"' . $test[1] . '"']; + } + } + + return $tests; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassConstTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassConstTest.php new file mode 100644 index 0000000..9a1b469 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassConstTest.php @@ -0,0 +1,34 @@ +assertTrue($node->{'is' . $modifier}()); + } + + public function testNoModifiers() { + $node = new ClassConst([], 0); + + $this->assertTrue($node->isPublic()); + $this->assertFalse($node->isProtected()); + $this->assertFalse($node->isPrivate()); + } + + public function provideModifiers() { + return [ + ['public'], + ['protected'], + ['private'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassMethodTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassMethodTest.php new file mode 100644 index 0000000..51c5f1c --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassMethodTest.php @@ -0,0 +1,123 @@ + constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier)) + ]); + + $this->assertTrue($node->{'is' . $modifier}()); + } + + public function testNoModifiers() { + $node = new ClassMethod('foo', ['type' => 0]); + + $this->assertTrue($node->isPublic()); + $this->assertFalse($node->isProtected()); + $this->assertFalse($node->isPrivate()); + $this->assertFalse($node->isAbstract()); + $this->assertFalse($node->isFinal()); + $this->assertFalse($node->isStatic()); + $this->assertFalse($node->isMagic()); + } + + public function provideModifiers() { + return [ + ['public'], + ['protected'], + ['private'], + ['abstract'], + ['final'], + ['static'], + ]; + } + + /** + * Checks that implicit public modifier detection for method is working + * + * @dataProvider implicitPublicModifiers + * + * @param string $modifier Node type modifier + */ + public function testImplicitPublic(string $modifier) + { + $node = new ClassMethod('foo', [ + 'type' => constant('PhpParser\Node\Stmt\Class_::MODIFIER_' . strtoupper($modifier)) + ]); + + $this->assertTrue($node->isPublic(), 'Node should be implicitly public'); + } + + public function implicitPublicModifiers() { + return [ + ['abstract'], + ['final'], + ['static'], + ]; + } + + /** + * @dataProvider provideMagics + * + * @param string $name Node name + */ + public function testMagic(string $name) { + $node = new ClassMethod($name); + $this->assertTrue($node->isMagic(), 'Method should be magic'); + } + + public function provideMagics() { + return [ + ['__construct'], + ['__DESTRUCT'], + ['__caLL'], + ['__callstatic'], + ['__get'], + ['__set'], + ['__isset'], + ['__unset'], + ['__sleep'], + ['__wakeup'], + ['__tostring'], + ['__set_state'], + ['__clone'], + ['__invoke'], + ['__debuginfo'], + ]; + } + + public function testFunctionLike() { + $param = new Param(new Variable('a')); + $type = new Name('Foo'); + $return = new Return_(new Variable('a')); + $method = new ClassMethod('test', [ + 'byRef' => false, + 'params' => [$param], + 'returnType' => $type, + 'stmts' => [$return], + ]); + + $this->assertFalse($method->returnsByRef()); + $this->assertSame([$param], $method->getParams()); + $this->assertSame($type, $method->getReturnType()); + $this->assertSame([$return], $method->getStmts()); + + $method = new ClassMethod('test', [ + 'byRef' => true, + 'stmts' => null, + ]); + + $this->assertTrue($method->returnsByRef()); + $this->assertNull($method->getStmts()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassTest.php new file mode 100644 index 0000000..accf813 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/ClassTest.php @@ -0,0 +1,113 @@ + Class_::MODIFIER_ABSTRACT]); + $this->assertTrue($class->isAbstract()); + + $class = new Class_('Foo'); + $this->assertFalse($class->isAbstract()); + } + + public function testIsFinal() { + $class = new Class_('Foo', ['type' => Class_::MODIFIER_FINAL]); + $this->assertTrue($class->isFinal()); + + $class = new Class_('Foo'); + $this->assertFalse($class->isFinal()); + } + + public function testGetTraitUses() { + $traitUses = [ + new TraitUse([new Trait_('foo')]), + new TraitUse([new Trait_('bar')]), + ]; + $class = new Class_('Foo', [ + 'stmts' => [ + $traitUses[0], + new ClassMethod('fooBar'), + $traitUses[1], + ] + ]); + + $this->assertSame($traitUses, $class->getTraitUses()); + } + + public function testGetMethods() { + $methods = [ + new ClassMethod('foo'), + new ClassMethod('bar'), + new ClassMethod('fooBar'), + ]; + $class = new Class_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $methods[0], + new ClassConst([]), + $methods[1], + new Property(0, []), + $methods[2], + ] + ]); + + $this->assertSame($methods, $class->getMethods()); + } + + public function testGetConstants() { + $constants = [ + new ClassConst([new \PhpParser\Node\Const_('foo', new String_('foo_value'))]), + new ClassConst([new \PhpParser\Node\Const_('bar', new String_('bar_value'))]), + ]; + $class = new Class_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $constants[0], + new ClassMethod('fooBar'), + $constants[1], + ] + ]); + + $this->assertSame($constants, $class->getConstants()); + } + + public function testGetProperties() + { + $properties = [ + new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('foo')]), + new Property(Class_::MODIFIER_PUBLIC, [new PropertyProperty('bar')]), + ]; + $class = new Class_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $properties[0], + new ClassConst([]), + $properties[1], + new ClassMethod('fooBar'), + ] + ]); + + $this->assertSame($properties, $class->getProperties()); + } + + public function testGetMethod() { + $methodConstruct = new ClassMethod('__CONSTRUCT'); + $methodTest = new ClassMethod('test'); + $class = new Class_('Foo', [ + 'stmts' => [ + new ClassConst([]), + $methodConstruct, + new Property(0, []), + $methodTest, + ] + ]); + + $this->assertSame($methodConstruct, $class->getMethod('__construct')); + $this->assertSame($methodTest, $class->getMethod('test')); + $this->assertNull($class->getMethod('nonExisting')); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/InterfaceTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/InterfaceTest.php new file mode 100644 index 0000000..48c8e2b --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/InterfaceTest.php @@ -0,0 +1,44 @@ + [ + new Node\Stmt\ClassConst([new Node\Const_('C1', new Node\Scalar\String_('C1'))]), + $methods[0], + new Node\Stmt\ClassConst([new Node\Const_('C2', new Node\Scalar\String_('C2'))]), + $methods[1], + new Node\Stmt\ClassConst([new Node\Const_('C3', new Node\Scalar\String_('C3'))]), + ] + ]); + + $this->assertSame($methods, $interface->getMethods()); + } + + public function testGetConstants() { + $constants = [ + new ClassConst([new \PhpParser\Node\Const_('foo', new String_('foo_value'))]), + new ClassConst([new \PhpParser\Node\Const_('bar', new String_('bar_value'))]), + ]; + $class = new Interface_('Foo', [ + 'stmts' => [ + new TraitUse([]), + $constants[0], + new ClassMethod('fooBar'), + $constants[1], + ] + ]); + + $this->assertSame($constants, $class->getConstants()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/PropertyTest.php b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/PropertyTest.php new file mode 100644 index 0000000..e5d69fb --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Node/Stmt/PropertyTest.php @@ -0,0 +1,44 @@ +assertTrue($node->{'is' . $modifier}()); + } + + public function testNoModifiers() { + $node = new Property(0, []); + + $this->assertTrue($node->isPublic()); + $this->assertFalse($node->isProtected()); + $this->assertFalse($node->isPrivate()); + $this->assertFalse($node->isStatic()); + } + + public function testStaticImplicitlyPublic() { + $node = new Property(Class_::MODIFIER_STATIC, []); + $this->assertTrue($node->isPublic()); + $this->assertFalse($node->isProtected()); + $this->assertFalse($node->isPrivate()); + $this->assertTrue($node->isStatic()); + } + + public function provideModifiers() { + return [ + ['public'], + ['protected'], + ['private'], + ['static'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeAbstractTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeAbstractTest.php new file mode 100644 index 0000000..071b8f2 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeAbstractTest.php @@ -0,0 +1,325 @@ +subNode1 = $subNode1; + $this->subNode2 = $subNode2; + } + + public function getSubNodeNames() : array { + return ['subNode1', 'subNode2']; + } + + // This method is only overwritten because the node is located in an unusual namespace + public function getType() : string { + return 'Dummy'; + } +} + +class NodeAbstractTest extends \PHPUnit\Framework\TestCase +{ + public function provideNodes() { + $attributes = [ + 'startLine' => 10, + 'endLine' => 11, + 'startTokenPos' => 12, + 'endTokenPos' => 13, + 'startFilePos' => 14, + 'endFilePos' => 15, + 'comments' => [ + new Comment('// Comment' . "\n"), + new Comment\Doc('/** doc comment */'), + ], + ]; + + $node = new DummyNode('value1', 'value2', $attributes); + $node->notSubNode = 'value3'; + + return [ + [$attributes, $node], + ]; + } + + /** + * @dataProvider provideNodes + */ + public function testConstruct(array $attributes, Node $node) { + $this->assertSame('Dummy', $node->getType()); + $this->assertSame(['subNode1', 'subNode2'], $node->getSubNodeNames()); + $this->assertSame(10, $node->getLine()); + $this->assertSame(10, $node->getStartLine()); + $this->assertSame(11, $node->getEndLine()); + $this->assertSame(12, $node->getStartTokenPos()); + $this->assertSame(13, $node->getEndTokenPos()); + $this->assertSame(14, $node->getStartFilePos()); + $this->assertSame(15, $node->getEndFilePos()); + $this->assertSame('/** doc comment */', $node->getDocComment()->getText()); + $this->assertSame('value1', $node->subNode1); + $this->assertSame('value2', $node->subNode2); + $this->assertObjectHasAttribute('subNode1', $node); + $this->assertObjectHasAttribute('subNode2', $node); + $this->assertObjectNotHasAttribute('subNode3', $node); + $this->assertSame($attributes, $node->getAttributes()); + $this->assertSame($attributes['comments'], $node->getComments()); + + return $node; + } + + /** + * @dataProvider provideNodes + */ + public function testGetDocComment(array $attributes, Node $node) { + $this->assertSame('/** doc comment */', $node->getDocComment()->getText()); + $comments = $node->getComments(); + + array_pop($comments); // remove doc comment + $node->setAttribute('comments', $comments); + $this->assertNull($node->getDocComment()); + + array_pop($comments); // remove comment + $node->setAttribute('comments', $comments); + $this->assertNull($node->getDocComment()); + } + + public function testSetDocComment() { + $node = new DummyNode(null, null, []); + + // Add doc comment to node without comments + $docComment = new Comment\Doc('/** doc */'); + $node->setDocComment($docComment); + $this->assertSame($docComment, $node->getDocComment()); + + // Replace it + $docComment = new Comment\Doc('/** doc 2 */'); + $node->setDocComment($docComment); + $this->assertSame($docComment, $node->getDocComment()); + + // Add docmment to node with other comments + $c1 = new Comment('/* foo */'); + $c2 = new Comment('/* bar */'); + $docComment = new Comment\Doc('/** baz */'); + $node->setAttribute('comments', [$c1, $c2]); + $node->setDocComment($docComment); + $this->assertSame([$c1, $c2, $docComment], $node->getAttribute('comments')); + } + + /** + * @dataProvider provideNodes + */ + public function testChange(array $attributes, Node $node) { + // direct modification + $node->subNode = 'newValue'; + $this->assertSame('newValue', $node->subNode); + + // indirect modification + $subNode =& $node->subNode; + $subNode = 'newNewValue'; + $this->assertSame('newNewValue', $node->subNode); + + // removal + unset($node->subNode); + $this->assertObjectNotHasAttribute('subNode', $node); + } + + /** + * @dataProvider provideNodes + */ + public function testIteration(array $attributes, Node $node) { + // Iteration is simple object iteration over properties, + // not over subnodes + $i = 0; + foreach ($node as $key => $value) { + if ($i === 0) { + $this->assertSame('subNode1', $key); + $this->assertSame('value1', $value); + } elseif ($i === 1) { + $this->assertSame('subNode2', $key); + $this->assertSame('value2', $value); + } elseif ($i === 2) { + $this->assertSame('notSubNode', $key); + $this->assertSame('value3', $value); + } else { + throw new \Exception; + } + $i++; + } + $this->assertSame(3, $i); + } + + public function testAttributes() { + /** @var $node Node */ + $node = $this->getMockForAbstractClass(NodeAbstract::class); + + $this->assertEmpty($node->getAttributes()); + + $node->setAttribute('key', 'value'); + $this->assertTrue($node->hasAttribute('key')); + $this->assertSame('value', $node->getAttribute('key')); + + $this->assertFalse($node->hasAttribute('doesNotExist')); + $this->assertNull($node->getAttribute('doesNotExist')); + $this->assertSame('default', $node->getAttribute('doesNotExist', 'default')); + + $node->setAttribute('null', null); + $this->assertTrue($node->hasAttribute('null')); + $this->assertNull($node->getAttribute('null')); + $this->assertNull($node->getAttribute('null', 'default')); + + $this->assertSame( + [ + 'key' => 'value', + 'null' => null, + ], + $node->getAttributes() + ); + + $node->setAttributes( + [ + 'a' => 'b', + 'c' => null, + ] + ); + $this->assertSame( + [ + 'a' => 'b', + 'c' => null, + ], + $node->getAttributes() + ); + } + + public function testJsonSerialization() { + $code = <<<'PHP' +parse(canonicalize($code)); + $json = json_encode($stmts, JSON_PRETTY_PRINT); + $this->assertEquals(canonicalize($expected), canonicalize($json)); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeDumperTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeDumperTest.php new file mode 100644 index 0000000..036c3d1 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeDumperTest.php @@ -0,0 +1,106 @@ +assertSame($this->canonicalize($dump), $this->canonicalize($dumper->dump($node))); + } + + public function provideTestDump() { + return [ + [ + [], +'array( +)' + ], + [ + ['Foo', 'Bar', 'Key' => 'FooBar'], +'array( + 0: Foo + 1: Bar + Key: FooBar +)' + ], + [ + new Node\Name(['Hallo', 'World']), +'Name( + parts: array( + 0: Hallo + 1: World + ) +)' + ], + [ + new Node\Expr\Array_([ + new Node\Expr\ArrayItem(new Node\Scalar\String_('Foo')) + ]), +'Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: Foo + ) + byRef: false + unpack: false + ) + ) +)' + ], + ]; + } + + public function testDumpWithPositions() { + $parser = (new ParserFactory)->create( + ParserFactory::ONLY_PHP7, + new Lexer(['usedAttributes' => ['startLine', 'endLine', 'startFilePos', 'endFilePos']]) + ); + $dumper = new NodeDumper(['dumpPositions' => true]); + + $code = "parse($code); + $dump = $dumper->dump($stmts, $code); + + $this->assertSame($this->canonicalize($expected), $this->canonicalize($dump)); + } + + public function testError() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Can only dump nodes and arrays.'); + $dumper = new NodeDumper; + $dumper->dump(new \stdClass); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeFinderTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeFinderTest.php new file mode 100644 index 0000000..909738a --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeFinderTest.php @@ -0,0 +1,59 @@ +var, $assign->expr->left, $assign->expr->right]; + return [$stmts, $vars]; + } + + public function testFind() { + $finder = new NodeFinder; + list($stmts, $vars) = $this->getStmtsAndVars(); + $varFilter = function(Node $node) { + return $node instanceof Expr\Variable; + }; + $this->assertSame($vars, $finder->find($stmts, $varFilter)); + $this->assertSame($vars, $finder->find($stmts[0], $varFilter)); + + $noneFilter = function () { return false; }; + $this->assertSame([], $finder->find($stmts, $noneFilter)); + } + + public function testFindInstanceOf() { + $finder = new NodeFinder; + list($stmts, $vars) = $this->getStmtsAndVars(); + $this->assertSame($vars, $finder->findInstanceOf($stmts, Expr\Variable::class)); + $this->assertSame($vars, $finder->findInstanceOf($stmts[0], Expr\Variable::class)); + $this->assertSame([], $finder->findInstanceOf($stmts, Expr\BinaryOp\Mul::class)); + } + + public function testFindFirst() { + $finder = new NodeFinder; + list($stmts, $vars) = $this->getStmtsAndVars(); + $varFilter = function(Node $node) { + return $node instanceof Expr\Variable; + }; + $this->assertSame($vars[0], $finder->findFirst($stmts, $varFilter)); + $this->assertSame($vars[0], $finder->findFirst($stmts[0], $varFilter)); + + $noneFilter = function () { return false; }; + $this->assertNull($finder->findFirst($stmts, $noneFilter)); + } + + public function testFindFirstInstanceOf() { + $finder = new NodeFinder; + list($stmts, $vars) = $this->getStmtsAndVars(); + $this->assertSame($vars[0], $finder->findFirstInstanceOf($stmts, Expr\Variable::class)); + $this->assertSame($vars[0], $finder->findFirstInstanceOf($stmts[0], Expr\Variable::class)); + $this->assertNull($finder->findFirstInstanceOf($stmts, Expr\BinaryOp\Mul::class)); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeTraverserTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeTraverserTest.php new file mode 100644 index 0000000..e988604 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeTraverserTest.php @@ -0,0 +1,348 @@ +getMockBuilder(NodeVisitor::class)->getMock(); + + $visitor->expects($this->at(0))->method('beforeTraverse')->with($stmts); + $visitor->expects($this->at(1))->method('enterNode')->with($echoNode); + $visitor->expects($this->at(2))->method('enterNode')->with($str1Node); + $visitor->expects($this->at(3))->method('leaveNode')->with($str1Node); + $visitor->expects($this->at(4))->method('enterNode')->with($str2Node); + $visitor->expects($this->at(5))->method('leaveNode')->with($str2Node); + $visitor->expects($this->at(6))->method('leaveNode')->with($echoNode); + $visitor->expects($this->at(7))->method('afterTraverse')->with($stmts); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + + $this->assertEquals($stmts, $traverser->traverse($stmts)); + } + + public function testModifying() { + $str1Node = new String_('Foo'); + $str2Node = new String_('Bar'); + $printNode = new Expr\Print_($str1Node); + + // first visitor changes the node, second verifies the change + $visitor1 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor2 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + // replace empty statements with string1 node + $visitor1->expects($this->at(0))->method('beforeTraverse')->with([]) + ->willReturn([$str1Node]); + $visitor2->expects($this->at(0))->method('beforeTraverse')->with([$str1Node]); + + // replace string1 node with print node + $visitor1->expects($this->at(1))->method('enterNode')->with($str1Node) + ->willReturn($printNode); + $visitor2->expects($this->at(1))->method('enterNode')->with($printNode); + + // replace string1 node with string2 node + $visitor1->expects($this->at(2))->method('enterNode')->with($str1Node) + ->willReturn($str2Node); + $visitor2->expects($this->at(2))->method('enterNode')->with($str2Node); + + // replace string2 node with string1 node again + $visitor1->expects($this->at(3))->method('leaveNode')->with($str2Node) + ->willReturn($str1Node); + $visitor2->expects($this->at(3))->method('leaveNode')->with($str1Node); + + // replace print node with string1 node again + $visitor1->expects($this->at(4))->method('leaveNode')->with($printNode) + ->willReturn($str1Node); + $visitor2->expects($this->at(4))->method('leaveNode')->with($str1Node); + + // replace string1 node with empty statements again + $visitor1->expects($this->at(5))->method('afterTraverse')->with([$str1Node]) + ->willReturn([]); + $visitor2->expects($this->at(5))->method('afterTraverse')->with([]); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor1); + $traverser->addVisitor($visitor2); + + // as all operations are reversed we end where we start + $this->assertEquals([], $traverser->traverse([])); + } + + public function testRemove() { + $str1Node = new String_('Foo'); + $str2Node = new String_('Bar'); + + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + // remove the string1 node, leave the string2 node + $visitor->expects($this->at(2))->method('leaveNode')->with($str1Node) + ->willReturn(NodeTraverser::REMOVE_NODE); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + + $this->assertEquals([$str2Node], $traverser->traverse([$str1Node, $str2Node])); + } + + public function testMerge() { + $strStart = new String_('Start'); + $strMiddle = new String_('End'); + $strEnd = new String_('Middle'); + $strR1 = new String_('Replacement 1'); + $strR2 = new String_('Replacement 2'); + + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + // replace strMiddle with strR1 and strR2 by merge + $visitor->expects($this->at(4))->method('leaveNode')->with($strMiddle) + ->willReturn([$strR1, $strR2]); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + + $this->assertEquals( + [$strStart, $strR1, $strR2, $strEnd], + $traverser->traverse([$strStart, $strMiddle, $strEnd]) + ); + } + + public function testInvalidDeepArray() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Invalid node structure: Contains nested arrays'); + $strNode = new String_('Foo'); + $stmts = [[[$strNode]]]; + + $traverser = new NodeTraverser; + $this->assertEquals($stmts, $traverser->traverse($stmts)); + } + + public function testDontTraverseChildren() { + $strNode = new String_('str'); + $printNode = new Expr\Print_($strNode); + $varNode = new Expr\Variable('foo'); + $mulNode = new Expr\BinaryOp\Mul($varNode, $varNode); + $negNode = new Expr\UnaryMinus($mulNode); + $stmts = [$printNode, $negNode]; + + $visitor1 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor2 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + $visitor1->expects($this->at(1))->method('enterNode')->with($printNode) + ->willReturn(NodeTraverser::DONT_TRAVERSE_CHILDREN); + $visitor2->expects($this->at(1))->method('enterNode')->with($printNode); + + $visitor1->expects($this->at(2))->method('leaveNode')->with($printNode); + $visitor2->expects($this->at(2))->method('leaveNode')->with($printNode); + + $visitor1->expects($this->at(3))->method('enterNode')->with($negNode); + $visitor2->expects($this->at(3))->method('enterNode')->with($negNode); + + $visitor1->expects($this->at(4))->method('enterNode')->with($mulNode); + $visitor2->expects($this->at(4))->method('enterNode')->with($mulNode) + ->willReturn(NodeTraverser::DONT_TRAVERSE_CHILDREN); + + $visitor1->expects($this->at(5))->method('leaveNode')->with($mulNode); + $visitor2->expects($this->at(5))->method('leaveNode')->with($mulNode); + + $visitor1->expects($this->at(6))->method('leaveNode')->with($negNode); + $visitor2->expects($this->at(6))->method('leaveNode')->with($negNode); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor1); + $traverser->addVisitor($visitor2); + + $this->assertEquals($stmts, $traverser->traverse($stmts)); + } + + public function testDontTraverseCurrentAndChildren() { + // print 'str'; -($foo * $foo); + $strNode = new String_('str'); + $printNode = new Expr\Print_($strNode); + $varNode = new Expr\Variable('foo'); + $mulNode = new Expr\BinaryOp\Mul($varNode, $varNode); + $divNode = new Expr\BinaryOp\Div($varNode, $varNode); + $negNode = new Expr\UnaryMinus($mulNode); + $stmts = [$printNode, $negNode]; + + $visitor1 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor2 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + $visitor1->expects($this->at(1))->method('enterNode')->with($printNode) + ->willReturn(NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN); + $visitor1->expects($this->at(2))->method('leaveNode')->with($printNode); + + $visitor1->expects($this->at(3))->method('enterNode')->with($negNode); + $visitor2->expects($this->at(1))->method('enterNode')->with($negNode); + + $visitor1->expects($this->at(4))->method('enterNode')->with($mulNode) + ->willReturn(NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN); + $visitor1->expects($this->at(5))->method('leaveNode')->with($mulNode)->willReturn($divNode); + + $visitor1->expects($this->at(6))->method('leaveNode')->with($negNode); + $visitor2->expects($this->at(2))->method('leaveNode')->with($negNode); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor1); + $traverser->addVisitor($visitor2); + + $resultStmts = $traverser->traverse($stmts); + + $this->assertInstanceOf(Expr\BinaryOp\Div::class, $resultStmts[1]->expr); + } + + public function testStopTraversal() { + $varNode1 = new Expr\Variable('a'); + $varNode2 = new Expr\Variable('b'); + $varNode3 = new Expr\Variable('c'); + $mulNode = new Expr\BinaryOp\Mul($varNode1, $varNode2); + $printNode = new Expr\Print_($varNode3); + $stmts = [$mulNode, $printNode]; + + // From enterNode() with array parent + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor->expects($this->at(1))->method('enterNode')->with($mulNode) + ->willReturn(NodeTraverser::STOP_TRAVERSAL); + $visitor->expects($this->at(2))->method('afterTraverse'); + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $this->assertEquals($stmts, $traverser->traverse($stmts)); + + // From enterNode with Node parent + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor->expects($this->at(2))->method('enterNode')->with($varNode1) + ->willReturn(NodeTraverser::STOP_TRAVERSAL); + $visitor->expects($this->at(3))->method('afterTraverse'); + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $this->assertEquals($stmts, $traverser->traverse($stmts)); + + // From leaveNode with Node parent + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor->expects($this->at(3))->method('leaveNode')->with($varNode1) + ->willReturn(NodeTraverser::STOP_TRAVERSAL); + $visitor->expects($this->at(4))->method('afterTraverse'); + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $this->assertEquals($stmts, $traverser->traverse($stmts)); + + // From leaveNode with array parent + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor->expects($this->at(6))->method('leaveNode')->with($mulNode) + ->willReturn(NodeTraverser::STOP_TRAVERSAL); + $visitor->expects($this->at(7))->method('afterTraverse'); + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $this->assertEquals($stmts, $traverser->traverse($stmts)); + + // Check that pending array modifications are still carried out + $visitor = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor->expects($this->at(6))->method('leaveNode')->with($mulNode) + ->willReturn(NodeTraverser::REMOVE_NODE); + $visitor->expects($this->at(7))->method('enterNode')->with($printNode) + ->willReturn(NodeTraverser::STOP_TRAVERSAL); + $visitor->expects($this->at(8))->method('afterTraverse'); + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor); + $this->assertEquals([$printNode], $traverser->traverse($stmts)); + + } + + public function testRemovingVisitor() { + $visitor1 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor2 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor3 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + + $traverser = new NodeTraverser; + $traverser->addVisitor($visitor1); + $traverser->addVisitor($visitor2); + $traverser->addVisitor($visitor3); + + $getVisitors = (function () { + return $this->visitors; + })->bindTo($traverser, NodeTraverser::class); + + $preExpected = [$visitor1, $visitor2, $visitor3]; + $this->assertSame($preExpected, $getVisitors()); + + $traverser->removeVisitor($visitor2); + + $postExpected = [0 => $visitor1, 2 => $visitor3]; + $this->assertSame($postExpected, $getVisitors()); + } + + public function testNoCloneNodes() { + $stmts = [new Node\Stmt\Echo_([new String_('Foo'), new String_('Bar')])]; + + $traverser = new NodeTraverser; + + $this->assertSame($stmts, $traverser->traverse($stmts)); + } + + /** + * @dataProvider provideTestInvalidReturn + */ + public function testInvalidReturn($visitor, $message) { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage($message); + + $stmts = [new Node\Stmt\Expression(new Node\Scalar\LNumber(42))]; + + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor); + $traverser->traverse($stmts); + } + + public function provideTestInvalidReturn() { + $visitor1 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor1->expects($this->at(1))->method('enterNode') + ->willReturn('foobar'); + + $visitor2 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor2->expects($this->at(2))->method('enterNode') + ->willReturn('foobar'); + + $visitor3 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor3->expects($this->at(3))->method('leaveNode') + ->willReturn('foobar'); + + $visitor4 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor4->expects($this->at(4))->method('leaveNode') + ->willReturn('foobar'); + + $visitor5 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor5->expects($this->at(3))->method('leaveNode') + ->willReturn([new Node\Scalar\DNumber(42.0)]); + + $visitor6 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor6->expects($this->at(4))->method('leaveNode') + ->willReturn(false); + + $visitor7 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor7->expects($this->at(1))->method('enterNode') + ->willReturn(new Node\Scalar\LNumber(42)); + + $visitor8 = $this->getMockBuilder(NodeVisitor::class)->getMock(); + $visitor8->expects($this->at(2))->method('enterNode') + ->willReturn(new Node\Stmt\Return_()); + + return [ + [$visitor1, 'enterNode() returned invalid value of type string'], + [$visitor2, 'enterNode() returned invalid value of type string'], + [$visitor3, 'leaveNode() returned invalid value of type string'], + [$visitor4, 'leaveNode() returned invalid value of type string'], + [$visitor5, 'leaveNode() may only return an array if the parent structure is an array'], + [$visitor6, 'bool(false) return from leaveNode() no longer supported. Return NodeTraverser::REMOVE_NODE instead'], + [$visitor7, 'Trying to replace statement (Stmt_Expression) with expression (Scalar_LNumber). Are you missing a Stmt_Expression wrapper?'], + [$visitor8, 'Trying to replace expression (Scalar_LNumber) with statement (Stmt_Return)'], + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FindingVisitorTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FindingVisitorTest.php new file mode 100644 index 0000000..27cb6dd --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FindingVisitorTest.php @@ -0,0 +1,53 @@ +addVisitor($visitor); + + $assign = new Expr\Assign(new Expr\Variable('a'), new Expr\BinaryOp\Concat( + new Expr\Variable('b'), new Expr\Variable('c') + )); + $stmts = [new Node\Stmt\Expression($assign)]; + + $traverser->traverse($stmts); + $this->assertSame([ + $assign->var, + $assign->expr->left, + $assign->expr->right, + ], $visitor->getFoundNodes()); + } + + public function testFindAll() { + $traverser = new NodeTraverser(); + $visitor = new FindingVisitor(function(Node $node) { + return true; // All nodes + }); + $traverser->addVisitor($visitor); + + $assign = new Expr\Assign(new Expr\Variable('a'), new Expr\BinaryOp\Concat( + new Expr\Variable('b'), new Expr\Variable('c') + )); + $stmts = [new Node\Stmt\Expression($assign)]; + + $traverser->traverse($stmts); + $this->assertSame([ + $stmts[0], + $assign, + $assign->var, + $assign->expr, + $assign->expr->left, + $assign->expr->right, + ], $visitor->getFoundNodes()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FirstFindingVisitorTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FirstFindingVisitorTest.php new file mode 100644 index 0000000..9ae8932 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/FirstFindingVisitorTest.php @@ -0,0 +1,38 @@ +addVisitor($visitor); + + $assign = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b')); + $stmts = [new Node\Stmt\Expression($assign)]; + + $traverser->traverse($stmts); + $this->assertSame($assign->var, $visitor->getFoundNode()); + } + + public function testFindNone() { + $traverser = new NodeTraverser(); + $visitor = new FirstFindingVisitor(function(Node $node) { + return $node instanceof Node\Expr\BinaryOp; + }); + $traverser->addVisitor($visitor); + + $assign = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b')); + $stmts = [new Node\Stmt\Expression($assign)]; + + $traverser->traverse($stmts); + $this->assertNull($visitor->getFoundNode()); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php new file mode 100644 index 0000000..ec09699 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php @@ -0,0 +1,512 @@ +addVisitor(new NameResolver); + + $stmts = $parser->parse($code); + $stmts = $traverser->traverse($stmts); + + $this->assertSame( + $this->canonicalize($expectedCode), + $prettyPrinter->prettyPrint($stmts) + ); + } + + /** + * @covers \PhpParser\NodeVisitor\NameResolver + */ + public function testResolveLocations() { + $code = <<<'EOC' + $a; +fn(A $a): A => $a; +fn(?A $a): ?A => $a; + +A::b(); +A::$b; +A::B; +new A; +$a instanceof A; + +namespace\a(); +namespace\A; + +try { + $someThing; +} catch (A $a) { + $someThingElse; +} +EOC; + $expectedCode = <<<'EOC' +namespace NS; + +class A extends \NS\B implements \NS\C, \NS\D +{ + use \NS\E, \NS\F, \NS\G { + f as private g; + \NS\E::h as i; + \NS\E::j insteadof \NS\F, \NS\G; + } +} +interface A extends \NS\C, \NS\D +{ + public function a(\NS\A $a) : \NS\A; +} +function f(\NS\A $a) : \NS\A +{ +} +function f2(array $a) : array +{ +} +function (\NS\A $a) : \NS\A { +}; +function fn3(?\NS\A $a) : ?\NS\A +{ +} +function fn4(?array $a) : ?array +{ +} +fn(array $a): array => $a; +fn(\NS\A $a): \NS\A => $a; +fn(?\NS\A $a): ?\NS\A => $a; +\NS\A::b(); +\NS\A::$b; +\NS\A::B; +new \NS\A(); +$a instanceof \NS\A; +\NS\a(); +\NS\A; +try { + $someThing; +} catch (\NS\A $a) { + $someThingElse; +} +EOC; + + $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative); + $prettyPrinter = new PhpParser\PrettyPrinter\Standard; + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $parser->parse($code); + $stmts = $traverser->traverse($stmts); + + $this->assertSame( + $this->canonicalize($expectedCode), + $prettyPrinter->prettyPrint($stmts) + ); + } + + public function testNoResolveSpecialName() { + $stmts = [new Node\Expr\New_(new Name('self'))]; + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $this->assertEquals($stmts, $traverser->traverse($stmts)); + } + + public function testAddDeclarationNamespacedName() { + $nsStmts = [ + new Stmt\Class_('A'), + new Stmt\Interface_('B'), + new Stmt\Function_('C'), + new Stmt\Const_([ + new Node\Const_('D', new Node\Scalar\LNumber(42)) + ]), + new Stmt\Trait_('E'), + new Expr\New_(new Stmt\Class_(null)), + ]; + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]); + $this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName); + $this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName); + $this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName); + $this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName); + $this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName); + $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class); + + $stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]); + $this->assertSame('A', (string) $stmts[0]->stmts[0]->namespacedName); + $this->assertSame('B', (string) $stmts[0]->stmts[1]->namespacedName); + $this->assertSame('C', (string) $stmts[0]->stmts[2]->namespacedName); + $this->assertSame('D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName); + $this->assertSame('E', (string) $stmts[0]->stmts[4]->namespacedName); + $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class); + } + + public function testAddRuntimeResolvedNamespacedName() { + $stmts = [ + new Stmt\Namespace_(new Name('NS'), [ + new Expr\FuncCall(new Name('foo')), + new Expr\ConstFetch(new Name('FOO')), + ]), + new Stmt\Namespace_(null, [ + new Expr\FuncCall(new Name('foo')), + new Expr\ConstFetch(new Name('FOO')), + ]), + ]; + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + $stmts = $traverser->traverse($stmts); + + $this->assertSame('NS\\foo', (string) $stmts[0]->stmts[0]->name->getAttribute('namespacedName')); + $this->assertSame('NS\\FOO', (string) $stmts[0]->stmts[1]->name->getAttribute('namespacedName')); + + $this->assertFalse($stmts[1]->stmts[0]->name->hasAttribute('namespacedName')); + $this->assertFalse($stmts[1]->stmts[1]->name->hasAttribute('namespacedName')); + } + + /** + * @dataProvider provideTestError + */ + public function testError(Node $stmt, $errorMsg) { + $this->expectException(\PhpParser\Error::class); + $this->expectExceptionMessage($errorMsg); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + $traverser->traverse([$stmt]); + } + + public function provideTestError() { + return [ + [ + new Stmt\Use_([ + new Stmt\UseUse(new Name('A\B'), 'B', 0, ['startLine' => 1]), + new Stmt\UseUse(new Name('C\D'), 'B', 0, ['startLine' => 2]), + ], Stmt\Use_::TYPE_NORMAL), + 'Cannot use C\D as B because the name is already in use on line 2' + ], + [ + new Stmt\Use_([ + new Stmt\UseUse(new Name('a\b'), 'b', 0, ['startLine' => 1]), + new Stmt\UseUse(new Name('c\d'), 'B', 0, ['startLine' => 2]), + ], Stmt\Use_::TYPE_FUNCTION), + 'Cannot use function c\d as B because the name is already in use on line 2' + ], + [ + new Stmt\Use_([ + new Stmt\UseUse(new Name('A\B'), 'B', 0, ['startLine' => 1]), + new Stmt\UseUse(new Name('C\D'), 'B', 0, ['startLine' => 2]), + ], Stmt\Use_::TYPE_CONSTANT), + 'Cannot use const C\D as B because the name is already in use on line 2' + ], + [ + new Expr\New_(new Name\FullyQualified('self', ['startLine' => 3])), + "'\\self' is an invalid class name on line 3" + ], + [ + new Expr\New_(new Name\Relative('self', ['startLine' => 3])), + "'\\self' is an invalid class name on line 3" + ], + [ + new Expr\New_(new Name\FullyQualified('PARENT', ['startLine' => 3])), + "'\\PARENT' is an invalid class name on line 3" + ], + [ + new Expr\New_(new Name\Relative('STATIC', ['startLine' => 3])), + "'\\STATIC' is an invalid class name on line 3" + ], + ]; + } + + public function testClassNameIsCaseInsensitive() + { + $source = <<<'EOC' +parse($source); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse($stmts); + $stmt = $stmts[0]; + + $assign = $stmt->stmts[1]->expr; + $this->assertSame(['Bar', 'Baz'], $assign->expr->class->parts); + } + + public function testSpecialClassNamesAreCaseInsensitive() { + $source = <<<'EOC' +parse($source); + + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver); + + $stmts = $traverser->traverse($stmts); + $classStmt = $stmts[0]; + $methodStmt = $classStmt->stmts[0]->stmts[0]; + + $this->assertSame('SELF', (string) $methodStmt->stmts[0]->expr->class); + $this->assertSame('PARENT', (string) $methodStmt->stmts[1]->expr->class); + $this->assertSame('STATIC', (string) $methodStmt->stmts[2]->expr->class); + } + + public function testAddOriginalNames() { + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true])); + + $n1 = new Name('Bar'); + $n2 = new Name('bar'); + $origStmts = [ + new Stmt\Namespace_(new Name('Foo'), [ + new Expr\ClassConstFetch($n1, 'FOO'), + new Expr\FuncCall($n2), + ]) + ]; + + $stmts = $traverser->traverse($origStmts); + + $this->assertSame($n1, $stmts[0]->stmts[0]->class->getAttribute('originalName')); + $this->assertSame($n2, $stmts[0]->stmts[1]->name->getAttribute('originalName')); + } + + public function testAttributeOnlyMode() { + $traverser = new PhpParser\NodeTraverser; + $traverser->addVisitor(new NameResolver(null, ['replaceNodes' => false])); + + $n1 = new Name('Bar'); + $n2 = new Name('bar'); + $origStmts = [ + new Stmt\Namespace_(new Name('Foo'), [ + new Expr\ClassConstFetch($n1, 'FOO'), + new Expr\FuncCall($n2), + ]) + ]; + + $traverser->traverse($origStmts); + + $this->assertEquals( + new Name\FullyQualified('Foo\Bar'), $n1->getAttribute('resolvedName')); + $this->assertFalse($n2->hasAttribute('resolvedName')); + $this->assertEquals( + new Name\FullyQualified('Foo\bar'), $n2->getAttribute('namespacedName')); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Parser/MultipleTest.php b/vendor/nikic/php-parser/test/PhpParser/Parser/MultipleTest.php new file mode 100644 index 0000000..f89e7bf --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Parser/MultipleTest.php @@ -0,0 +1,94 @@ + []]); + return new Multiple([new Php7($lexer), new Php5($lexer)]); + } + + private function getPrefer5() { + $lexer = new Lexer(['usedAttributes' => []]); + return new Multiple([new Php5($lexer), new Php7($lexer)]); + } + + /** @dataProvider provideTestParse */ + public function testParse($code, Multiple $parser, $expected) { + $this->assertEquals($expected, $parser->parse($code)); + } + + public function provideTestParse() { + return [ + [ + // PHP 7 only code + 'getPrefer5(), + [ + new Stmt\Class_('Test', ['stmts' => [ + new Stmt\ClassMethod('function') + ]]), + ] + ], + [ + // PHP 5 only code + 'b;', + $this->getPrefer7(), + [ + new Stmt\Global_([ + new Expr\Variable(new Expr\PropertyFetch(new Expr\Variable('a'), 'b')) + ]) + ] + ], + [ + // Different meaning (PHP 5) + 'getPrefer5(), + [ + new Stmt\Expression(new Expr\Variable( + new Expr\ArrayDimFetch(new Expr\Variable('a'), LNumber::fromString('0')) + )) + ] + ], + [ + // Different meaning (PHP 7) + 'getPrefer7(), + [ + new Stmt\Expression(new Expr\ArrayDimFetch( + new Expr\Variable(new Expr\Variable('a')), LNumber::fromString('0') + )) + ] + ], + ]; + } + + public function testThrownError() { + $this->expectException(Error::class); + $this->expectExceptionMessage('FAIL A'); + + $parserA = $this->getMockBuilder(\PhpParser\Parser::class)->getMock(); + $parserA->expects($this->at(0)) + ->method('parse')->willThrowException(new Error('FAIL A')); + + $parserB = $this->getMockBuilder(\PhpParser\Parser::class)->getMock(); + $parserB->expects($this->at(0)) + ->method('parse')->willThrowException(new Error('FAIL B')); + + $parser = new Multiple([$parserA, $parserB]); + $parser->parse('dummy'); + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/Parser/Php5Test.php b/vendor/nikic/php-parser/test/PhpParser/Parser/Php5Test.php new file mode 100644 index 0000000..4386b51 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/Parser/Php5Test.php @@ -0,0 +1,13 @@ +assertInstanceOf($expected, (new ParserFactory)->create($kind, $lexer)); + } + + public function provideTestCreate() { + $lexer = new Lexer(); + return [ + [ + ParserFactory::PREFER_PHP7, $lexer, + Parser\Multiple::class + ], + [ + ParserFactory::PREFER_PHP5, null, + Parser\Multiple::class + ], + [ + ParserFactory::ONLY_PHP7, null, + Parser\Php7::class + ], + [ + ParserFactory::ONLY_PHP5, $lexer, + Parser\Php5::class + ] + ]; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/ParserTest.php b/vendor/nikic/php-parser/test/PhpParser/ParserTest.php new file mode 100644 index 0000000..0f7f1b7 --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/ParserTest.php @@ -0,0 +1,183 @@ +expectException(Error::class); + $this->expectExceptionMessage('Syntax error, unexpected EOF on line 1'); + $parser = $this->getParser(new Lexer()); + $parser->parse('expectException(Error::class); + $this->expectExceptionMessage('Cannot use foo as self because \'self\' is a special class name on line 1'); + $parser = $this->getParser(new Lexer()); + $parser->parse('expectException(Error::class); + $this->expectExceptionMessage('Unterminated comment on line 1'); + $parser = $this->getParser(new Lexer()); + $parser->parse(' [ + 'comments', 'startLine', 'endLine', + 'startTokenPos', 'endTokenPos', + ] + ]); + + $code = <<<'EOC' +getParser($lexer); + $stmts = $parser->parse($code); + + /** @var Stmt\Function_ $fn */ + $fn = $stmts[0]; + $this->assertInstanceOf(Stmt\Function_::class, $fn); + $this->assertEquals([ + 'comments' => [ + new Comment\Doc('/** Doc comment */', 2, 6, 1), + ], + 'startLine' => 3, + 'endLine' => 7, + 'startTokenPos' => 3, + 'endTokenPos' => 21, + ], $fn->getAttributes()); + + $param = $fn->params[0]; + $this->assertInstanceOf(Node\Param::class, $param); + $this->assertEquals([ + 'startLine' => 3, + 'endLine' => 3, + 'startTokenPos' => 7, + 'endTokenPos' => 7, + ], $param->getAttributes()); + + /** @var Stmt\Echo_ $echo */ + $echo = $fn->stmts[0]; + $this->assertInstanceOf(Stmt\Echo_::class, $echo); + $this->assertEquals([ + 'comments' => [ + new Comment("// Line\n", 4, 49, 12), + new Comment("// Comments\n", 5, 61, 14), + ], + 'startLine' => 6, + 'endLine' => 6, + 'startTokenPos' => 16, + 'endTokenPos' => 19, + ], $echo->getAttributes()); + + /** @var \PhpParser\Node\Expr\Variable $var */ + $var = $echo->exprs[0]; + $this->assertInstanceOf(Expr\Variable::class, $var); + $this->assertEquals([ + 'startLine' => 6, + 'endLine' => 6, + 'startTokenPos' => 18, + 'endTokenPos' => 18, + ], $var->getAttributes()); + } + + public function testInvalidToken() { + $this->expectException(\RangeException::class); + $this->expectExceptionMessage('The lexer returned an invalid token (id=999, value=foobar)'); + $lexer = new InvalidTokenLexer; + $parser = $this->getParser($lexer); + $parser->parse('dummy'); + } + + /** + * @dataProvider provideTestExtraAttributes + */ + public function testExtraAttributes($code, $expectedAttributes) { + $parser = $this->getParser(new Lexer\Emulative); + $stmts = $parser->parse("expr : $stmts[0]; + $attributes = $node->getAttributes(); + foreach ($expectedAttributes as $name => $value) { + $this->assertSame($value, $attributes[$name]); + } + } + + public function provideTestExtraAttributes() { + return [ + ['0', ['kind' => Scalar\LNumber::KIND_DEC]], + ['9', ['kind' => Scalar\LNumber::KIND_DEC]], + ['07', ['kind' => Scalar\LNumber::KIND_OCT]], + ['0xf', ['kind' => Scalar\LNumber::KIND_HEX]], + ['0XF', ['kind' => Scalar\LNumber::KIND_HEX]], + ['0b1', ['kind' => Scalar\LNumber::KIND_BIN]], + ['0B1', ['kind' => Scalar\LNumber::KIND_BIN]], + ['[]', ['kind' => Expr\Array_::KIND_SHORT]], + ['array()', ['kind' => Expr\Array_::KIND_LONG]], + ["'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]], + ["b'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]], + ["B'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]], + ['"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ['b"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ['B"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ['"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ['b"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ['B"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]], + ["<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<< String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<<<\"STR\"\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["b<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["B<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<<< \t 'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<<<'\xff'\n\xff\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => "\xff", 'docIndentation' => '']], + ["<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["b<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["B<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<<< \t \"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => '']], + ["<< String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => ' ']], + ["<< String_::KIND_HEREDOC, 'docLabel' => 'STR', 'docIndentation' => "\t"]], + ["<<<'STR'\n Foo\n STR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR', 'docIndentation' => ' ']], + ["die", ['kind' => Expr\Exit_::KIND_DIE]], + ["die('done')", ['kind' => Expr\Exit_::KIND_DIE]], + ["exit", ['kind' => Expr\Exit_::KIND_EXIT]], + ["exit(1)", ['kind' => Expr\Exit_::KIND_EXIT]], + ["?>Foo", ['hasLeadingNewline' => false]], + ["?>\nFoo", ['hasLeadingNewline' => true]], + ["namespace Foo;", ['kind' => Stmt\Namespace_::KIND_SEMICOLON]], + ["namespace Foo {}", ['kind' => Stmt\Namespace_::KIND_BRACED]], + ["namespace {}", ['kind' => Stmt\Namespace_::KIND_BRACED]], + ["(float) 5.0", ['kind' => Expr\Cast\Double::KIND_FLOAT]], + ["(double) 5.0", ['kind' => Expr\Cast\Double::KIND_DOUBLE]], + ["(real) 5.0", ['kind' => Expr\Cast\Double::KIND_REAL]], + [" ( REAL ) 5.0", ['kind' => Expr\Cast\Double::KIND_REAL]], + ]; + } +} + +class InvalidTokenLexer extends Lexer +{ + public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int { + $value = 'foobar'; + return 999; + } +} diff --git a/vendor/nikic/php-parser/test/PhpParser/PrettyPrinterTest.php b/vendor/nikic/php-parser/test/PhpParser/PrettyPrinterTest.php new file mode 100644 index 0000000..e5b8a1a --- /dev/null +++ b/vendor/nikic/php-parser/test/PhpParser/PrettyPrinterTest.php @@ -0,0 +1,307 @@ +parseModeLine($modeLine); + $prettyPrinter = new Standard($options); + + try { + $output5 = canonicalize($prettyPrinter->$method($parser5->parse($code))); + } catch (Error $e) { + $output5 = null; + if ('php7' !== $version) { + throw $e; + } + } + + try { + $output7 = canonicalize($prettyPrinter->$method($parser7->parse($code))); + } catch (Error $e) { + $output7 = null; + if ('php5' !== $version) { + throw $e; + } + } + + if ('php5' === $version) { + $this->assertSame($expected, $output5, $name); + $this->assertNotSame($expected, $output7, $name); + } elseif ('php7' === $version) { + $this->assertSame($expected, $output7, $name); + $this->assertNotSame($expected, $output5, $name); + } else { + $this->assertSame($expected, $output5, $name); + $this->assertSame($expected, $output7, $name); + } + } + + /** + * @dataProvider provideTestPrettyPrint + * @covers \PhpParser\PrettyPrinter\Standard + */ + public function testPrettyPrint($name, $code, $expected, $mode) { + $this->doTestPrettyPrintMethod('prettyPrint', $name, $code, $expected, $mode); + } + + /** + * @dataProvider provideTestPrettyPrintFile + * @covers \PhpParser\PrettyPrinter\Standard + */ + public function testPrettyPrintFile($name, $code, $expected, $mode) { + $this->doTestPrettyPrintMethod('prettyPrintFile', $name, $code, $expected, $mode); + } + + public function provideTestPrettyPrint() { + return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'test'); + } + + public function provideTestPrettyPrintFile() { + return $this->getTests(__DIR__ . '/../code/prettyPrinter', 'file-test'); + } + + public function testPrettyPrintExpr() { + $prettyPrinter = new Standard; + $expr = new Expr\BinaryOp\Mul( + new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')), + new Expr\Variable('c') + ); + $this->assertEquals('($a + $b) * $c', $prettyPrinter->prettyPrintExpr($expr)); + + $expr = new Expr\Closure([ + 'stmts' => [new Stmt\Return_(new String_("a\nb"))] + ]); + $this->assertEquals("function () {\n return 'a\nb';\n}", $prettyPrinter->prettyPrintExpr($expr)); + } + + public function testCommentBeforeInlineHTML() { + $prettyPrinter = new PrettyPrinter\Standard; + $comment = new Comment\Doc("/**\n * This is a comment\n */"); + $stmts = [new Stmt\InlineHTML('Hello World!', ['comments' => [$comment]])]; + $expected = "\nHello World!"; + $this->assertSame($expected, $prettyPrinter->prettyPrintFile($stmts)); + } + + private function parseModeLine($modeLine) { + $parts = explode(' ', (string) $modeLine, 2); + $version = $parts[0] ?? 'both'; + $options = isset($parts[1]) ? json_decode($parts[1], true) : []; + return [$version, $options]; + } + + public function testArraySyntaxDefault() { + $prettyPrinter = new Standard(['shortArraySyntax' => true]); + $expr = new Expr\Array_([ + new Expr\ArrayItem(new String_('val'), new String_('key')) + ]); + $expected = "['key' => 'val']"; + $this->assertSame($expected, $prettyPrinter->prettyPrintExpr($expr)); + } + + /** + * @dataProvider provideTestKindAttributes + */ + public function testKindAttributes($node, $expected) { + $prttyPrinter = new PrettyPrinter\Standard; + $result = $prttyPrinter->prettyPrintExpr($node); + $this->assertSame($expected, $result); + } + + public function provideTestKindAttributes() { + $nowdoc = ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']; + $heredoc = ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']; + return [ + // Defaults to single quoted + [new String_('foo'), "'foo'"], + // Explicit single/double quoted + [new String_('foo', ['kind' => String_::KIND_SINGLE_QUOTED]), "'foo'"], + [new String_('foo', ['kind' => String_::KIND_DOUBLE_QUOTED]), '"foo"'], + // Fallback from doc string if no label + [new String_('foo', ['kind' => String_::KIND_NOWDOC]), "'foo'"], + [new String_('foo', ['kind' => String_::KIND_HEREDOC]), '"foo"'], + // Fallback if string contains label + [new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'A']), "'A\nB\nC'"], + [new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'B']), "'A\nB\nC'"], + [new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'C']), "'A\nB\nC'"], + [new String_("STR;", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']), "'STR;'"], + // Doc string if label not contained (or not in ending position) + [new String_("foo", $nowdoc), "<<<'STR'\nfoo\nSTR\n"], + [new String_("foo", $heredoc), "<<prettyPrintExpr($node); + $this->assertSame($expected, $result); + } + + public function provideTestUnnaturalLiterals() { + return [ + [new LNumber(-1), '-1'], + [new LNumber(-PHP_INT_MAX - 1), '(-' . PHP_INT_MAX . '-1)'], + [new LNumber(-1, ['kind' => LNumber::KIND_BIN]), '-0b1'], + [new LNumber(-1, ['kind' => LNumber::KIND_OCT]), '-01'], + [new LNumber(-1, ['kind' => LNumber::KIND_HEX]), '-0x1'], + [new DNumber(\INF), '\INF'], + [new DNumber(-\INF), '-\INF'], + [new DNumber(-\NAN), '\NAN'], + ]; + } + + public function testPrettyPrintWithError() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot pretty-print AST with Error nodes'); + $stmts = [new Stmt\Expression( + new Expr\PropertyFetch(new Expr\Variable('a'), new Expr\Error()) + )]; + $prettyPrinter = new PrettyPrinter\Standard; + $prettyPrinter->prettyPrint($stmts); + } + + public function testPrettyPrintWithErrorInClassConstFetch() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot pretty-print AST with Error nodes'); + $stmts = [new Stmt\Expression( + new Expr\ClassConstFetch(new Name('Foo'), new Expr\Error()) + )]; + $prettyPrinter = new PrettyPrinter\Standard; + $prettyPrinter->prettyPrint($stmts); + } + + public function testPrettyPrintEncapsedStringPart() { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Cannot directly print EncapsedStringPart'); + $expr = new Node\Scalar\EncapsedStringPart('foo'); + $prettyPrinter = new PrettyPrinter\Standard; + $prettyPrinter->prettyPrintExpr($expr); + } + + /** + * @dataProvider provideTestFormatPreservingPrint + * @covers \PhpParser\PrettyPrinter\Standard + */ + public function testFormatPreservingPrint($name, $code, $modification, $expected, $modeLine) { + $lexer = new Lexer\Emulative([ + 'usedAttributes' => [ + 'comments', + 'startLine', 'endLine', + 'startTokenPos', 'endTokenPos', + ], + ]); + + $parser = new Parser\Php7($lexer); + $traverser = new NodeTraverser(); + $traverser->addVisitor(new NodeVisitor\CloningVisitor()); + + $printer = new PrettyPrinter\Standard(); + + $oldStmts = $parser->parse($code); + $oldTokens = $lexer->getTokens(); + + $newStmts = $traverser->traverse($oldStmts); + + /** @var callable $fn */ + eval(<<printFormatPreserving($newStmts, $oldStmts, $oldTokens); + $this->assertSame(canonicalize($expected), canonicalize($newCode), $name); + } + + public function provideTestFormatPreservingPrint() { + return $this->getTests(__DIR__ . '/../code/formatPreservation', 'test', 3); + } + + /** + * @dataProvider provideTestRoundTripPrint + * @covers \PhpParser\PrettyPrinter\Standard + */ + public function testRoundTripPrint($name, $code, $expected, $modeLine) { + /** + * This test makes sure that the format-preserving pretty printer round-trips for all + * the pretty printer tests (i.e. returns the input if no changes occurred). + */ + + list($version) = $this->parseModeLine($modeLine); + + $lexer = new Lexer\Emulative([ + 'usedAttributes' => [ + 'comments', + 'startLine', 'endLine', + 'startTokenPos', 'endTokenPos', + ], + ]); + + $parserClass = $version === 'php5' ? Parser\Php5::class : Parser\Php7::class; + /** @var Parser $parser */ + $parser = new $parserClass($lexer); + + $traverser = new NodeTraverser(); + $traverser->addVisitor(new NodeVisitor\CloningVisitor()); + + $printer = new PrettyPrinter\Standard(); + + try { + $oldStmts = $parser->parse($code); + } catch (Error $e) { + // Can't do a format-preserving print on a file with errors + return; + } + + $oldTokens = $lexer->getTokens(); + + $newStmts = $traverser->traverse($oldStmts); + + $newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens); + $this->assertSame(canonicalize($code), canonicalize($newCode), $name); + } + + public function provideTestRoundTripPrint() { + return array_merge( + $this->getTests(__DIR__ . '/../code/prettyPrinter', 'test'), + $this->getTests(__DIR__ . '/../code/parser', 'test') + ); + } +} diff --git a/vendor/nikic/php-parser/test/bootstrap.php b/vendor/nikic/php-parser/test/bootstrap.php new file mode 100644 index 0000000..0bfa9d0 --- /dev/null +++ b/vendor/nikic/php-parser/test/bootstrap.php @@ -0,0 +1,31 @@ +getPathname(); + yield $fileName => file_get_contents($fileName); + } +} diff --git a/vendor/nikic/php-parser/test/code/formatPreservation/addingPropertyType.test b/vendor/nikic/php-parser/test/code/formatPreservation/addingPropertyType.test new file mode 100644 index 0000000..2ef332b --- /dev/null +++ b/vendor/nikic/php-parser/test/code/formatPreservation/addingPropertyType.test @@ -0,0 +1,39 @@ +Adding property type +----- +stmts[0]->type = new Node\Identifier('string'); +----- +stmts[0]->type = new Node\Identifier('int'); +----- +expr; +$new->class->extends = null; +$new->args[] = new Expr\Variable('y'); +----- +expr; +$new->class->name = new Node\Identifier('Anon1'); +----- +expr->expr; +$array->items[] = new Expr\ArrayItem(new Expr\Variable('b')); +----- +expr->expr; +$array->items[] = new Expr\ArrayItem(new Expr\Variable('c'), null, false, [], true); +----- + +$a; +----- +$stmts[0]->expr->expr = new Expr\Variable('b'); +----- + +$b; +----- + $a; +----- +$stmts[0]->expr->params[] = new Node\Param(new Expr\Variable('b')); +----- + $a; +----- + +$a; +----- +// TODO: Format preserving currently not supported +$stmts[0]->expr->params = []; +----- + $a; +----- + $a; +----- +$stmts[0]->expr->returnType = new Node\Identifier('bool'); +----- + $a; +----- + $a; +----- +$stmts[0]->expr->returnType = null; +----- + $a; +----- + $a; + +static fn($a) +: int +=> $a; +----- +// TODO: Format preserving currently not supported +$stmts[0]->expr->static = true; +$stmts[1]->expr->static = false; +----- + $a; + +fn($a): int => $a; +----- + $a; + +fn&($a) +: int +=> $a; +----- +// TODO: Format preserving currently not supported +$stmts[0]->expr->byRef = true; +$stmts[1]->expr->byRef = false; +----- + $a; + +fn($a): int => $a; diff --git a/vendor/nikic/php-parser/test/code/formatPreservation/basic.test b/vendor/nikic/php-parser/test/code/formatPreservation/basic.test new file mode 100644 index 0000000..48db780 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/formatPreservation/basic.test @@ -0,0 +1,190 @@ +abc1 +----- +exprs[0]->left->right->value = 42; +----- +name = new Node\Identifier('bar'); +----- +byRef = true; +----- +byRef = true; +----- +stmts[0]; +$stmts[0]->stmts[0] = $stmts[1]; +$stmts[1] = $tmp; +----- +stmts[0]; +$stmts[1]->stmts[0] = $stmts[2]; +$stmts[2] = $tmp; +// Same test, but also removing first statement, triggering fallback +array_splice($stmts, 0, 1, []); +----- +exprs[0] = new Expr\ConstFetch(new Node\Name('C')); +----- +exprs[0]->parts[0] = new Expr\Variable('bar'); +$stmts[1]->exprs[0]->parts[0] = new Expr\Variable('bar'); +----- +stmts[] = new Stmt\Expression(new Expr\Variable('c')); +----- +stmts[] = new Stmt\Expression(new Expr\Variable('c')); +----- +setAttribute('comments', []); +----- +getComments(); +$comments[] = new Comment("// foo"); +$stmts[1]->setAttribute('comments', $comments); +----- +stmts[0]; +$method->setAttribute('comments', [new Comment\Doc("/**\n *\n */")]); +----- +implements[] = new Node\Name('Iface'); +$stmts[0]->implements[] = new Node\Name('Iface2'); +$stmts[1]->extends[] = new Node\Name('Iface'); +$stmts[1]->extends[] = new Node\Name('Iface2'); +----- + 42; +----- +$stmts[0]->params[] = new Node\Param(new Node\Expr\Variable('a')); +$stmts[0]->params[] = new Node\Param(new Node\Expr\Variable('b')); +$stmts[1]->stmts[0]->params[] = new Node\Param(new Node\Expr\Variable('a')); +$stmts[1]->stmts[0]->params[] = new Node\Param(new Node\Expr\Variable('b')); +$stmts[2]->expr->params[] = new Node\Param(new Node\Expr\Variable('a')); +$stmts[2]->expr->params[] = new Node\Param(new Node\Expr\Variable('b')); +$stmts[2]->expr->uses[] = new Node\Expr\Variable('c'); +$stmts[2]->expr->uses[] = new Node\Expr\Variable('d'); +$stmts[3]->expr->params[] = new Node\Param(new Node\Expr\Variable('a')); +$stmts[3]->expr->params[] = new Node\Param(new Node\Expr\Variable('b')); +----- + 42; +----- + +bar(); + +Foo +::bar (); + +new +Foo +(); + +new class +() +extends Foo {}; +----- +$stmts[0]->expr->args[] = new Node\Expr\Variable('a'); +$stmts[0]->expr->args[] = new Node\Expr\Variable('b'); +$stmts[1]->expr->args[] = new Node\Expr\Variable('a'); +$stmts[1]->expr->args[] = new Node\Expr\Variable('b'); +$stmts[2]->expr->args[] = new Node\Expr\Variable('a'); +$stmts[2]->expr->args[] = new Node\Expr\Variable('b'); +$stmts[3]->expr->args[] = new Node\Expr\Variable('a'); +$stmts[3]->expr->args[] = new Node\Expr\Variable('b'); +$stmts[4]->expr->args[] = new Node\Expr\Variable('a'); +$stmts[4]->expr->args[] = new Node\Expr\Variable('b'); +----- + +bar($a, $b); + +Foo +::bar ($a, $b); + +new +Foo +($a, $b); + +new class +($a, $b) +extends Foo {}; \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/formatPreservation/fixup.test b/vendor/nikic/php-parser/test/code/formatPreservation/fixup.test new file mode 100644 index 0000000..e8870ae --- /dev/null +++ b/vendor/nikic/php-parser/test/code/formatPreservation/fixup.test @@ -0,0 +1,67 @@ +Fixup for precedence and some special syntax +----- +expr->left = new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')); +// The parens here are "correct", because add is left assoc +$stmts[1]->expr->right = new Expr\BinaryOp\Plus(new Expr\Variable('b'), new Expr\Variable('c')); +// No parens necessary +$stmts[2]->expr->left = new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')); +// Parens for RHS not strictly necessary due to assign speciality +$stmts[3]->expr->cond = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[3]->expr->if = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[3]->expr->else = new Expr\Assign(new Expr\Variable('a'), new Expr\Variable('b')); +// Already has parens +$stmts[4]->expr->left = new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[5]->expr->left = new Expr\BinaryOp\Plus(new Expr\Variable('a'), new Expr\Variable('b')); +----- + bar; +$foo -> bar; +$foo -> bar; +$foo -> bar; +$foo -> bar; +self :: $foo; +self :: $foo; +----- +$stmts[0]->expr->name = new Expr\Variable('a'); +$stmts[1]->expr->name = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[2]->expr->var = new Expr\Variable('bar'); +$stmts[3]->expr->var = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[4]->expr->name = new Node\Identifier('foo'); +// In this case the braces are not strictly necessary. However, on PHP 5 they may be required +// depending on where the property fetch node itself occurs. +$stmts[5]->expr->name = new Expr\Variable('bar'); +$stmts[6]->expr->name = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b')); +$stmts[7]->expr->name = new Node\VarLikeIdentifier('bar'); +$stmts[8]->expr->name = new Expr\BinaryOp\Concat(new Expr\Variable('a'), new Expr\Variable('b')); +----- + bar; +($a . $b) -> bar; +$foo -> foo; +$foo -> {$bar}; +$foo -> {$a . $b}; +self :: $bar; +self :: ${$a . $b}; \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/formatPreservation/inlineHtml.test b/vendor/nikic/php-parser/test/code/formatPreservation/inlineHtml.test new file mode 100644 index 0000000..7494e53 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/formatPreservation/inlineHtml.test @@ -0,0 +1,54 @@ +Handling of inline HTML +----- +FoosetAttribute('origNode', null); +----- +FooBarstmts[2] = $stmts[0]->stmts[1]; +----- +BarBarstmts[1] = $stmts[0]->stmts[2]; +----- +returnType = new Node\Name('Foo'); +$stmts[0]->params[0]->type = new Node\Identifier('int'); +$stmts[0]->params[1]->type = new Node\Identifier('array'); +$stmts[0]->params[1]->default = new Expr\ConstFetch(new Node\Name('null')); +$stmts[1]->expr->dim = new Expr\Variable('a'); +$stmts[2]->expr->items[0]->key = new Scalar\String_('X'); +$stmts[3]->expr->returnType = new Node\Name('Bar'); +$stmts[4]->expr->if = new Expr\Variable('z'); +$stmts[5]->expr->key = new Expr\Variable('k'); +$stmts[6]->expr->value = new Expr\Variable('v'); +$stmts[7]->num = new Scalar\LNumber(2); +$stmts[8]->num = new Scalar\LNumber(2); +$stmts[9]->expr = new Expr\Variable('x'); +$stmts[10]->extends = new Node\Name\FullyQualified('Bar'); +$stmts[10]->stmts[0]->returnType = new Node\Name('Y'); +$stmts[10]->stmts[1]->props[0]->default = new Scalar\DNumber(42.0); +$stmts[11]->keyVar = new Expr\Variable('z'); +$stmts[12]->vars[0]->default = new Scalar\String_('abc'); +$stmts[13]->finally = new Stmt\Finally_([]); +$stmts[14]->else = new Stmt\Else_([]); +----- + $value +]; + +function +() : Bar +{}; + +$x +? $z +: +$y; + +yield +$k => $v ; +yield $v ; + +break 2 +; +continue 2 +; +return $x +; + +class +X extends \Bar +{ + public + function y() : Y + {} + + private + $x = 42.0 + ; +} + +foreach ( + $x + as + $z => $y +) {} + +static +$var = 'abc' +; + +try { +} catch (X +$y) { +} finally { +} + +if ($cond) { // Foo +} elseif ($cond2) { // Bar +} else { +} +----- +name = new Node\Name('Foo'); +----- +stmts[] = new Stmt\Expression(new Expr\Variable('baz')); +----- +params[] = new Node\Param(new Expr\Variable('param2')); +----- +catches[0]->types[] = new Node\Name('Bar'); +----- +params, new Node\Param(new Expr\Variable('param0'))); +----- +params[] = new Node\Param(new Expr\Variable('param0')); +----- +elseifs[] = new Stmt\ElseIf_(new Expr\Variable('cond3'), []); +----- +catches[] = new Stmt\Catch_([new Node\Name('Bar')], new Expr\Variable('bar'), []); +----- +setAttribute('comments', [new Comment('// Test')]); +$stmts[] = $node; +----- +setAttribute('comments', [new Comment('// Test'), new Comment('// Test 2')]); +$stmts[0]->stmts[] = $node; +----- +name->parts[0] = 'Xyz'; +----- +stmts, $node); +----- +setAttribute('comments', [new Comment('// Test')]); +array_unshift($stmts[0]->stmts, $node); +----- +setAttribute('comments', [new Comment('// Test')]); +array_unshift($stmts[0]->stmts, $node); +----- +setAttribute('comments', [new Comment('// Test')]); +array_unshift($stmts[0]->stmts, $node); +$stmts[0]->stmts[1]->setAttribute('comments', [new Comment('// Bar foo')]); +----- +setAttribute('comments', [new Comment('// Test')]); +array_unshift($stmts[0]->stmts, $node); +$stmts[0]->stmts[1]->setAttribute('comments', []); +----- +stmts, + new Stmt\Expression(new Expr\Variable('a')), + new Stmt\Expression(new Expr\Variable('b'))); +----- +stmts = [ + new Stmt\Expression(new Expr\Variable('a')), + new Stmt\Expression(new Expr\Variable('b')), +]; +----- +expr->expr->items, new Expr\ArrayItem(new Scalar\LNumber(42))); +$stmts[0]->expr->expr->items[] = new Expr\ArrayItem(new Scalar\LNumber(24)); +----- +expr->expr->items[] = new Expr\ArrayItem(new Scalar\LNumber(24)); +----- +foo = new Foo; +$this->foo->a() + ->b(); +----- +$outerCall = $stmts[1]->expr; +$innerCall = $outerCall->var; +$var = $innerCall->var; +$stmts[1]->expr = $innerCall; +$stmts[2] = new Stmt\Expression(new Expr\MethodCall($var, $outerCall->name)); +----- +foo = new Foo; +$this->foo->a(); +$this->foo->b(); \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/formatPreservation/listRemoval.test b/vendor/nikic/php-parser/test/code/formatPreservation/listRemoval.test new file mode 100644 index 0000000..0ac4239 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/formatPreservation/listRemoval.test @@ -0,0 +1,41 @@ +Removing from list nodes +----- +params); +----- +params); +$stmts[0]->params[] = new Node\Param(new Expr\Variable('x')); +$stmts[0]->params[] = new Node\Param(new Expr\Variable('y')); +----- +flags = Stmt\Class_::MODIFIER_ABSTRACT; +$stmts[1]->flags = 0; +$stmts[1]->stmts[0]->flags = Stmt\Class_::MODIFIER_PRIVATE; +$stmts[1]->stmts[1]->flags = Stmt\Class_::MODIFIER_PROTECTED; +$stmts[1]->stmts[2]->flags |= Stmt\Class_::MODIFIER_FINAL; +----- + [new Comment('//Some comment here')]]); +----- + $b +, $c => $d]; + +yield +$foo +=> +$bar; +yield +$bar; + +break +2 +; +continue +2 +; + +foreach( + $array +as + $key + => + $value +) {} + +if +($x) +{ +} + +else {} + +return +$val +; +static + $x + = + $y +; + +try {} catch + (X $y) + {} +finally +{} +----- +$stmts[0]->returnType = null; +$stmts[0]->params[0]->default = null; +$stmts[0]->params[1]->type = null; +$stmts[1]->expr->returnType = null; +$stmts[2]->extends = null; +$stmts[2]->stmts[0]->returnType = null; +$stmts[2]->stmts[1]->props[0]->default = null; +$stmts[2]->stmts[2]->adaptations[0]->newName = null; +$stmts[3]->expr->dim = null; +$stmts[4]->expr->expr = null; +$stmts[5]->expr->if = null; +$stmts[6]->expr->items[1]->key = null; +$stmts[7]->expr->key = null; +$stmts[8]->expr->value = null; +$stmts[9]->num = null; +$stmts[10]->num = null; +$stmts[11]->keyVar = null; +$stmts[12]->else = null; +$stmts[13]->expr = null; +$stmts[14]->vars[0]->default = null; +$stmts[15]->finally = null; +----- + $b +, $d]; + +yield +$bar; +yield; + +break; +continue; + +foreach( + $array +as + $value +) {} + +if +($x) +{ +} + +return; +static + $x +; + +try {} catch + (X $y) + {} +----- +name = null; +----- +stmts[0]->type = null; +----- + +----- +array( + 0: Stmt_Nop( + comments: array( + 0: /** doc */ + 1: /* foobar */ + 2: // foo + 3: // bar + ) + ) +) +----- + +; +----- +!!positions +Syntax error, unexpected ';', expecting T_STRING or T_VARIABLE or '{' or '$' from 3:1 to 3:1 +array( + 0: Stmt_Expression[2:1 - 3:1]( + expr: Expr_PropertyFetch[2:1 - 2:6]( + var: Expr_Variable[2:1 - 2:4]( + name: foo + ) + name: Expr_Error[3:1 - 2:6]( + ) + ) + ) +) +----- + +} +----- +!!positions +Syntax error, unexpected '}', expecting T_STRING or T_VARIABLE or '{' or '$' from 4:1 to 4:1 +array( + 0: Stmt_Function[2:1 - 4:1]( + byRef: false + name: Identifier[2:10 - 2:12]( + name: foo + ) + params: array( + ) + returnType: null + stmts: array( + 0: Stmt_Expression[3:5 - 3:10]( + expr: Expr_PropertyFetch[3:5 - 3:10]( + var: Expr_Variable[3:5 - 3:8]( + name: bar + ) + name: Expr_Error[4:1 - 3:10]( + ) + ) + ) + ) + ) +) +----- +value $oopsAnotherValue->get() +]; +$array = [ + $value $oopsAnotherValue +]; +$array = [ + 'key' => $value $oopsAnotherValue +]; +----- +!!php7 +Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 3:18 to 3:34 +Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 6:12 to 6:28 +Syntax error, unexpected T_VARIABLE, expecting ',' or ')' or ']' from 9:21 to 9:37 +array( + 0: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: array + ) + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_PropertyFetch( + var: Expr_Variable( + name: this + ) + name: Identifier( + name: value + ) + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_MethodCall( + var: Expr_Variable( + name: oopsAnotherValue + ) + name: Identifier( + name: get + ) + args: array( + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: array + ) + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: value + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: oopsAnotherValue + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: array + ) + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: key + ) + value: Expr_Variable( + name: value + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: oopsAnotherValue + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) +) +----- + 'd', 'e' => &$f); + +// short array syntax +[]; +[1, 2, 3]; +['a' => 'b']; +----- +array( + 0: Stmt_Expression( + expr: Expr_Array( + items: array( + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: a + ) + byRef: false + unpack: false + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: a + ) + byRef: false + unpack: false + ) + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: a + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Scalar_String( + value: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: a + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: true + unpack: false + ) + 2: Expr_ArrayItem( + key: Scalar_String( + value: c + ) + value: Scalar_String( + value: d + ) + byRef: false + unpack: false + ) + 3: Expr_ArrayItem( + key: Scalar_String( + value: e + ) + value: Expr_Variable( + name: f + ) + byRef: true + unpack: false + ) + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_Array( + items: array( + ) + comments: array( + 0: // short array syntax + ) + ) + comments: array( + 0: // short array syntax + ) + ) + 6: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 1 + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 2 + ) + byRef: false + unpack: false + ) + 2: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 3 + ) + byRef: false + unpack: false + ) + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Scalar_String( + value: b + ) + byRef: false + unpack: false + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/arrayDestructuring.test b/vendor/nikic/php-parser/test/code/parser/expr/arrayDestructuring.test new file mode 100644 index 0000000..cfec0d1 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/arrayDestructuring.test @@ -0,0 +1,164 @@ +Array destructuring +----- + $b, 'b' => $a] = $baz; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: c + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: d + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: null + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 2: null + 3: null + 4: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + 5: null + ) + ) + expr: Expr_Variable( + name: foo + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: null + 1: Expr_ArrayItem( + key: null + value: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + 2: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: bar + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: Scalar_String( + value: b + ) + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: baz + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/arraySpread.test b/vendor/nikic/php-parser/test/code/parser/expr/arraySpread.test new file mode 100644 index 0000000..ed2edce --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/arraySpread.test @@ -0,0 +1,432 @@ +Spread array +----- + $a; +fn($x = 42) => $x; +static fn(&$x) => $x; +fn&($x) => $x; +fn($x, ...$rest) => $rest; +fn(): int => $x; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_ArrowFunction( + static: false + byRef: false + params: array( + 0: Param( + type: Identifier( + name: bool + ) + byRef: false + variadic: false + var: Expr_Variable( + name: a + ) + default: null + ) + ) + returnType: null + expr: Expr_Variable( + name: a + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_ArrowFunction( + static: false + byRef: false + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: x + ) + default: Scalar_LNumber( + value: 42 + ) + ) + ) + returnType: null + expr: Expr_Variable( + name: x + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_ArrowFunction( + static: true + byRef: false + params: array( + 0: Param( + type: null + byRef: true + variadic: false + var: Expr_Variable( + name: x + ) + default: null + ) + ) + returnType: null + expr: Expr_Variable( + name: x + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_ArrowFunction( + static: false + byRef: true + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: x + ) + default: null + ) + ) + returnType: null + expr: Expr_Variable( + name: x + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_ArrowFunction( + static: false + byRef: false + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: x + ) + default: null + ) + 1: Param( + type: null + byRef: false + variadic: true + var: Expr_Variable( + name: rest + ) + default: null + ) + ) + returnType: null + expr: Expr_Variable( + name: rest + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_ArrowFunction( + static: false + byRef: false + params: array( + ) + returnType: Identifier( + name: int + ) + expr: Expr_Variable( + name: x + ) + ) + ) +) diff --git a/vendor/nikic/php-parser/test/code/parser/expr/assign.test b/vendor/nikic/php-parser/test/code/parser/expr/assign.test new file mode 100644 index 0000000..423f48a --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/assign.test @@ -0,0 +1,381 @@ +Assignments +----- +>= $b; +$a **= $b; +$a ??= $b; + +// chained assign +$a = $b *= $c **= $d; + +// by ref assign +$a =& $b; + +// list() assign +list($a) = $b; +list($a, , $b) = $c; +list($a, list(, $c), $d) = $e; + +// inc/dec +++$a; +$a++; +--$a; +$a--; +----- +array( + 0: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: a + comments: array( + 0: // simple assign + ) + ) + expr: Expr_Variable( + name: b + ) + comments: array( + 0: // simple assign + ) + ) + comments: array( + 0: // simple assign + ) + ) + 1: Stmt_Expression( + expr: Expr_AssignOp_BitwiseAnd( + var: Expr_Variable( + name: a + comments: array( + 0: // combined assign + ) + ) + expr: Expr_Variable( + name: b + ) + comments: array( + 0: // combined assign + ) + ) + comments: array( + 0: // combined assign + ) + ) + 2: Stmt_Expression( + expr: Expr_AssignOp_BitwiseOr( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_AssignOp_BitwiseXor( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_AssignOp_Concat( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_AssignOp_Div( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_AssignOp_Minus( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_AssignOp_Mod( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_AssignOp_Mul( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 9: Stmt_Expression( + expr: Expr_AssignOp_Plus( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 10: Stmt_Expression( + expr: Expr_AssignOp_ShiftLeft( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 11: Stmt_Expression( + expr: Expr_AssignOp_ShiftRight( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 12: Stmt_Expression( + expr: Expr_AssignOp_Pow( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 13: Stmt_Expression( + expr: Expr_AssignOp_Coalesce( + var: Expr_Variable( + name: a + ) + expr: Expr_Variable( + name: b + ) + ) + ) + 14: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: a + comments: array( + 0: // chained assign + ) + ) + expr: Expr_AssignOp_Mul( + var: Expr_Variable( + name: b + ) + expr: Expr_AssignOp_Pow( + var: Expr_Variable( + name: c + ) + expr: Expr_Variable( + name: d + ) + ) + ) + comments: array( + 0: // chained assign + ) + ) + comments: array( + 0: // chained assign + ) + ) + 15: Stmt_Expression( + expr: Expr_AssignRef( + var: Expr_Variable( + name: a + comments: array( + 0: // by ref assign + ) + ) + expr: Expr_Variable( + name: b + ) + comments: array( + 0: // by ref assign + ) + ) + comments: array( + 0: // by ref assign + ) + ) + 16: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + ) + comments: array( + 0: // list() assign + ) + ) + expr: Expr_Variable( + name: b + ) + comments: array( + 0: // list() assign + ) + ) + comments: array( + 0: // list() assign + ) + ) + 17: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: null + 2: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: c + ) + ) + ) + 18: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_List( + items: array( + 0: null + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: c + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + 2: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: d + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: e + ) + ) + ) + 19: Stmt_Expression( + expr: Expr_PreInc( + var: Expr_Variable( + name: a + ) + comments: array( + 0: // inc/dec + ) + ) + comments: array( + 0: // inc/dec + ) + ) + 20: Stmt_Expression( + expr: Expr_PostInc( + var: Expr_Variable( + name: a + ) + ) + ) + 21: Stmt_Expression( + expr: Expr_PreDec( + var: Expr_Variable( + name: a + ) + ) + ) + 22: Stmt_Expression( + expr: Expr_PostDec( + var: Expr_Variable( + name: a + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/assignNewByRef.test b/vendor/nikic/php-parser/test/code/parser/expr/assignNewByRef.test new file mode 100644 index 0000000..a66d943 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/assignNewByRef.test @@ -0,0 +1,43 @@ +Assigning new by reference (PHP 5 only) +----- + $b; +$a >= $b; +$a == $b; +$a === $b; +$a != $b; +$a !== $b; +$a <=> $b; +$a instanceof B; +$a instanceof $b; +----- +array( + 0: Stmt_Expression( + expr: Expr_BinaryOp_Smaller( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_BinaryOp_SmallerOrEqual( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_BinaryOp_Greater( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_BinaryOp_GreaterOrEqual( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_BinaryOp_Equal( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_BinaryOp_Identical( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_BinaryOp_NotEqual( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_BinaryOp_NotIdentical( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_BinaryOp_Spaceship( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 9: Stmt_Expression( + expr: Expr_Instanceof( + expr: Expr_Variable( + name: a + ) + class: Name( + parts: array( + 0: B + ) + ) + ) + ) + 10: Stmt_Expression( + expr: Expr_Instanceof( + expr: Expr_Variable( + name: a + ) + class: Expr_Variable( + name: b + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/constant_expr.test b/vendor/nikic/php-parser/test/code/parser/expr/constant_expr.test new file mode 100644 index 0000000..0f9815f --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/constant_expr.test @@ -0,0 +1,694 @@ +Expressions in static scalar context +----- + 0; +const T_20 = 1 >= 0; +const T_21 = 1 === 1; +const T_22 = 1 !== 1; +const T_23 = 0 != "0"; +const T_24 = 1 == "1"; +const T_25 = 1 + 2 * 3; +const T_26 = "1" + 2 + "3"; +const T_27 = 2 ** 3; +const T_28 = [1, 2, 3][1]; +const T_29 = 12 - 13; +const T_30 = 12 ^ 13; +const T_31 = 12 & 13; +const T_32 = 12 | 13; +const T_33 = 12 % 3; +const T_34 = 100 >> 4; +const T_35 = !false; +----- +array( + 0: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_1 + ) + value: Expr_BinaryOp_ShiftLeft( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 1: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_2 + ) + value: Expr_BinaryOp_Div( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 2 + ) + ) + ) + ) + ) + 2: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_3 + ) + value: Expr_BinaryOp_Plus( + left: Scalar_DNumber( + value: 1.5 + ) + right: Scalar_DNumber( + value: 1.5 + ) + ) + ) + ) + ) + 3: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_4 + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: foo + ) + right: Scalar_String( + value: bar + ) + ) + ) + ) + ) + 4: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_5 + ) + value: Expr_BinaryOp_Mul( + left: Expr_BinaryOp_Plus( + left: Scalar_DNumber( + value: 1.5 + ) + right: Scalar_DNumber( + value: 1.5 + ) + ) + right: Scalar_LNumber( + value: 2 + ) + ) + ) + ) + ) + 5: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_6 + ) + value: Expr_BinaryOp_Concat( + left: Expr_BinaryOp_Concat( + left: Expr_BinaryOp_Concat( + left: Scalar_String( + value: foo + ) + right: Scalar_LNumber( + value: 2 + ) + ) + right: Scalar_LNumber( + value: 3 + ) + ) + right: Scalar_DNumber( + value: 4 + ) + ) + ) + ) + ) + 6: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_7 + ) + value: Scalar_MagicConst_Line( + ) + ) + ) + ) + 7: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_8 + ) + value: Scalar_String( + value: This is a test string + ) + ) + ) + ) + 8: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_9 + ) + value: Expr_BitwiseNot( + expr: Expr_UnaryMinus( + expr: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + ) + 9: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_10 + ) + value: Expr_BinaryOp_Plus( + left: Expr_Ternary( + cond: Expr_UnaryMinus( + expr: Scalar_LNumber( + value: 1 + ) + ) + if: null + else: Scalar_LNumber( + value: 1 + ) + ) + right: Expr_Ternary( + cond: Scalar_LNumber( + value: 0 + ) + if: Scalar_LNumber( + value: 2 + ) + else: Scalar_LNumber( + value: 3 + ) + ) + ) + ) + ) + ) + 10: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_11 + ) + value: Expr_BinaryOp_BooleanAnd( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 11: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_12 + ) + value: Expr_BinaryOp_LogicalAnd( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 12: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_13 + ) + value: Expr_BinaryOp_BooleanOr( + left: Scalar_LNumber( + value: 0 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 13: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_14 + ) + value: Expr_BinaryOp_LogicalOr( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 14: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_15 + ) + value: Expr_BinaryOp_LogicalXor( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 15: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_16 + ) + value: Expr_BinaryOp_LogicalXor( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 16: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_17 + ) + value: Expr_BinaryOp_Smaller( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 17: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_18 + ) + value: Expr_BinaryOp_SmallerOrEqual( + left: Scalar_LNumber( + value: 0 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 18: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_19 + ) + value: Expr_BinaryOp_Greater( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 19: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_20 + ) + value: Expr_BinaryOp_GreaterOrEqual( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 20: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_21 + ) + value: Expr_BinaryOp_Identical( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 21: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_22 + ) + value: Expr_BinaryOp_NotIdentical( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 22: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_23 + ) + value: Expr_BinaryOp_NotEqual( + left: Scalar_LNumber( + value: 0 + ) + right: Scalar_String( + value: 0 + ) + ) + ) + ) + ) + 23: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_24 + ) + value: Expr_BinaryOp_Equal( + left: Scalar_LNumber( + value: 1 + ) + right: Scalar_String( + value: 1 + ) + ) + ) + ) + ) + 24: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_25 + ) + value: Expr_BinaryOp_Plus( + left: Scalar_LNumber( + value: 1 + ) + right: Expr_BinaryOp_Mul( + left: Scalar_LNumber( + value: 2 + ) + right: Scalar_LNumber( + value: 3 + ) + ) + ) + ) + ) + ) + 25: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_26 + ) + value: Expr_BinaryOp_Plus( + left: Expr_BinaryOp_Plus( + left: Scalar_String( + value: 1 + ) + right: Scalar_LNumber( + value: 2 + ) + ) + right: Scalar_String( + value: 3 + ) + ) + ) + ) + ) + 26: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_27 + ) + value: Expr_BinaryOp_Pow( + left: Scalar_LNumber( + value: 2 + ) + right: Scalar_LNumber( + value: 3 + ) + ) + ) + ) + ) + 27: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_28 + ) + value: Expr_ArrayDimFetch( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 1 + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 2 + ) + byRef: false + unpack: false + ) + 2: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 3 + ) + byRef: false + unpack: false + ) + ) + ) + dim: Scalar_LNumber( + value: 1 + ) + ) + ) + ) + ) + 28: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_29 + ) + value: Expr_BinaryOp_Minus( + left: Scalar_LNumber( + value: 12 + ) + right: Scalar_LNumber( + value: 13 + ) + ) + ) + ) + ) + 29: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_30 + ) + value: Expr_BinaryOp_BitwiseXor( + left: Scalar_LNumber( + value: 12 + ) + right: Scalar_LNumber( + value: 13 + ) + ) + ) + ) + ) + 30: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_31 + ) + value: Expr_BinaryOp_BitwiseAnd( + left: Scalar_LNumber( + value: 12 + ) + right: Scalar_LNumber( + value: 13 + ) + ) + ) + ) + ) + 31: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_32 + ) + value: Expr_BinaryOp_BitwiseOr( + left: Scalar_LNumber( + value: 12 + ) + right: Scalar_LNumber( + value: 13 + ) + ) + ) + ) + ) + 32: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_33 + ) + value: Expr_BinaryOp_Mod( + left: Scalar_LNumber( + value: 12 + ) + right: Scalar_LNumber( + value: 3 + ) + ) + ) + ) + ) + 33: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_34 + ) + value: Expr_BinaryOp_ShiftRight( + left: Scalar_LNumber( + value: 100 + ) + right: Scalar_LNumber( + value: 4 + ) + ) + ) + ) + ) + 34: Stmt_Const( + consts: array( + 0: Const( + name: Identifier( + name: T_35 + ) + value: Expr_BooleanNot( + expr: Expr_ConstFetch( + name: Name( + parts: array( + 0: false + ) + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/errorSuppress.test b/vendor/nikic/php-parser/test/code/parser/expr/errorSuppress.test new file mode 100644 index 0000000..7f09998 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/errorSuppress.test @@ -0,0 +1,14 @@ +Error suppression +----- +b['c'](); + +// array dereferencing +a()['b']; +----- +array( + 0: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: a + ) + comments: array( + 0: // function name variations + ) + ) + args: array( + ) + comments: array( + 0: // function name variations + ) + ) + comments: array( + 0: // function name variations + ) + ) + 1: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_Variable( + name: a + ) + args: array( + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_Variable( + name: Scalar_String( + value: a + ) + ) + args: array( + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_Variable( + name: Expr_Variable( + name: a + ) + ) + args: array( + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_Variable( + name: Expr_Variable( + name: Expr_Variable( + name: a + ) + ) + ) + args: array( + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_ArrayDimFetch( + var: Expr_Variable( + name: a + ) + dim: Scalar_String( + value: b + ) + ) + args: array( + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_ArrayDimFetch( + var: Expr_Variable( + name: a + ) + dim: Scalar_String( + value: b + ) + ) + args: array( + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + args: array( + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_FuncCall( + name: Name( + parts: array( + 0: a + ) + comments: array( + 0: // array dereferencing + ) + ) + args: array( + ) + comments: array( + 0: // array dereferencing + ) + ) + dim: Scalar_String( + value: b + ) + comments: array( + 0: // array dereferencing + ) + ) + comments: array( + 0: // array dereferencing + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/newDeref.test b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/newDeref.test new file mode 100644 index 0000000..a4b7a72 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/newDeref.test @@ -0,0 +1,82 @@ +New expression dereferencing +----- +b; +(new A)->b(); +(new A)['b']; +(new A)['b']['c']; +----- +array( + 0: Stmt_Expression( + expr: Expr_PropertyFetch( + var: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + name: Identifier( + name: b + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + name: Identifier( + name: b + ) + args: array( + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + dim: Scalar_String( + value: b + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_ArrayDimFetch( + var: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + dim: Scalar_String( + value: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/objectAccess.test b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/objectAccess.test new file mode 100644 index 0000000..2d1808b --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/objectAccess.test @@ -0,0 +1,184 @@ +Object access +----- +b; +$a->b['c']; +$a->b{'c'}; + +// method call variations +$a->b(); +$a->{'b'}(); +$a->$b(); +$a->$b['c'](); + +// array dereferencing +$a->b()['c']; +$a->b(){'c'}; // invalid PHP: drop Support? +----- +!!php5 +array( + 0: Stmt_Expression( + expr: Expr_PropertyFetch( + var: Expr_Variable( + name: a + comments: array( + 0: // property fetch variations + ) + ) + name: Identifier( + name: b + ) + comments: array( + 0: // property fetch variations + ) + ) + comments: array( + 0: // property fetch variations + ) + ) + 1: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: a + comments: array( + 0: // method call variations + ) + ) + name: Identifier( + name: b + ) + args: array( + ) + comments: array( + 0: // method call variations + ) + ) + comments: array( + 0: // method call variations + ) + ) + 4: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: a + ) + name: Scalar_String( + value: b + ) + args: array( + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: a + ) + name: Expr_Variable( + name: b + ) + args: array( + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: a + ) + name: Expr_ArrayDimFetch( + var: Expr_Variable( + name: b + ) + dim: Scalar_String( + value: c + ) + ) + args: array( + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_MethodCall( + var: Expr_Variable( + name: a + comments: array( + 0: // array dereferencing + ) + ) + name: Identifier( + name: b + ) + args: array( + ) + comments: array( + 0: // array dereferencing + ) + ) + dim: Scalar_String( + value: c + ) + comments: array( + 0: // array dereferencing + ) + ) + comments: array( + 0: // array dereferencing + ) + ) + 8: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_MethodCall( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + args: array( + ) + ) + dim: Scalar_String( + value: c + ) + ) + ) + 9: Stmt_Nop( + comments: array( + 0: // invalid PHP: drop Support? + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test new file mode 100644 index 0000000..133771b --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/fetchAndCall/simpleArrayAccess.test @@ -0,0 +1,72 @@ +Simple array access +----- + &$v) = $x; +[&$v] = $x; +['k' => &$v] = $x; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: v + ) + byRef: true + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: x + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: k + ) + value: Expr_Variable( + name: v + ) + byRef: true + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: x + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: v + ) + byRef: true + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: x + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: k + ) + value: Expr_Variable( + name: v + ) + byRef: true + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: x + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/listWithKeys.test b/vendor/nikic/php-parser/test/code/parser/expr/listWithKeys.test new file mode 100644 index 0000000..e2eeedf --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/listWithKeys.test @@ -0,0 +1,84 @@ +List destructing with keys +----- + $b) = ['a' => 'b']; +list('a' => list($b => $c), 'd' => $e) = $x; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Scalar_String( + value: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Assign( + var: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: Expr_Variable( + name: b + ) + value: Expr_Variable( + name: c + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: Scalar_String( + value: d + ) + value: Expr_Variable( + name: e + ) + byRef: false + unpack: false + ) + ) + ) + expr: Expr_Variable( + name: x + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/logic.test b/vendor/nikic/php-parser/test/code/parser/expr/logic.test new file mode 100644 index 0000000..6b43456 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/logic.test @@ -0,0 +1,190 @@ +Logical operators +----- +> $b; +$a ** $b; + +// associativity +$a * $b * $c; +$a * ($b * $c); + +// precedence +$a + $b * $c; +($a + $b) * $c; + +// pow is special +$a ** $b ** $c; +($a ** $b) ** $c; +----- +array( + 0: Stmt_Expression( + expr: Expr_BitwiseNot( + expr: Expr_Variable( + name: a + ) + comments: array( + 0: // unary ops + ) + ) + comments: array( + 0: // unary ops + ) + ) + 1: Stmt_Expression( + expr: Expr_UnaryPlus( + expr: Expr_Variable( + name: a + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_UnaryMinus( + expr: Expr_Variable( + name: a + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_BinaryOp_BitwiseAnd( + left: Expr_Variable( + name: a + comments: array( + 0: // binary ops + ) + ) + right: Expr_Variable( + name: b + ) + comments: array( + 0: // binary ops + ) + ) + comments: array( + 0: // binary ops + ) + ) + 4: Stmt_Expression( + expr: Expr_BinaryOp_BitwiseOr( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_BinaryOp_BitwiseXor( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_BinaryOp_Concat( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_BinaryOp_Div( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_BinaryOp_Minus( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 9: Stmt_Expression( + expr: Expr_BinaryOp_Mod( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 10: Stmt_Expression( + expr: Expr_BinaryOp_Mul( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 11: Stmt_Expression( + expr: Expr_BinaryOp_Plus( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 12: Stmt_Expression( + expr: Expr_BinaryOp_ShiftLeft( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 13: Stmt_Expression( + expr: Expr_BinaryOp_ShiftRight( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 14: Stmt_Expression( + expr: Expr_BinaryOp_Pow( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + ) + 15: Stmt_Expression( + expr: Expr_BinaryOp_Mul( + left: Expr_BinaryOp_Mul( + left: Expr_Variable( + name: a + comments: array( + 0: // associativity + ) + ) + right: Expr_Variable( + name: b + ) + comments: array( + 0: // associativity + ) + ) + right: Expr_Variable( + name: c + ) + comments: array( + 0: // associativity + ) + ) + comments: array( + 0: // associativity + ) + ) + 16: Stmt_Expression( + expr: Expr_BinaryOp_Mul( + left: Expr_Variable( + name: a + ) + right: Expr_BinaryOp_Mul( + left: Expr_Variable( + name: b + ) + right: Expr_Variable( + name: c + ) + ) + ) + ) + 17: Stmt_Expression( + expr: Expr_BinaryOp_Plus( + left: Expr_Variable( + name: a + comments: array( + 0: // precedence + ) + ) + right: Expr_BinaryOp_Mul( + left: Expr_Variable( + name: b + ) + right: Expr_Variable( + name: c + ) + ) + comments: array( + 0: // precedence + ) + ) + comments: array( + 0: // precedence + ) + ) + 18: Stmt_Expression( + expr: Expr_BinaryOp_Mul( + left: Expr_BinaryOp_Plus( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + right: Expr_Variable( + name: c + ) + ) + ) + 19: Stmt_Expression( + expr: Expr_BinaryOp_Pow( + left: Expr_Variable( + name: a + comments: array( + 0: // pow is special + ) + ) + right: Expr_BinaryOp_Pow( + left: Expr_Variable( + name: b + ) + right: Expr_Variable( + name: c + ) + ) + comments: array( + 0: // pow is special + ) + ) + comments: array( + 0: // pow is special + ) + ) + 20: Stmt_Expression( + expr: Expr_BinaryOp_Pow( + left: Expr_BinaryOp_Pow( + left: Expr_Variable( + name: a + ) + right: Expr_Variable( + name: b + ) + ) + right: Expr_Variable( + name: c + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/new.test b/vendor/nikic/php-parser/test/code/parser/expr/new.test new file mode 100644 index 0000000..2735bfe --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/new.test @@ -0,0 +1,187 @@ +New +----- +b(); +new $a->b->c(); +new $a->b['c'](); +new $a->b{'c'}(); + +// test regression introduces by new dereferencing syntax +(new A); +----- +array( + 0: Stmt_Expression( + expr: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_New( + class: Expr_Variable( + name: a + ) + args: array( + ) + comments: array( + 0: // class name variations + ) + ) + comments: array( + 0: // class name variations + ) + ) + 3: Stmt_Expression( + expr: Expr_New( + class: Expr_ArrayDimFetch( + var: Expr_Variable( + name: a + ) + dim: Scalar_String( + value: b + ) + ) + args: array( + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_New( + class: Expr_StaticPropertyFetch( + class: Name( + parts: array( + 0: A + ) + ) + name: VarLikeIdentifier( + name: b + ) + ) + args: array( + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_New( + class: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + args: array( + ) + comments: array( + 0: // DNCR object access + ) + ) + comments: array( + 0: // DNCR object access + ) + ) + 6: Stmt_Expression( + expr: Expr_New( + class: Expr_PropertyFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + name: Identifier( + name: c + ) + ) + args: array( + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_New( + class: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + args: array( + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_New( + class: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Variable( + name: a + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_String( + value: c + ) + ) + args: array( + ) + ) + ) + 9: Stmt_Expression( + expr: Expr_New( + class: Name( + parts: array( + 0: A + ) + ) + args: array( + ) + ) + comments: array( + 0: // test regression introduces by new dereferencing syntax + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/newWithoutClass.test b/vendor/nikic/php-parser/test/code/parser/expr/newWithoutClass.test new file mode 100644 index 0000000..318f930 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/newWithoutClass.test @@ -0,0 +1,25 @@ +New without a class +----- +bar($a, $b, ); +Foo::bar($a, $b, ); +new Foo($a, $b, ); +unset($a, $b, ); +isset($a, $b, ); +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: foo + ) + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Arg( + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: foo + ) + name: Identifier( + name: bar + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Arg( + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_StaticCall( + class: Name( + parts: array( + 0: Foo + ) + ) + name: Identifier( + name: bar + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Arg( + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_New( + class: Name( + parts: array( + 0: Foo + ) + ) + args: array( + 0: Arg( + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Arg( + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + ) + 4: Stmt_Unset( + vars: array( + 0: Expr_Variable( + name: a + ) + 1: Expr_Variable( + name: b + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_Isset( + vars: array( + 0: Expr_Variable( + name: a + ) + 1: Expr_Variable( + name: b + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/uvs/globalNonSimpleVarError.test b/vendor/nikic/php-parser/test/code/parser/expr/uvs/globalNonSimpleVarError.test new file mode 100644 index 0000000..5ae4f95 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/uvs/globalNonSimpleVarError.test @@ -0,0 +1,27 @@ +Non-simple variables are forbidden in PHP 7 +----- +bar; +----- +!!php7 +Syntax error, unexpected T_OBJECT_OPERATOR, expecting ';' from 2:13 to 2:14 +array( + 0: Stmt_Global( + vars: array( + 0: Expr_Variable( + name: Expr_Variable( + name: foo + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_ConstFetch( + name: Name( + parts: array( + 0: bar + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/uvs/indirectCall.test b/vendor/nikic/php-parser/test/code/parser/expr/uvs/indirectCall.test new file mode 100644 index 0000000..39db60c --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/uvs/indirectCall.test @@ -0,0 +1,511 @@ +UVS indirect calls +----- + 'b']->a); +isset("str"->a); +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_Isset( + vars: array( + 0: Expr_ArrayDimFetch( + var: Expr_BinaryOp_Plus( + left: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 0 + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 1 + ) + byRef: false + unpack: false + ) + ) + ) + right: Expr_Array( + items: array( + ) + ) + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Isset( + vars: array( + 0: Expr_PropertyFetch( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Scalar_String( + value: a + ) + value: Scalar_String( + value: b + ) + byRef: false + unpack: false + ) + ) + ) + name: Identifier( + name: a + ) + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Isset( + vars: array( + 0: Expr_PropertyFetch( + var: Scalar_String( + value: str + ) + name: Identifier( + name: a + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/uvs/misc.test b/vendor/nikic/php-parser/test/code/parser/expr/uvs/misc.test new file mode 100644 index 0000000..801eb37 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/uvs/misc.test @@ -0,0 +1,129 @@ +Uniform variable syntax in PHP 7 (misc) +----- +length(); +(clone $obj)->b[0](1); +[0, 1][0] = 1; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_ClassConstFetch( + class: Name( + parts: array( + 0: A + ) + ) + name: Identifier( + name: A + ) + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_ArrayDimFetch( + var: Expr_ArrayDimFetch( + var: Expr_ArrayDimFetch( + var: Expr_ClassConstFetch( + class: Name( + parts: array( + 0: A + ) + ) + name: Identifier( + name: A + ) + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + dim: Scalar_LNumber( + value: 1 + ) + ) + dim: Scalar_LNumber( + value: 2 + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_MethodCall( + var: Scalar_String( + value: string + ) + name: Identifier( + name: length + ) + args: array( + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_FuncCall( + name: Expr_ArrayDimFetch( + var: Expr_PropertyFetch( + var: Expr_Clone( + expr: Expr_Variable( + name: obj + ) + ) + name: Identifier( + name: b + ) + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + args: array( + 0: Arg( + value: Scalar_LNumber( + value: 1 + ) + byRef: false + unpack: false + ) + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_Assign( + var: Expr_ArrayDimFetch( + var: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 0 + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Scalar_LNumber( + value: 1 + ) + byRef: false + unpack: false + ) + ) + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + expr: Scalar_LNumber( + value: 1 + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/uvs/new.test b/vendor/nikic/php-parser/test/code/parser/expr/uvs/new.test new file mode 100644 index 0000000..5e1caf2 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/uvs/new.test @@ -0,0 +1,119 @@ +UVS new expressions +----- +className; +new Test::$className; +new $test::$className; +new $weird[0]->foo::$className; +----- +!!php7 +array( + 0: Stmt_Expression( + expr: Expr_New( + class: Expr_Variable( + name: className + ) + args: array( + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_New( + class: Expr_ArrayDimFetch( + var: Expr_Variable( + name: array + ) + dim: Scalar_String( + value: className + ) + ) + args: array( + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_New( + class: Expr_ArrayDimFetch( + var: Expr_Variable( + name: array + ) + dim: Scalar_String( + value: className + ) + ) + args: array( + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_New( + class: Expr_PropertyFetch( + var: Expr_Variable( + name: obj + ) + name: Identifier( + name: className + ) + ) + args: array( + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_New( + class: Expr_StaticPropertyFetch( + class: Name( + parts: array( + 0: Test + ) + ) + name: VarLikeIdentifier( + name: className + ) + ) + args: array( + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_New( + class: Expr_StaticPropertyFetch( + class: Expr_Variable( + name: test + ) + name: VarLikeIdentifier( + name: className + ) + ) + args: array( + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_New( + class: Expr_StaticPropertyFetch( + class: Expr_PropertyFetch( + var: Expr_ArrayDimFetch( + var: Expr_Variable( + name: weird + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + name: Identifier( + name: foo + ) + ) + name: VarLikeIdentifier( + name: className + ) + ) + args: array( + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/expr/uvs/staticProperty.test b/vendor/nikic/php-parser/test/code/parser/expr/uvs/staticProperty.test new file mode 100644 index 0000000..bf3547c --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/expr/uvs/staticProperty.test @@ -0,0 +1,123 @@ +UVS static access +----- +c test +EOS; + +b<<B"; +"$A[B]"; +"$A[0]"; +"$A[1234]"; +"$A[9223372036854775808]"; +"$A[000]"; +"$A[0x0]"; +"$A[0b0]"; +"$A[$B]"; +"{$A}"; +"{$A['B']}"; +"${A}"; +"${A['B']}"; +"${$A}"; +"\{$A}"; +"\{ $A }"; +"\\{$A}"; +"\\{ $A }"; +"{$$A}[B]"; +"$$A[B]"; +"A $B C"; +b"$A"; +B"$A"; +----- +array( + 0: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: A + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_PropertyFetch( + var: Expr_Variable( + name: A + ) + name: Identifier( + name: B + ) + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: B + ) + ) + ) + ) + ) + 3: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_LNumber( + value: 0 + ) + ) + ) + ) + ) + 4: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_LNumber( + value: 1234 + ) + ) + ) + ) + ) + 5: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: 9223372036854775808 + ) + ) + ) + ) + ) + 6: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: 000 + ) + ) + ) + ) + ) + 7: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: 0x0 + ) + ) + ) + ) + ) + 8: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: 0b0 + ) + ) + ) + ) + ) + 9: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Expr_Variable( + name: B + ) + ) + ) + ) + ) + 10: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: A + ) + ) + ) + ) + 11: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: B + ) + ) + ) + ) + ) + 12: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: A + ) + ) + ) + ) + 13: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: B + ) + ) + ) + ) + ) + 14: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: Expr_Variable( + name: A + ) + ) + ) + ) + ) + 15: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: \{ + ) + 1: Expr_Variable( + name: A + ) + 2: Scalar_EncapsedStringPart( + value: } + ) + ) + ) + ) + 16: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: \{ + ) + 1: Expr_Variable( + name: A + ) + 2: Scalar_EncapsedStringPart( + value: } + ) + ) + ) + ) + 17: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: \ + ) + 1: Expr_Variable( + name: A + ) + ) + ) + ) + 18: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: \{ + ) + 1: Expr_Variable( + name: A + ) + 2: Scalar_EncapsedStringPart( + value: } + ) + ) + ) + ) + 19: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: Expr_Variable( + name: A + ) + ) + 1: Scalar_EncapsedStringPart( + value: [B] + ) + ) + ) + ) + 20: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: $ + ) + 1: Expr_ArrayDimFetch( + var: Expr_Variable( + name: A + ) + dim: Scalar_String( + value: B + ) + ) + ) + ) + ) + 21: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Scalar_EncapsedStringPart( + value: A + ) + 1: Expr_Variable( + name: B + ) + 2: Scalar_EncapsedStringPart( + value: C + ) + ) + ) + ) + 22: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: A + ) + ) + ) + ) + 23: Stmt_Expression( + expr: Scalar_Encapsed( + parts: array( + 0: Expr_Variable( + name: A + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/scalar/flexibleDocString.test b/vendor/nikic/php-parser/test/code/parser/scalar/flexibleDocString.test new file mode 100644 index 0000000..17d728d --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/scalar/flexibleDocString.test @@ -0,0 +1,361 @@ +Flexible heredoc/nowdoc (PHP 7.3) +----- + float overflows +// (all are actually the same number, just in different representations) +18446744073709551615; +0xFFFFFFFFFFFFFFFF; +01777777777777777777777; +0177777777777777777777787; +0b1111111111111111111111111111111111111111111111111111111111111111; +----- +array( + 0: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 1: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 2: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 3: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 4: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 5: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 6: Stmt_Expression( + expr: Scalar_DNumber( + value: 0 + ) + ) + 7: Stmt_Expression( + expr: Scalar_DNumber( + value: 302000000000 + ) + ) + 8: Stmt_Expression( + expr: Scalar_DNumber( + value: 3.002E+102 + ) + ) + 9: Stmt_Expression( + expr: Scalar_DNumber( + value: INF + ) + ) + 10: Stmt_Expression( + expr: Scalar_DNumber( + value: 1.844674407371E+19 + comments: array( + 0: // various integer -> float overflows + 1: // (all are actually the same number, just in different representations) + ) + ) + comments: array( + 0: // various integer -> float overflows + 1: // (all are actually the same number, just in different representations) + ) + ) + 11: Stmt_Expression( + expr: Scalar_DNumber( + value: 1.844674407371E+19 + ) + ) + 12: Stmt_Expression( + expr: Scalar_DNumber( + value: 1.844674407371E+19 + ) + ) + 13: Stmt_Expression( + expr: Scalar_DNumber( + value: 1.844674407371E+19 + ) + ) + 14: Stmt_Expression( + expr: Scalar_DNumber( + value: 1.844674407371E+19 + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/scalar/int.test b/vendor/nikic/php-parser/test/code/parser/scalar/int.test new file mode 100644 index 0000000..b65858d --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/scalar/int.test @@ -0,0 +1,61 @@ +Different integer syntaxes +----- +array(); +$t->public(); + +Test::list(); +Test::protected(); + +$t->class; +$t->private; + +Test::TRAIT; +Test::FINAL; + +class Foo { + use TraitA, TraitB { + TraitA::catch insteadof namespace\TraitB; + TraitA::list as foreach; + TraitB::throw as protected public; + TraitB::self as protected; + exit as die; + \TraitC::exit as bye; + namespace\TraitC::exit as byebye; + TraitA:: + // + /** doc comment */ + # + catch /* comment */ + // comment + # comment + insteadof TraitB; + } +} +----- +array( + 0: Stmt_Class( + flags: 0 + name: Identifier( + name: Test + ) + extends: null + implements: array( + ) + stmts: array( + 0: Stmt_ClassMethod( + flags: 0 + byRef: false + name: Identifier( + name: array + ) + params: array( + ) + returnType: null + stmts: array( + ) + ) + 1: Stmt_ClassMethod( + flags: 0 + byRef: false + name: Identifier( + name: public + ) + params: array( + ) + returnType: null + stmts: array( + ) + ) + 2: Stmt_ClassMethod( + flags: MODIFIER_STATIC (8) + byRef: false + name: Identifier( + name: list + ) + params: array( + ) + returnType: null + stmts: array( + ) + ) + 3: Stmt_ClassMethod( + flags: MODIFIER_STATIC (8) + byRef: false + name: Identifier( + name: protected + ) + params: array( + ) + returnType: null + stmts: array( + ) + ) + 4: Stmt_Property( + flags: MODIFIER_PUBLIC (1) + type: null + props: array( + 0: Stmt_PropertyProperty( + name: VarLikeIdentifier( + name: class + ) + default: null + ) + ) + ) + 5: Stmt_Property( + flags: MODIFIER_PUBLIC (1) + type: null + props: array( + 0: Stmt_PropertyProperty( + name: VarLikeIdentifier( + name: private + ) + default: null + ) + ) + ) + 6: Stmt_ClassConst( + flags: 0 + consts: array( + 0: Const( + name: Identifier( + name: TRAIT + ) + value: Scalar_LNumber( + value: 3 + ) + ) + 1: Const( + name: Identifier( + name: FINAL + ) + value: Scalar_LNumber( + value: 4 + ) + ) + ) + ) + 7: Stmt_ClassConst( + flags: 0 + consts: array( + 0: Const( + name: Identifier( + name: __CLASS__ + ) + value: Scalar_LNumber( + value: 1 + ) + ) + 1: Const( + name: Identifier( + name: __TRAIT__ + ) + value: Scalar_LNumber( + value: 2 + ) + ) + 2: Const( + name: Identifier( + name: __FUNCTION__ + ) + value: Scalar_LNumber( + value: 3 + ) + ) + 3: Const( + name: Identifier( + name: __METHOD__ + ) + value: Scalar_LNumber( + value: 4 + ) + ) + 4: Const( + name: Identifier( + name: __LINE__ + ) + value: Scalar_LNumber( + value: 5 + ) + ) + 5: Const( + name: Identifier( + name: __FILE__ + ) + value: Scalar_LNumber( + value: 6 + ) + ) + 6: Const( + name: Identifier( + name: __DIR__ + ) + value: Scalar_LNumber( + value: 7 + ) + ) + 7: Const( + name: Identifier( + name: __NAMESPACE__ + ) + value: Scalar_LNumber( + value: 8 + ) + ) + ) + ) + 8: Stmt_Nop( + comments: array( + 0: // __halt_compiler does not work + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: t + ) + expr: Expr_New( + class: Name( + parts: array( + 0: Test + ) + ) + args: array( + ) + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: t + ) + name: Identifier( + name: array + ) + args: array( + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: t + ) + name: Identifier( + name: public + ) + args: array( + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_StaticCall( + class: Name( + parts: array( + 0: Test + ) + ) + name: Identifier( + name: list + ) + args: array( + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_StaticCall( + class: Name( + parts: array( + 0: Test + ) + ) + name: Identifier( + name: protected + ) + args: array( + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_PropertyFetch( + var: Expr_Variable( + name: t + ) + name: Identifier( + name: class + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_PropertyFetch( + var: Expr_Variable( + name: t + ) + name: Identifier( + name: private + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_ClassConstFetch( + class: Name( + parts: array( + 0: Test + ) + ) + name: Identifier( + name: TRAIT + ) + ) + ) + 9: Stmt_Expression( + expr: Expr_ClassConstFetch( + class: Name( + parts: array( + 0: Test + ) + ) + name: Identifier( + name: FINAL + ) + ) + ) + 10: Stmt_Class( + flags: 0 + name: Identifier( + name: Foo + ) + extends: null + implements: array( + ) + stmts: array( + 0: Stmt_TraitUse( + traits: array( + 0: Name( + parts: array( + 0: TraitA + ) + ) + 1: Name( + parts: array( + 0: TraitB + ) + ) + ) + adaptations: array( + 0: Stmt_TraitUseAdaptation_Precedence( + trait: Name( + parts: array( + 0: TraitA + ) + ) + method: Identifier( + name: catch + ) + insteadof: array( + 0: Name_Relative( + parts: array( + 0: TraitB + ) + ) + ) + ) + 1: Stmt_TraitUseAdaptation_Alias( + trait: Name( + parts: array( + 0: TraitA + ) + ) + method: Identifier( + name: list + ) + newModifier: null + newName: Identifier( + name: foreach + ) + ) + 2: Stmt_TraitUseAdaptation_Alias( + trait: Name( + parts: array( + 0: TraitB + ) + ) + method: Identifier( + name: throw + ) + newModifier: MODIFIER_PROTECTED (2) + newName: Identifier( + name: public + ) + ) + 3: Stmt_TraitUseAdaptation_Alias( + trait: Name( + parts: array( + 0: TraitB + ) + ) + method: Identifier( + name: self + ) + newModifier: MODIFIER_PROTECTED (2) + newName: null + ) + 4: Stmt_TraitUseAdaptation_Alias( + trait: null + method: Identifier( + name: exit + ) + newModifier: null + newName: Identifier( + name: die + ) + ) + 5: Stmt_TraitUseAdaptation_Alias( + trait: Name_FullyQualified( + parts: array( + 0: TraitC + ) + ) + method: Identifier( + name: exit + ) + newModifier: null + newName: Identifier( + name: bye + ) + ) + 6: Stmt_TraitUseAdaptation_Alias( + trait: Name_Relative( + parts: array( + 0: TraitC + ) + ) + method: Identifier( + name: exit + ) + newModifier: null + newName: Identifier( + name: byebye + ) + ) + 7: Stmt_TraitUseAdaptation_Precedence( + trait: Name( + parts: array( + 0: TraitA + ) + ) + method: Identifier( + name: catch + comments: array( + 0: // + 1: /** doc comment */ + 2: # + ) + ) + insteadof: array( + 0: Name( + parts: array( + 0: TraitB + ) + ) + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/blocklessStatement.test b/vendor/nikic/php-parser/test/code/parser/stmt/blocklessStatement.test new file mode 100644 index 0000000..abf5864 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/blocklessStatement.test @@ -0,0 +1,130 @@ +Blockless statements for if/for/etc +----- + 'baz'] +) {} +----- +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: a + ) + params: array( + 0: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: b + ) + default: Expr_ConstFetch( + name: Name( + parts: array( + 0: null + ) + ) + ) + ) + 1: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: c + ) + default: Scalar_String( + value: foo + ) + ) + 2: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: d + ) + default: Expr_ClassConstFetch( + class: Name( + parts: array( + 0: A + ) + ) + name: Identifier( + name: B + ) + ) + ) + 3: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: f + ) + default: Expr_UnaryPlus( + expr: Scalar_LNumber( + value: 1 + ) + ) + ) + 4: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: g + ) + default: Expr_UnaryMinus( + expr: Scalar_DNumber( + value: 1 + ) + ) + ) + 5: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: h + ) + default: Expr_Array( + items: array( + ) + ) + ) + 6: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: i + ) + default: Expr_Array( + items: array( + ) + ) + ) + 7: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: j + ) + default: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: foo + ) + byRef: false + unpack: false + ) + ) + ) + ) + 8: Param( + type: null + byRef: false + variadic: false + var: Expr_Variable( + name: k + ) + default: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Scalar_String( + value: foo + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: Scalar_String( + value: bar + ) + value: Scalar_String( + value: baz + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + returnType: null + stmts: array( + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/function/nullableTypes.test b/vendor/nikic/php-parser/test/code/parser/stmt/function/nullableTypes.test new file mode 100644 index 0000000..8bf2d31 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/function/nullableTypes.test @@ -0,0 +1,55 @@ +Nullable types +----- + $value; + + // expressions + $data = yield; + $data = (yield $value); + $data = (yield $key => $value); + + // yield in language constructs with their own parentheses + if (yield $foo); elseif (yield $foo); + if (yield $foo): elseif (yield $foo): endif; + while (yield $foo); + do {} while (yield $foo); + switch (yield $foo) {} + die(yield $foo); + + // yield in function calls + func(yield $foo); + $foo->func(yield $foo); + new Foo(yield $foo); + + yield from $foo; + yield from $foo and yield from $bar; + yield from $foo + $bar; +} +----- +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: gen + ) + params: array( + ) + returnType: null + stmts: array( + 0: Stmt_Expression( + expr: Expr_Yield( + key: null + value: null + comments: array( + 0: // statements + ) + ) + comments: array( + 0: // statements + ) + ) + 1: Stmt_Expression( + expr: Expr_Yield( + key: null + value: Expr_Variable( + name: value + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Yield( + key: Expr_Variable( + name: key + ) + value: Expr_Variable( + name: value + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: data + comments: array( + 0: // expressions + ) + ) + expr: Expr_Yield( + key: null + value: null + ) + comments: array( + 0: // expressions + ) + ) + comments: array( + 0: // expressions + ) + ) + 4: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: data + ) + expr: Expr_Yield( + key: null + value: Expr_Variable( + name: value + ) + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_Assign( + var: Expr_Variable( + name: data + ) + expr: Expr_Yield( + key: Expr_Variable( + name: key + ) + value: Expr_Variable( + name: value + ) + ) + ) + ) + 6: Stmt_If( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + stmts: array( + ) + elseifs: array( + 0: Stmt_ElseIf( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + stmts: array( + ) + ) + ) + else: null + comments: array( + 0: // yield in language constructs with their own parentheses + ) + ) + 7: Stmt_If( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + stmts: array( + ) + elseifs: array( + 0: Stmt_ElseIf( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + stmts: array( + ) + ) + ) + else: null + ) + 8: Stmt_While( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + stmts: array( + ) + ) + 9: Stmt_Do( + stmts: array( + ) + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + ) + 10: Stmt_Switch( + cond: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + cases: array( + ) + ) + 11: Stmt_Expression( + expr: Expr_Exit( + expr: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + ) + ) + 12: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: func + ) + comments: array( + 0: // yield in function calls + ) + ) + args: array( + 0: Arg( + value: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + byRef: false + unpack: false + ) + ) + comments: array( + 0: // yield in function calls + ) + ) + comments: array( + 0: // yield in function calls + ) + ) + 13: Stmt_Expression( + expr: Expr_MethodCall( + var: Expr_Variable( + name: foo + ) + name: Identifier( + name: func + ) + args: array( + 0: Arg( + value: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + 14: Stmt_Expression( + expr: Expr_New( + class: Name( + parts: array( + 0: Foo + ) + ) + args: array( + 0: Arg( + value: Expr_Yield( + key: null + value: Expr_Variable( + name: foo + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + 15: Stmt_Expression( + expr: Expr_YieldFrom( + expr: Expr_Variable( + name: foo + ) + ) + ) + 16: Stmt_Expression( + expr: Expr_BinaryOp_LogicalAnd( + left: Expr_YieldFrom( + expr: Expr_Variable( + name: foo + ) + ) + right: Expr_YieldFrom( + expr: Expr_Variable( + name: bar + ) + ) + ) + ) + 17: Stmt_Expression( + expr: Expr_YieldFrom( + expr: Expr_BinaryOp_Plus( + left: Expr_Variable( + name: foo + ) + right: Expr_Variable( + name: bar + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldPrecedence.test b/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldPrecedence.test new file mode 100644 index 0000000..ce2b637 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldPrecedence.test @@ -0,0 +1,253 @@ +Yield operator precedence +----- + "a" . "b"; + yield "k" => "a" or die; + var_dump([yield "k" => "a" . "b"]); + yield yield "k1" => yield "k2" => "a" . "b"; + yield yield "k1" => (yield "k2") => "a" . "b"; + var_dump([yield "k1" => yield "k2" => "a" . "b"]); + var_dump([yield "k1" => (yield "k2") => "a" . "b"]); +} +----- +!!php7 +array( + 0: Stmt_Function( + byRef: false + name: Identifier( + name: gen + ) + params: array( + ) + returnType: null + stmts: array( + 0: Stmt_Expression( + expr: Expr_Yield( + key: null + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + ) + 1: Stmt_Expression( + expr: Expr_BinaryOp_LogicalOr( + left: Expr_Yield( + key: null + value: Scalar_String( + value: a + ) + ) + right: Expr_Exit( + expr: null + ) + ) + ) + 2: Stmt_Expression( + expr: Expr_Yield( + key: Scalar_String( + value: k + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + ) + 3: Stmt_Expression( + expr: Expr_BinaryOp_LogicalOr( + left: Expr_Yield( + key: Scalar_String( + value: k + ) + value: Scalar_String( + value: a + ) + ) + right: Expr_Exit( + expr: null + ) + ) + ) + 4: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: var_dump + ) + ) + args: array( + 0: Arg( + value: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Yield( + key: Scalar_String( + value: k + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + 5: Stmt_Expression( + expr: Expr_Yield( + key: null + value: Expr_Yield( + key: Scalar_String( + value: k1 + ) + value: Expr_Yield( + key: Scalar_String( + value: k2 + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + ) + ) + ) + 6: Stmt_Expression( + expr: Expr_Yield( + key: Expr_Yield( + key: Scalar_String( + value: k1 + ) + value: Expr_Yield( + key: null + value: Scalar_String( + value: k2 + ) + ) + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + ) + 7: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: var_dump + ) + ) + args: array( + 0: Arg( + value: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Yield( + key: Scalar_String( + value: k1 + ) + value: Expr_Yield( + key: Scalar_String( + value: k2 + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + 8: Stmt_Expression( + expr: Expr_FuncCall( + name: Name( + parts: array( + 0: var_dump + ) + ) + args: array( + 0: Arg( + value: Expr_Array( + items: array( + 0: Expr_ArrayItem( + key: Expr_Yield( + key: Scalar_String( + value: k1 + ) + value: Expr_Yield( + key: null + value: Scalar_String( + value: k2 + ) + ) + ) + value: Expr_BinaryOp_Concat( + left: Scalar_String( + value: a + ) + right: Scalar_String( + value: b + ) + ) + byRef: false + unpack: false + ) + ) + ) + byRef: false + unpack: false + ) + ) + ) + ) + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test b/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test new file mode 100644 index 0000000..6b77d33 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/generator/yieldUnaryPrecedence.test @@ -0,0 +1,56 @@ +Yield with unary operator argument +----- + +Hallo World! +----- +array( + 0: Stmt_Expression( + expr: Expr_Variable( + name: a + ) + ) + 1: Stmt_HaltCompiler( + remaining: Hallo World! + ) +) +----- + +#!/usr/bin/env php +----- +array( + 0: Stmt_InlineHTML( + value: #!/usr/bin/env php + + ) + 1: Stmt_Echo( + exprs: array( + 0: Scalar_String( + value: foobar + ) + ) + ) + 2: Stmt_InlineHTML( + value: #!/usr/bin/env php + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/if.test b/vendor/nikic/php-parser/test/code/parser/stmt/if.test new file mode 100644 index 0000000..e054c89 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/if.test @@ -0,0 +1,103 @@ +If/Elseif/Else +----- + +B + + $c) {} +foreach ($a as $b => &$c) {} +foreach ($a as list($a, $b)) {} +foreach ($a as $a => list($b, , $c)) {} + +// foreach on expression +foreach (array() as $b) {} + +// alternative syntax +foreach ($a as $b): +endforeach; +----- +array( + 0: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: null + byRef: false + valueVar: Expr_Variable( + name: b + ) + stmts: array( + ) + comments: array( + 0: // foreach on variable + ) + ) + 1: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: null + byRef: true + valueVar: Expr_Variable( + name: b + ) + stmts: array( + ) + ) + 2: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: Expr_Variable( + name: b + ) + byRef: false + valueVar: Expr_Variable( + name: c + ) + stmts: array( + ) + ) + 3: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: Expr_Variable( + name: b + ) + byRef: true + valueVar: Expr_Variable( + name: c + ) + stmts: array( + ) + ) + 4: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: null + byRef: false + valueVar: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: a + ) + byRef: false + unpack: false + ) + 1: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + ) + ) + stmts: array( + ) + ) + 5: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: Expr_Variable( + name: a + ) + byRef: false + valueVar: Expr_List( + items: array( + 0: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: b + ) + byRef: false + unpack: false + ) + 1: null + 2: Expr_ArrayItem( + key: null + value: Expr_Variable( + name: c + ) + byRef: false + unpack: false + ) + ) + ) + stmts: array( + ) + ) + 6: Stmt_Foreach( + expr: Expr_Array( + items: array( + ) + ) + keyVar: null + byRef: false + valueVar: Expr_Variable( + name: b + ) + stmts: array( + ) + comments: array( + 0: // foreach on expression + ) + ) + 7: Stmt_Foreach( + expr: Expr_Variable( + name: a + ) + keyVar: null + byRef: false + valueVar: Expr_Variable( + name: b + ) + stmts: array( + ) + comments: array( + 0: // alternative syntax + ) + ) +) \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/parser/stmt/loop/while.test b/vendor/nikic/php-parser/test/code/parser/stmt/loop/while.test new file mode 100644 index 0000000..65f6b23 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/parser/stmt/loop/while.test @@ -0,0 +1,25 @@ +While loop +----- + +Hi! +----- +array( + 0: Stmt_Declare( + declares: array( + 0: Stmt_DeclareDeclare( + key: Identifier( + name: A + ) + value: Scalar_String( + value: B + ) + ) + ) + stmts: null + ) + 1: Stmt_Namespace( + name: Name( + parts: array( + 0: B + ) + ) + stmts: array( + ) + ) + 2: Stmt_HaltCompiler( + remaining: Hi! + ) +) +----- +a = $a; + } +}; +----- +new class +{ +}; +new class extends A implements B, C +{ +}; +new class($a) extends A +{ + private $a; + public function __construct($a) + { + $this->a = $a; + } +}; diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arrayDestructuring.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arrayDestructuring.test new file mode 100644 index 0000000..bff1999 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arrayDestructuring.test @@ -0,0 +1,14 @@ +Array destructuring +----- + $b, 'b' => $a] = $baz; +----- +!!php7 +[$a, $b] = [$c, $d]; +[, $a, , , $b, ] = $foo; +[, [[$a]], $b] = $bar; +['a' => $b, 'b' => $a] = $baz; \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arraySpread.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arraySpread.test new file mode 100644 index 0000000..5e8393f --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/arraySpread.test @@ -0,0 +1,8 @@ +Array spread +----- + $a; +fn($x = 42) => $x; +fn(&$x) => $x; +fn&($x) => $x; +static fn($x, ...$rest) => $rest; +fn(): int => $x; +----- +!!php7 +fn($a) => $a; +fn($x = 42) => $x; +fn(&$x) => $x; +fn&($x) => $x; +static fn($x, ...$rest) => $rest; +fn(): int => $x; diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/call.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/call.test new file mode 100644 index 0000000..0ec8925 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/call.test @@ -0,0 +1,13 @@ +Calls +----- +d} +STR; + +call( + <<d} +STR; +call(<<> $b; +$a < $b; +$a <= $b; +$a > $b; +$a >= $b; +$a == $b; +$a != $b; +$a <> $b; +$a === $b; +$a !== $b; +$a <=> $b; +$a & $b; +$a ^ $b; +$a | $b; +$a && $b; +$a || $b; +$a ? $b : $c; +$a ?: $c; +$a ?? $c; +$a = $b; +$a **= $b; +$a ??= $c; +$a *= $b; +$a /= $b; +$a %= $b; +$a += $b; +$a -= $b; +$a .= $b; +$a <<= $b; +$a >>= $b; +$a &= $b; +$a ^= $b; +$a |= $b; +$a =& $b; + +$a and $b; +$a xor $b; +$a or $b; + +$a instanceof Foo; +$a instanceof $b; +----- +$a ** $b; +++$a; +--$a; +$a++; +$a--; +@$a; +~$a; +-$a; ++$a; +(int) $a; +(int) $a; +(float) $a; +(double) $a; +(real) $a; +(float) $a; +(double) $a; +(real) $a; +(string) $a; +(string) $a; +(array) $a; +(object) $a; +(bool) $a; +(bool) $a; +(unset) $a; +$a * $b; +$a / $b; +$a % $b; +$a + $b; +$a - $b; +$a . $b; +$a << $b; +$a >> $b; +$a < $b; +$a <= $b; +$a > $b; +$a >= $b; +$a == $b; +$a != $b; +$a != $b; +$a === $b; +$a !== $b; +$a <=> $b; +$a & $b; +$a ^ $b; +$a | $b; +$a && $b; +$a || $b; +$a ? $b : $c; +$a ?: $c; +$a ?? $c; +$a = $b; +$a **= $b; +$a ??= $c; +$a *= $b; +$a /= $b; +$a %= $b; +$a += $b; +$a -= $b; +$a .= $b; +$a <<= $b; +$a >>= $b; +$a &= $b; +$a ^= $b; +$a |= $b; +$a =& $b; +$a and $b; +$a xor $b; +$a or $b; +$a instanceof Foo; +$a instanceof $b; diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/parentheses.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/parentheses.test new file mode 100644 index 0000000..a49c110 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/parentheses.test @@ -0,0 +1,86 @@ +Pretty printer generates least-parentheses output +----- + 0) > (1 < 0); +++$a + $b; +$a + $b++; + +$a ** $b ** $c; +($a ** $b) ** $c; +-1 ** 2; + +yield from $a and yield from $b; +yield from ($a and yield from $b); + +print ($a and print $b); + +-(-$a); ++(+$a); +-(--$a); ++(++$a); + +// The following will currently add unnecessary parentheses, because the pretty printer is not aware that assignment +// and incdec only work on variables. +!$a = $b; +++$a ** $b; +$a ** $b++; +----- +echo 'abc' . 'cde' . 'fgh'; +echo 'abc' . ('cde' . 'fgh'); +echo 'abc' . 1 + 2 . 'fgh'; +echo 'abc' . (1 + 2) . 'fgh'; +echo 1 * 2 + 3 / 4 % 5 . 6; +echo 1 * (2 + 3) / (4 % (5 . 6)); +$a = $b = $c = $d = $f && true; +($a = $b = $c = $d = $f) && true; +$a = $b = $c = $d = $f and true; +$a = $b = $c = $d = ($f and true); +$a ? $b : $c ? $d : $e ? $f : $g; +$a ? $b : ($c ? $d : ($e ? $f : $g)); +$a ? $b ? $c : $d : $f; +$a ?? $b ?? $c; +($a ?? $b) ?? $c; +$a ?? ($b ? $c : $d); +$a || ($b ?? $c); +(1 > 0) > (1 < 0); +++$a + $b; +$a + $b++; +$a ** $b ** $c; +($a ** $b) ** $c; +-1 ** 2; +yield from $a and yield from $b; +yield from ($a and yield from $b); +print ($a and print $b); +-(-$a); ++(+$a); +-(--$a); ++(++$a); +// The following will currently add unnecessary parentheses, because the pretty printer is not aware that assignment +// and incdec only work on variables. +!($a = $b); +(++$a) ** $b; +$a ** ($b++); diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/shortArraySyntax.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/shortArraySyntax.test new file mode 100644 index 0000000..082c2e0 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/shortArraySyntax.test @@ -0,0 +1,11 @@ +Short array syntax +----- + 'b', 'c' => 'd']; +----- +[]; +array(1, 2, 3); +['a' => 'b', 'c' => 'd']; \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/stringEscaping.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/stringEscaping.test new file mode 100644 index 0000000..02877ad --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/stringEscaping.test @@ -0,0 +1,23 @@ +Escape sequences in double-quoted strings +----- +b)(); +(A::$b)(); +----- +!!php7 +(function () { +})(); +array('a', 'b')()(); +A::$b::$c; +$A::$b[$c](); +$A::{$b[$c]}(); +A::${$b}[$c](); +($a->b)(); +(A::$b)(); diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/variables.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/variables.test new file mode 100644 index 0000000..4e0fa2e --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/variables.test @@ -0,0 +1,73 @@ +Variables +----- +b; +$a->b(); +$a->b($c); +$a->$b(); +$a->{$b}(); +$a->$b[$c](); +$$a->b; +$a[$b]; +$a[$b](); +$$a[$b]; +$a::B; +$a::$b; +$a::b(); +$a::b($c); +$a::$b(); +$a::$b[$c]; +$a::$b[$c]($d); +$a::{$b[$c]}($d); +$a::{$b->c}(); +A::$$b[$c](); +a(); +$a(); +$a()[$b]; +$a->b()[$c]; +$a::$b()[$c]; +(new A)->b; +(new A())->b(); +(new $$a)[$b]; +(new $a->b)->c; + +global $a, $$a, $$a[$b], $$a->b; +----- +!!php5 +$a; +${$a}; +${$a}; +$a->b; +$a->b(); +$a->b($c); +$a->{$b}(); +$a->{$b}(); +$a->{$b[$c]}(); +${$a}->b; +$a[$b]; +$a[$b](); +${$a[$b]}; +$a::B; +$a::$b; +$a::b(); +$a::b($c); +$a::$b(); +$a::$b[$c]; +$a::{$b[$c]}($d); +$a::{$b[$c]}($d); +$a::{$b->c}(); +A::${$b[$c]}(); +a(); +$a(); +$a()[$b]; +$a->b()[$c]; +$a::$b()[$c]; +(new A())->b; +(new A())->b(); +(new ${$a}())[$b]; +(new $a->b())->c; +global $a, ${$a}, ${$a[$b]}, ${$a->b}; diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/expr/yield.test b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/yield.test new file mode 100644 index 0000000..12ab7de --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/expr/yield.test @@ -0,0 +1,46 @@ +Yield +----- + $b; + $a = yield; + $a = (yield $b); + $a = (yield $b => $c); +} +// TODO Get rid of parens for cases 2 and 3 +----- +function gen() +{ + yield; + (yield $a); + (yield $a => $b); + $a = yield; + $a = (yield $b); + $a = (yield $b => $c); +} +// TODO Get rid of parens for cases 2 and 3 +----- + $c; + yield from $a; + $a = yield from $b; +} +// TODO Get rid of parens for last case +----- +!!php7 +function gen() +{ + $a = (yield $b); + $a = (yield $b => $c); + yield from $a; + $a = (yield from $b); +} +// TODO Get rid of parens for last case \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test b/vendor/nikic/php-parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test new file mode 100644 index 0000000..b33eb52 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/inlineHTMLandPHPtest.file-test @@ -0,0 +1,58 @@ +File containing both inline HTML and PHP +----- +HTML + +HTML +----- + +HTML +----- +HTML + +HTML +----- +HTML + +HTML +----- +HTML + +HTML + +HTML +----- +HTML + +HTML + +HTML +----- +HTMLHTML +----- +HTMLHTML \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/nestedInlineHTML.test b/vendor/nikic/php-parser/test/code/prettyPrinter/nestedInlineHTML.test new file mode 100644 index 0000000..bc611f7 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/nestedInlineHTML.test @@ -0,0 +1,16 @@ +InlineHTML node nested inside other code +----- + +Test + +Test + a = 'bar'; + echo 'test'; + } + + protected function baz() {} + public function foo() {} + abstract static function bar() {} +} + +trait Bar +{ + function test() + { + } +} +----- +class Foo extends Bar implements ABC, \DEF, namespace\GHI +{ + var $a = 'foo'; + private $b = 'bar'; + static $c = 'baz'; + function test() + { + $this->a = 'bar'; + echo 'test'; + } + protected function baz() + { + } + public function foo() + { + } + static abstract function bar() + { + } +} +trait Bar +{ + function test() + { + } +} \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/class_const.test b/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/class_const.test new file mode 100644 index 0000000..e73ad43 --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/class_const.test @@ -0,0 +1,20 @@ +Class constants +----- + $val) { + +} + +foreach ($arr as $key => &$val) { + +} +----- +foreach ($arr as $val) { +} +foreach ($arr as &$val) { +} +foreach ($arr as $key => $val) { +} +foreach ($arr as $key => &$val) { +} \ No newline at end of file diff --git a/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/function_signatures.test b/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/function_signatures.test new file mode 100644 index 0000000..af1088a --- /dev/null +++ b/vendor/nikic/php-parser/test/code/prettyPrinter/stmt/function_signatures.test @@ -0,0 +1,43 @@ +Function signatures +----- + $code) { + if (false !== strpos($code, '@@{')) { + // Skip tests with evaluate segments + continue; + } + + list($name, $tests) = $testParser->parseTest($code, 2); + $newTests = []; + foreach ($tests as list($modeLine, list($input, $expected))) { + $modes = null !== $modeLine ? array_fill_keys(explode(',', $modeLine), true) : []; + list($parser5, $parser7) = $codeParsingTest->createParsers($modes); + list(, $output) = isset($modes['php5']) + ? $codeParsingTest->getParseOutput($parser5, $input, $modes) + : $codeParsingTest->getParseOutput($parser7, $input, $modes); + $newTests[] = [$modeLine, [$input, $output]]; + } + + $newCode = $testParser->reconstructTest($name, $newTests); + file_put_contents($fileName, $newCode); +} diff --git a/vendor/nikic/php-parser/test_old/run-php-src.sh b/vendor/nikic/php-parser/test_old/run-php-src.sh new file mode 100644 index 0000000..d4f3889 --- /dev/null +++ b/vendor/nikic/php-parser/test_old/run-php-src.sh @@ -0,0 +1,4 @@ +wget -q https://github.com/php/php-src/archive/PHP-7.4.tar.gz +mkdir -p ./data/php-src +tar -xzf ./PHP-7.4.tar.gz -C ./data/php-src --strip-components=1 +php -n test_old/run.php --verbose --no-progress PHP7 ./data/php-src diff --git a/vendor/nikic/php-parser/test_old/run.php b/vendor/nikic/php-parser/test_old/run.php new file mode 100644 index 0000000..a5bd00b --- /dev/null +++ b/vendor/nikic/php-parser/test_old/run.php @@ -0,0 +1,273 @@ + [ + 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos', +]]); +$parserName = 'PhpParser\Parser\\' . $version; +/** @var PhpParser\Parser $parser */ +$parser = new $parserName($lexer); +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; +$nodeDumper = new PhpParser\NodeDumper; + +$cloningTraverser = new PhpParser\NodeTraverser; +$cloningTraverser->addVisitor(new PhpParser\NodeVisitor\CloningVisitor); + +$parseFail = $fpppFail = $ppFail = $compareFail = $count = 0; + +$readTime = $parseTime = $cloneTime = 0; +$fpppTime = $ppTime = $reparseTime = $compareTime = 0; +$totalStartTime = microtime(true); + +foreach (new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dir), + RecursiveIteratorIterator::LEAVES_ONLY) + as $file) { + if (!$fileFilter($file)) { + continue; + } + + $startTime = microtime(true); + $origCode = file_get_contents($file); + $readTime += microtime(true) - $startTime; + + if (null === $origCode = $codeExtractor($file, $origCode)) { + continue; + } + + set_time_limit(10); + + ++$count; + + if ($showProgress) { + echo substr(str_pad('Testing file ' . $count . ': ' . substr($file, strlen($dir)), 79), 0, 79), "\r"; + } + + try { + $startTime = microtime(true); + $origStmts = $parser->parse($origCode); + $parseTime += microtime(true) - $startTime; + + $origTokens = $lexer->getTokens(); + + $startTime = microtime(true); + $stmts = $cloningTraverser->traverse($origStmts); + $cloneTime += microtime(true) - $startTime; + + $startTime = microtime(true); + $code = $prettyPrinter->printFormatPreserving($stmts, $origStmts, $origTokens); + $fpppTime += microtime(true) - $startTime; + + if ($code !== $origCode) { + echo $file, ":\n Result of format-preserving pretty-print differs\n"; + if ($verbose) { + echo "FPPP output:\n=====\n$code\n=====\n\n"; + } + + ++$fpppFail; + } + + $startTime = microtime(true); + $code = "prettyPrint($stmts); + $ppTime += microtime(true) - $startTime; + + try { + $startTime = microtime(true); + $ppStmts = $parser->parse($code); + $reparseTime += microtime(true) - $startTime; + + $startTime = microtime(true); + $same = $nodeDumper->dump($stmts) == $nodeDumper->dump($ppStmts); + $compareTime += microtime(true) - $startTime; + + if (!$same) { + echo $file, ":\n Result of initial parse and parse after pretty print differ\n"; + if ($verbose) { + echo "Pretty printer output:\n=====\n$code\n=====\n\n"; + } + + ++$compareFail; + } + } catch (PhpParser\Error $e) { + echo $file, ":\n Parse of pretty print failed with message: {$e->getMessage()}\n"; + if ($verbose) { + echo "Pretty printer output:\n=====\n$code\n=====\n\n"; + } + + ++$ppFail; + } + } catch (PhpParser\Error $e) { + echo $file, ":\n Parse failed with message: {$e->getMessage()}\n"; + + ++$parseFail; + } catch (Throwable $e) { + echo $file, ":\n Unknown error occurred: $e\n"; + } +} + +if (0 === $parseFail && 0 === $ppFail && 0 === $compareFail) { + $exit = 0; + echo "\n\n", 'All tests passed.', "\n"; +} else { + $exit = 1; + echo "\n\n", '==========', "\n\n", 'There were: ', "\n"; + if (0 !== $parseFail) { + echo ' ', $parseFail, ' parse failures.', "\n"; + } + if (0 !== $ppFail) { + echo ' ', $ppFail, ' pretty print failures.', "\n"; + } + if (0 !== $fpppFail) { + echo ' ', $fpppFail, ' FPPP failures.', "\n"; + } + if (0 !== $compareFail) { + echo ' ', $compareFail, ' compare failures.', "\n"; + } +} + +echo "\n", + 'Tested files: ', $count, "\n", + "\n", + 'Reading files took: ', $readTime, "\n", + 'Parsing took: ', $parseTime, "\n", + 'Cloning took: ', $cloneTime, "\n", + 'FPPP took: ', $fpppTime, "\n", + 'Pretty printing took: ', $ppTime, "\n", + 'Reparsing took: ', $reparseTime, "\n", + 'Comparing took: ', $compareTime, "\n", + "\n", + 'Total time: ', microtime(true) - $totalStartTime, "\n", + 'Maximum memory usage: ', memory_get_peak_usage(true), "\n"; + +exit($exit); diff --git a/vendor/symfony/polyfill-php56/LICENSE b/vendor/symfony/polyfill-php56/LICENSE new file mode 100644 index 0000000..4cd8bdd --- /dev/null +++ b/vendor/symfony/polyfill-php56/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +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/symfony/polyfill-php56/Php56.php b/vendor/symfony/polyfill-php56/Php56.php new file mode 100644 index 0000000..dbbc0e1 --- /dev/null +++ b/vendor/symfony/polyfill-php56/Php56.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Php56; + +use Symfony\Polyfill\Util\Binary; + +/** + * @internal + */ +final class Php56 +{ + const LDAP_ESCAPE_FILTER = 1; + const LDAP_ESCAPE_DN = 2; + + public static function hash_equals($knownString, $userInput) + { + if (!\is_string($knownString)) { + trigger_error('Expected known_string to be a string, '.\gettype($knownString).' given', E_USER_WARNING); + + return false; + } + + if (!\is_string($userInput)) { + trigger_error('Expected user_input to be a string, '.\gettype($userInput).' given', E_USER_WARNING); + + return false; + } + + $knownLen = Binary::strlen($knownString); + $userLen = Binary::strlen($userInput); + + if ($knownLen !== $userLen) { + return false; + } + + $result = 0; + + for ($i = 0; $i < $knownLen; ++$i) { + $result |= \ord($knownString[$i]) ^ \ord($userInput[$i]); + } + + return 0 === $result; + } + + /** + * Stub implementation of the {@link ldap_escape()} function of the ldap + * extension. + * + * Escape strings for safe use in LDAP filters and DNs. + * + * @author Chris Wright + * + * @param string $subject + * @param string $ignore + * @param int $flags + * + * @return string + * + * @see http://stackoverflow.com/a/8561604 + */ + public static function ldap_escape($subject, $ignore = '', $flags = 0) + { + static $charMaps = null; + + if (null === $charMaps) { + $charMaps = array( + self::LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"), + self::LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#', "\r"), + ); + + $charMaps[0] = array(); + + for ($i = 0; $i < 256; ++$i) { + $charMaps[0][\chr($i)] = sprintf('\\%02x', $i); + } + + for ($i = 0, $l = \count($charMaps[self::LDAP_ESCAPE_FILTER]); $i < $l; ++$i) { + $chr = $charMaps[self::LDAP_ESCAPE_FILTER][$i]; + unset($charMaps[self::LDAP_ESCAPE_FILTER][$i]); + $charMaps[self::LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr]; + } + + for ($i = 0, $l = \count($charMaps[self::LDAP_ESCAPE_DN]); $i < $l; ++$i) { + $chr = $charMaps[self::LDAP_ESCAPE_DN][$i]; + unset($charMaps[self::LDAP_ESCAPE_DN][$i]); + $charMaps[self::LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr]; + } + } + + // Create the base char map to escape + $flags = (int) $flags; + $charMap = array(); + + if ($flags & self::LDAP_ESCAPE_FILTER) { + $charMap += $charMaps[self::LDAP_ESCAPE_FILTER]; + } + + if ($flags & self::LDAP_ESCAPE_DN) { + $charMap += $charMaps[self::LDAP_ESCAPE_DN]; + } + + if (!$charMap) { + $charMap = $charMaps[0]; + } + + // Remove any chars to ignore from the list + $ignore = (string) $ignore; + + for ($i = 0, $l = \strlen($ignore); $i < $l; ++$i) { + unset($charMap[$ignore[$i]]); + } + + // Do the main replacement + $result = strtr($subject, $charMap); + + // Encode leading/trailing spaces if self::LDAP_ESCAPE_DN is passed + if ($flags & self::LDAP_ESCAPE_DN) { + if (' ' === $result[0]) { + $result = '\\20'.substr($result, 1); + } + + if (' ' === $result[\strlen($result) - 1]) { + $result = substr($result, 0, -1).'\\20'; + } + } + + return $result; + } +} diff --git a/vendor/symfony/polyfill-php56/README.md b/vendor/symfony/polyfill-php56/README.md new file mode 100644 index 0000000..307ce5b --- /dev/null +++ b/vendor/symfony/polyfill-php56/README.md @@ -0,0 +1,15 @@ +Symfony Polyfill / Php56 +======================== + +This component provides functions unavailable in releases prior to PHP 5.6: + +- [`hash_equals`](http://php.net/hash_equals) (part of [hash](http://php.net/hash) extension) +- [`ldap_escape`](http://php.net/ldap_escape) (part of [ldap](http://php.net/ldap) extension) + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-php56/bootstrap.php b/vendor/symfony/polyfill-php56/bootstrap.php new file mode 100644 index 0000000..587c2a8 --- /dev/null +++ b/vendor/symfony/polyfill-php56/bootstrap.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Php56 as p; + +if (PHP_VERSION_ID < 50600) { + if (!function_exists('hash_equals')) { + function hash_equals($knownString, $userInput) { return p\Php56::hash_equals($knownString, $userInput); } + } + if (extension_loaded('ldap') && !function_exists('ldap_escape')) { + define('LDAP_ESCAPE_FILTER', 1); + define('LDAP_ESCAPE_DN', 2); + + function ldap_escape($subject, $ignore = '', $flags = 0) { return p\Php56::ldap_escape($subject, $ignore, $flags); } + } + + if (50509 === PHP_VERSION_ID && 4 === PHP_INT_SIZE) { + // Missing functions in PHP 5.5.9 - affects 32 bit builds of Ubuntu 14.04LTS + // See https://bugs.launchpad.net/ubuntu/+source/php5/+bug/1315888 + if (!function_exists('gzopen') && function_exists('gzopen64')) { + function gzopen($filename, $mode, $use_include_path = 0) { return gzopen64($filename, $mode, $use_include_path); } + } + if (!function_exists('gzseek') && function_exists('gzseek64')) { + function gzseek($zp, $offset, $whence = SEEK_SET) { return gzseek64($zp, $offset, $whence); } + } + if (!function_exists('gztell') && function_exists('gztell64')) { + function gztell($zp) { return gztell64($zp); } + } + } +} diff --git a/vendor/symfony/polyfill-php56/composer.json b/vendor/symfony/polyfill-php56/composer.json new file mode 100644 index 0000000..81f58fb --- /dev/null +++ b/vendor/symfony/polyfill-php56/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/polyfill-php56", + "type": "library", + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php56\\": "" }, + "files": [ "bootstrap.php" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + } +} diff --git a/vendor/symfony/polyfill-util/Binary.php b/vendor/symfony/polyfill-util/Binary.php new file mode 100644 index 0000000..23ff974 --- /dev/null +++ b/vendor/symfony/polyfill-util/Binary.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +if (\extension_loaded('mbstring')) { + class Binary extends BinaryOnFuncOverload + { + } +} else { + class Binary extends BinaryNoFuncOverload + { + } +} diff --git a/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php b/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php new file mode 100644 index 0000000..800ad75 --- /dev/null +++ b/vendor/symfony/polyfill-util/BinaryNoFuncOverload.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class BinaryNoFuncOverload +{ + public static function strlen($s) + { + return \strlen($s); + } + + public static function strpos($haystack, $needle, $offset = 0) + { + return strpos($haystack, $needle, $offset); + } + + public static function strrpos($haystack, $needle, $offset = 0) + { + return strrpos($haystack, $needle, $offset); + } + + public static function substr($string, $start, $length = PHP_INT_MAX) + { + return substr($string, $start, $length); + } + + public static function stripos($s, $needle, $offset = 0) + { + return stripos($s, $needle, $offset); + } + + public static function stristr($s, $needle, $part = false) + { + return stristr($s, $needle, $part); + } + + public static function strrchr($s, $needle, $part = false) + { + return strrchr($s, $needle, $part); + } + + public static function strripos($s, $needle, $offset = 0) + { + return strripos($s, $needle, $offset); + } + + public static function strstr($s, $needle, $part = false) + { + return strstr($s, $needle, $part); + } +} diff --git a/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php b/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php new file mode 100644 index 0000000..e1b886e --- /dev/null +++ b/vendor/symfony/polyfill-util/BinaryOnFuncOverload.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * Binary safe version of string functions overloaded when MB_OVERLOAD_STRING is enabled. + * + * @author Nicolas Grekas + * + * @internal + */ +class BinaryOnFuncOverload +{ + public static function strlen($s) + { + return mb_strlen($s, '8bit'); + } + + public static function strpos($haystack, $needle, $offset = 0) + { + return mb_strpos($haystack, $needle, $offset, '8bit'); + } + + public static function strrpos($haystack, $needle, $offset = 0) + { + return mb_strrpos($haystack, $needle, $offset, '8bit'); + } + + public static function substr($string, $start, $length = 2147483647) + { + return mb_substr($string, $start, $length, '8bit'); + } + + public static function stripos($s, $needle, $offset = 0) + { + return mb_stripos($s, $needle, $offset, '8bit'); + } + + public static function stristr($s, $needle, $part = false) + { + return mb_stristr($s, $needle, $part, '8bit'); + } + + public static function strrchr($s, $needle, $part = false) + { + return mb_strrchr($s, $needle, $part, '8bit'); + } + + public static function strripos($s, $needle, $offset = 0) + { + return mb_strripos($s, $needle, $offset, '8bit'); + } + + public static function strstr($s, $needle, $part = false) + { + return mb_strstr($s, $needle, $part, '8bit'); + } +} diff --git a/vendor/symfony/polyfill-util/LICENSE b/vendor/symfony/polyfill-util/LICENSE new file mode 100644 index 0000000..4cd8bdd --- /dev/null +++ b/vendor/symfony/polyfill-util/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2019 Fabien Potencier + +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/symfony/polyfill-util/README.md b/vendor/symfony/polyfill-util/README.md new file mode 100644 index 0000000..1c655fc --- /dev/null +++ b/vendor/symfony/polyfill-util/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Util +======================= + +This component provides binary-safe string functions, using the +[mbstring](https://php.net/mbstring) extension when available. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-util/TestListener.php b/vendor/symfony/polyfill-util/TestListener.php new file mode 100644 index 0000000..6b17d45 --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListener.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { + class_alias('Symfony\Polyfill\Util\TestListenerForV5', 'Symfony\Polyfill\Util\TestListener'); +// Using an early return instead of a else does not work when using the PHPUnit phar due to some weird PHP behavior (the class +// gets defined without executing the code before it and so the definition is not properly conditional) +} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { + class_alias('Symfony\Polyfill\Util\TestListenerForV6', 'Symfony\Polyfill\Util\TestListener'); +} else { + class_alias('Symfony\Polyfill\Util\TestListenerForV7', 'Symfony\Polyfill\Util\TestListener'); +} + +if (false) { + class TestListener + { + } +} diff --git a/vendor/symfony/polyfill-util/TestListenerForV5.php b/vendor/symfony/polyfill-util/TestListenerForV5.php new file mode 100644 index 0000000..501053d --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListenerForV5.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * @author Nicolas Grekas + */ +class TestListenerForV5 extends \PHPUnit_Framework_TestSuite implements \PHPUnit_Framework_TestListener +{ + private $suite; + private $trait; + + public function __construct(\PHPUnit_Framework_TestSuite $suite = null) + { + if ($suite) { + $this->suite = $suite; + $this->setName($suite->getName().' with polyfills enabled'); + $this->addTest($suite); + } + $this->trait = new TestListenerTrait(); + } + + public function startTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + $this->trait->startTestSuite($suite); + } + + public function addError(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + $this->trait->addError($test, $e, $time); + } + + public function addWarning(\PHPUnit_Framework_Test $test, \PHPUnit_Framework_Warning $e, $time) + { + } + + public function addFailure(\PHPUnit_Framework_Test $test, \PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->trait->addError($test, $e, $time); + } + + public function addIncompleteTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function addRiskyTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function addSkippedTest(\PHPUnit_Framework_Test $test, \Exception $e, $time) + { + } + + public function endTestSuite(\PHPUnit_Framework_TestSuite $suite) + { + } + + public function startTest(\PHPUnit_Framework_Test $test) + { + } + + public function endTest(\PHPUnit_Framework_Test $test, $time) + { + } + + public static function warning($message) + { + return parent::warning($message); + } + + protected function setUp() + { + TestListenerTrait::$enabledPolyfills = $this->suite->getName(); + } + + protected function tearDown() + { + TestListenerTrait::$enabledPolyfills = false; + } +} diff --git a/vendor/symfony/polyfill-util/TestListenerForV6.php b/vendor/symfony/polyfill-util/TestListenerForV6.php new file mode 100644 index 0000000..e9e87cb --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListenerForV6.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener as TestListenerInterface; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; + +/** + * @author Nicolas Grekas + */ +class TestListenerForV6 extends TestSuite implements TestListenerInterface +{ + private $suite; + private $trait; + + public function __construct(TestSuite $suite = null) + { + if ($suite) { + $this->suite = $suite; + $this->setName($suite->getName().' with polyfills enabled'); + $this->addTest($suite); + } + $this->trait = new TestListenerTrait(); + } + + public function startTestSuite(TestSuite $suite) + { + $this->trait->startTestSuite($suite); + } + + public function addError(Test $test, \Exception $e, $time) + { + $this->trait->addError($test, $e, $time); + } + + public function addWarning(Test $test, Warning $e, $time) + { + } + + public function addFailure(Test $test, AssertionFailedError $e, $time) + { + $this->trait->addError($test, $e, $time); + } + + public function addIncompleteTest(Test $test, \Exception $e, $time) + { + } + + public function addRiskyTest(Test $test, \Exception $e, $time) + { + } + + public function addSkippedTest(Test $test, \Exception $e, $time) + { + } + + public function endTestSuite(TestSuite $suite) + { + } + + public function startTest(Test $test) + { + } + + public function endTest(Test $test, $time) + { + } + + public static function warning($message) + { + return parent::warning($message); + } + + protected function setUp() + { + TestListenerTrait::$enabledPolyfills = $this->suite->getName(); + } + + protected function tearDown() + { + TestListenerTrait::$enabledPolyfills = false; + } +} diff --git a/vendor/symfony/polyfill-util/TestListenerForV7.php b/vendor/symfony/polyfill-util/TestListenerForV7.php new file mode 100644 index 0000000..b304bfa --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListenerForV7.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener as TestListenerInterface; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Framework\WarningTestCase; + +/** + * @author Ion Bazan + */ +class TestListenerForV7 extends TestSuite implements TestListenerInterface +{ + private $suite; + private $trait; + + public function __construct(TestSuite $suite = null) + { + if ($suite) { + $this->suite = $suite; + $this->setName($suite->getName().' with polyfills enabled'); + $this->addTest($suite); + } + $this->trait = new TestListenerTrait(); + } + + public function startTestSuite(TestSuite $suite): void + { + $this->trait->startTestSuite($suite); + } + + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->trait->addError($test, $t, $time); + } + + public function addWarning(Test $test, Warning $e, float $time): void + { + } + + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->trait->addError($test, $e, $time); + } + + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + } + + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + } + + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + } + + public function endTestSuite(TestSuite $suite): void + { + } + + public function startTest(Test $test): void + { + } + + public function endTest(Test $test, float $time): void + { + } + + public static function warning($message): WarningTestCase + { + return parent::warning($message); + } + + protected function setUp(): void + { + TestListenerTrait::$enabledPolyfills = $this->suite->getName(); + } + + protected function tearDown(): void + { + TestListenerTrait::$enabledPolyfills = false; + } +} diff --git a/vendor/symfony/polyfill-util/TestListenerTrait.php b/vendor/symfony/polyfill-util/TestListenerTrait.php new file mode 100644 index 0000000..d8047a0 --- /dev/null +++ b/vendor/symfony/polyfill-util/TestListenerTrait.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Util; + +/** + * @author Nicolas Grekas + */ +class TestListenerTrait +{ + public static $enabledPolyfills; + + public function startTestSuite($mainSuite) + { + if (null !== self::$enabledPolyfills) { + return; + } + self::$enabledPolyfills = false; + $SkippedTestError = class_exists('PHPUnit\Framework\SkippedTestError') ? 'PHPUnit\Framework\SkippedTestError' : 'PHPUnit_Framework_SkippedTestError'; + + foreach ($mainSuite->tests() as $suite) { + $testClass = $suite->getName(); + if (!$tests = $suite->tests()) { + continue; + } + if (!preg_match('/^(.+)\\\\Tests(\\\\.*)Test$/', $testClass, $m)) { + $mainSuite->addTest(TestListener::warning('Unknown naming convention for '.$testClass)); + continue; + } + if (!class_exists($m[1].$m[2])) { + continue; + } + $testedClass = new \ReflectionClass($m[1].$m[2]); + $bootstrap = new \SplFileObject(\dirname($testedClass->getFileName()).'/bootstrap.php'); + $warnings = array(); + $defLine = null; + + foreach (new \RegexIterator($bootstrap, '/define\(\'/') as $defLine) { + preg_match('/define\(\'(?P.+)\'/', $defLine, $matches); + if (\defined($matches['name'])) { + continue; + } + + try { + eval($defLine); + } catch (\PHPUnit_Framework_Exception $ex){ + $warnings[] = TestListener::warning($ex->getMessage()); + } catch (\PHPUnit\Framework\Exception $ex) { + $warnings[] = TestListener::warning($ex->getMessage()); + } + } + + $bootstrap->rewind(); + + foreach (new \RegexIterator($bootstrap, '/return p\\\\'.$testedClass->getShortName().'::/') as $defLine) { + if (!preg_match('/^\s*function (?P[^\(]++)(?P\(.*\)) \{ (?return p\\\\'.$testedClass->getShortName().'::[^\(]++)(?P\([^\)]*+\)); \}$/', $defLine, $f)) { + $warnings[] = TestListener::warning('Invalid line in bootstrap.php: '.trim($defLine)); + continue; + } + $testNamespace = substr($testClass, 0, strrpos($testClass, '\\')); + if (\function_exists($testNamespace.'\\'.$f['name'])) { + continue; + } + + try { + $r = new \ReflectionFunction($f['name']); + if ($r->isUserDefined()) { + throw new \ReflectionException(); + } + if ('idn_to_ascii' === $f['name'] || 'idn_to_utf8' === $f['name']) { + $defLine = sprintf('return INTL_IDNA_VARIANT_2003 === $variant ? \\%s($domain, $options, $variant) : \\%1$s%s', $f['name'], $f['args']); + } elseif (false !== strpos($f['signature'], '&') && 'idn_to_ascii' !== $f['name'] && 'idn_to_utf8' !== $f['name']) { + $defLine = sprintf('return \\%s%s', $f['name'], $f['args']); + } else { + $defLine = sprintf("return \\call_user_func_array('%s', \\func_get_args())", $f['name']); + } + } catch (\ReflectionException $e) { + $defLine = sprintf("throw new \\{$SkippedTestError}('Internal function not found: %s')", $f['name']); + } + + eval(<<getNamespaceName()} as p; + +function {$f['name']}{$f['signature']} +{ + if ('{$testClass}' === TestListenerTrait::\$enabledPolyfills) { + {$f['return']}{$f['args']}; + } + + {$defLine}; +} +EOPHP + ); + } + if (!$warnings && null === $defLine) { + $warnings[] = new $SkippedTestError('No Polyfills found in bootstrap.php for '.$testClass); + } else { + $mainSuite->addTest(new TestListener($suite)); + } + } + foreach ($warnings as $w) { + $mainSuite->addTest($w); + } + } + + public function addError($test, \Exception $e, $time) + { + if (false !== self::$enabledPolyfills) { + $r = new \ReflectionProperty('Exception', 'message'); + $r->setAccessible(true); + $r->setValue($e, 'Polyfills enabled, '.$r->getValue($e)); + } + } +} diff --git a/vendor/symfony/polyfill-util/composer.json b/vendor/symfony/polyfill-util/composer.json new file mode 100644 index 0000000..fa9132b --- /dev/null +++ b/vendor/symfony/polyfill-util/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/polyfill-util", + "type": "library", + "description": "Symfony utilities for portability of PHP codes", + "keywords": ["polyfill", "shim", "compat", "compatibility"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Util\\": "" } + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + } +} diff --git a/vendor/topthink/think-helper/.gitignore b/vendor/topthink/think-helper/.gitignore new file mode 100644 index 0000000..d851bdb --- /dev/null +++ b/vendor/topthink/think-helper/.gitignore @@ -0,0 +1,3 @@ +/vendor/ +/.idea/ +composer.lock \ No newline at end of file diff --git a/vendor/topthink/think-helper/LICENSE b/vendor/topthink/think-helper/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/vendor/topthink/think-helper/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/topthink/think-helper/README.md b/vendor/topthink/think-helper/README.md new file mode 100644 index 0000000..7baf8f7 --- /dev/null +++ b/vendor/topthink/think-helper/README.md @@ -0,0 +1,33 @@ +# thinkphp6 常用的一些扩展类库 + +基于PHP7.1+ + +> 以下类库都在`\\think\\helper`命名空间下 + +## Str + +> 字符串操作 + +``` +// 检查字符串中是否包含某些字符串 +Str::contains($haystack, $needles) + +// 检查字符串是否以某些字符串结尾 +Str::endsWith($haystack, $needles) + +// 获取指定长度的随机字母数字组合的字符串 +Str::random($length = 16) + +// 字符串转小写 +Str::lower($value) + +// 字符串转大写 +Str::upper($value) + +// 获取字符串的长度 +Str::length($value) + +// 截取字符串 +Str::substr($string, $start, $length = null) + +``` \ No newline at end of file diff --git a/vendor/topthink/think-helper/composer.json b/vendor/topthink/think-helper/composer.json new file mode 100644 index 0000000..b68c43b --- /dev/null +++ b/vendor/topthink/think-helper/composer.json @@ -0,0 +1,22 @@ +{ + "name": "topthink/think-helper", + "description": "The ThinkPHP6 Helper Package", + "license": "Apache-2.0", + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "require": { + "php": ">=7.1.0" + }, + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "src/helper.php" + ] + } +} diff --git a/vendor/topthink/think-helper/src/Collection.php b/vendor/topthink/think-helper/src/Collection.php new file mode 100644 index 0000000..f3d0a83 --- /dev/null +++ b/vendor/topthink/think-helper/src/Collection.php @@ -0,0 +1,651 @@ + +// +---------------------------------------------------------------------- +declare (strict_types = 1); + +namespace think; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use IteratorAggregate; +use JsonSerializable; +use think\contract\Arrayable; +use think\contract\Jsonable; +use think\helper\Arr; + +/** + * 数据集管理类 + */ +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Arrayable, Jsonable +{ + /** + * 数据集数据 + * @var array + */ + protected $items = []; + + public function __construct($items = []) + { + $this->items = $this->convertToArray($items); + } + + public static function make($items = []) + { + return new static($items); + } + + /** + * 是否为空 + * @access public + * @return bool + */ + public function isEmpty(): bool + { + return empty($this->items); + } + + public function toArray(): array + { + return array_map(function ($value) { + return $value instanceof Arrayable ? $value->toArray() : $value; + }, $this->items); + } + + public function all(): array + { + return $this->items; + } + + /** + * 合并数组 + * + * @access public + * @param mixed $items 数据 + * @return static + */ + public function merge($items) + { + return new static(array_merge($this->items, $this->convertToArray($items))); + } + + /** + * 按指定键整理数据 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 键名 + * @return array + */ + public function dictionary($items = null, string &$indexKey = null) + { + if ($items instanceof self) { + $items = $items->all(); + } + + $items = is_null($items) ? $this->items : $items; + + if ($items && empty($indexKey)) { + $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk(); + } + + if (isset($indexKey) && is_string($indexKey)) { + return array_column($items, null, $indexKey); + } + + return $items; + } + + /** + * 比较数组,返回差集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 + * @return static + */ + public function diff($items, string $indexKey = null) + { + if ($this->isEmpty() || is_scalar($this->items[0])) { + return new static(array_diff($this->items, $this->convertToArray($items))); + } + + $diff = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (!isset($dictionary[$item[$indexKey]])) { + $diff[] = $item; + } + } + } + + return new static($diff); + } + + /** + * 比较数组,返回交集 + * + * @access public + * @param mixed $items 数据 + * @param string $indexKey 指定比较的键名 + * @return static + */ + public function intersect($items, string $indexKey = null) + { + if ($this->isEmpty() || is_scalar($this->items[0])) { + return new static(array_diff($this->items, $this->convertToArray($items))); + } + + $intersect = []; + $dictionary = $this->dictionary($items, $indexKey); + + if (is_string($indexKey)) { + foreach ($this->items as $item) { + if (isset($dictionary[$item[$indexKey]])) { + $intersect[] = $item; + } + } + } + + return new static($intersect); + } + + /** + * 交换数组中的键和值 + * + * @access public + * @return static + */ + public function flip() + { + return new static(array_flip($this->items)); + } + + /** + * 返回数组中所有的键名 + * + * @access public + * @return static + */ + public function keys() + { + return new static(array_keys($this->items)); + } + + /** + * 返回数组中所有的值组成的新 Collection 实例 + * @access public + * @return static + */ + public function values() + { + return new static(array_values($this->items)); + } + + /** + * 删除数组的最后一个元素(出栈) + * + * @access public + * @return mixed + */ + public function pop() + { + return array_pop($this->items); + } + + /** + * 通过使用用户自定义函数,以字符串返回数组 + * + * @access public + * @param callable $callback 调用方法 + * @param mixed $initial + * @return mixed + */ + public function reduce(callable $callback, $initial = null) + { + return array_reduce($this->items, $callback, $initial); + } + + /** + * 以相反的顺序返回数组。 + * + * @access public + * @return static + */ + public function reverse() + { + return new static(array_reverse($this->items)); + } + + /** + * 删除数组中首个元素,并返回被删除元素的值 + * + * @access public + * @return mixed + */ + public function shift() + { + return array_shift($this->items); + } + + /** + * 在数组结尾插入一个元素 + * @access public + * @param mixed $value 元素 + * @param string $key KEY + * @return void + */ + public function push($value, string $key = null): void + { + if (is_null($key)) { + $this->items[] = $value; + } else { + $this->items[$key] = $value; + } + } + + /** + * 把一个数组分割为新的数组块. + * + * @access public + * @param int $size 块大小 + * @param bool $preserveKeys + * @return static + */ + public function chunk(int $size, bool $preserveKeys = false) + { + $chunks = []; + + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { + $chunks[] = new static($chunk); + } + + return new static($chunks); + } + + /** + * 在数组开头插入一个元素 + * @access public + * @param mixed $value 元素 + * @param string $key KEY + * @return void + */ + public function unshift($value, string $key = null): void + { + if (is_null($key)) { + array_unshift($this->items, $value); + } else { + $this->items = [$key => $value] + $this->items; + } + } + + /** + * 给每个元素执行个回调 + * + * @access public + * @param callable $callback 回调 + * @return $this + */ + public function each(callable $callback) + { + foreach ($this->items as $key => $item) { + $result = $callback($item, $key); + + if (false === $result) { + break; + } elseif (!is_object($item)) { + $this->items[$key] = $result; + } + } + + return $this; + } + + /** + * 用回调函数处理数组中的元素 + * @access public + * @param callable|null $callback 回调 + * @return static + */ + public function map(callable $callback) + { + return new static(array_map($callback, $this->items)); + } + + /** + * 用回调函数过滤数组中的元素 + * @access public + * @param callable|null $callback 回调 + * @return static + */ + public function filter(callable $callback = null) + { + if ($callback) { + return new static(array_filter($this->items, $callback)); + } + + return new static(array_filter($this->items)); + } + + /** + * 根据字段条件过滤数组中的元素 + * @access public + * @param string $field 字段名 + * @param mixed $operator 操作符 + * @param mixed $value 数据 + * @return static + */ + public function where(string $field, $operator, $value = null) + { + if (is_null($value)) { + $value = $operator; + $operator = '='; + } + + return $this->filter(function ($data) use ($field, $operator, $value) { + if (strpos($field, '.')) { + list($field, $relation) = explode('.', $field); + + $result = $data[$field][$relation] ?? null; + } else { + $result = $data[$field] ?? null; + } + + switch (strtolower($operator)) { + case '===': + return $result === $value; + case '!==': + return $result !== $value; + case '!=': + case '<>': + return $result != $value; + case '>': + return $result > $value; + case '>=': + return $result >= $value; + case '<': + return $result < $value; + case '<=': + return $result <= $value; + case 'like': + return is_string($result) && false !== strpos($result, $value); + case 'not like': + return is_string($result) && false === strpos($result, $value); + case 'in': + return is_scalar($result) && in_array($result, $value, true); + case 'not in': + return is_scalar($result) && !in_array($result, $value, true); + case 'between': + list($min, $max) = is_string($value) ? explode(',', $value) : $value; + return is_scalar($result) && $result >= $min && $result <= $max; + case 'not between': + list($min, $max) = is_string($value) ? explode(',', $value) : $value; + return is_scalar($result) && $result > $max || $result < $min; + case '==': + case '=': + default: + return $result == $value; + } + }); + } + + /** + * LIKE过滤 + * @access public + * @param string $field 字段名 + * @param string $value 数据 + * @return static + */ + public function whereLike(string $field, string $value) + { + return $this->where($field, 'like', $value); + } + + /** + * NOT LIKE过滤 + * @access public + * @param string $field 字段名 + * @param string $value 数据 + * @return static + */ + public function whereNotLike(string $field, string $value) + { + return $this->where($field, 'not like', $value); + } + + /** + * IN过滤 + * @access public + * @param string $field 字段名 + * @param array $value 数据 + * @return static + */ + public function whereIn(string $field, array $value) + { + return $this->where($field, 'in', $value); + } + + /** + * NOT IN过滤 + * @access public + * @param string $field 字段名 + * @param array $value 数据 + * @return static + */ + public function whereNotIn(string $field, array $value) + { + return $this->where($field, 'not in', $value); + } + + /** + * BETWEEN 过滤 + * @access public + * @param string $field 字段名 + * @param mixed $value 数据 + * @return static + */ + public function whereBetween(string $field, $value) + { + return $this->where($field, 'between', $value); + } + + /** + * NOT BETWEEN 过滤 + * @access public + * @param string $field 字段名 + * @param mixed $value 数据 + * @return static + */ + public function whereNotBetween(string $field, $value) + { + return $this->where($field, 'not between', $value); + } + + /** + * 返回数据中指定的一列 + * @access public + * @param string $columnKey 键名 + * @param string $indexKey 作为索引值的列 + * @return array + */ + public function column(string $columnKey, string $indexKey = null) + { + return array_column($this->items, $columnKey, $indexKey); + } + + /** + * 对数组排序 + * + * @access public + * @param callable|null $callback 回调 + * @return static + */ + public function sort(callable $callback = null) + { + $items = $this->items; + + $callback = $callback ?: function ($a, $b) { + return $a == $b ? 0 : (($a < $b) ? -1 : 1); + }; + + uasort($items, $callback); + + return new static($items); + } + + /** + * 指定字段排序 + * @access public + * @param string $field 排序字段 + * @param string $order 排序 + * @return $this + */ + public function order(string $field, string $order = null) + { + return $this->sort(function ($a, $b) use ($field, $order) { + $fieldA = $a[$field] ?? null; + $fieldB = $b[$field] ?? null; + + return 'desc' == strtolower($order) ? strcmp($fieldB, $fieldA) : strcmp($fieldA, $fieldB); + }); + } + + /** + * 将数组打乱 + * + * @access public + * @return static + */ + public function shuffle() + { + $items = $this->items; + + shuffle($items); + + return new static($items); + } + + /** + * 获取最后一个单元数据 + * + * @access public + * @param callable|null $callback + * @param null $default + * @return mixed + */ + public function first(callable $callback = null, $default = null) + { + return Arr::first($this->items, $callback, $default); + } + + /** + * 获取第一个单元数据 + * + * @access public + * @param callable|null $callback + * @param null $default + * @return mixed + */ + public function last(callable $callback = null, $default = null) + { + return Arr::last($this->items, $callback, $default); + } + + /** + * 截取数组 + * + * @access public + * @param int $offset 起始位置 + * @param int $length 截取长度 + * @param bool $preserveKeys preserveKeys + * @return static + */ + public function slice(int $offset, int $length = null, bool $preserveKeys = false) + { + return new static(array_slice($this->items, $offset, $length, $preserveKeys)); + } + + // ArrayAccess + public function offsetExists($offset) + { + return array_key_exists($offset, $this->items); + } + + public function offsetGet($offset) + { + return $this->items[$offset]; + } + + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->items[] = $value; + } else { + $this->items[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->items[$offset]); + } + + //Countable + public function count() + { + return count($this->items); + } + + //IteratorAggregate + public function getIterator() + { + return new ArrayIterator($this->items); + } + + //JsonSerializable + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * 转换当前数据集为JSON字符串 + * @access public + * @param integer $options json参数 + * @return string + */ + public function toJson(int $options = JSON_UNESCAPED_UNICODE): string + { + return json_encode($this->toArray(), $options); + } + + public function __toString() + { + return $this->toJson(); + } + + /** + * 转换成数组 + * + * @access public + * @param mixed $items 数据 + * @return array + */ + protected function convertToArray($items): array + { + if ($items instanceof self) { + return $items->all(); + } + + return (array) $items; + } +} diff --git a/vendor/topthink/think-helper/src/contract/Arrayable.php b/vendor/topthink/think-helper/src/contract/Arrayable.php new file mode 100644 index 0000000..7c6b992 --- /dev/null +++ b/vendor/topthink/think-helper/src/contract/Arrayable.php @@ -0,0 +1,8 @@ + +// +---------------------------------------------------------------------- + +use think\Collection; +use think\helper\Arr; + +if (!function_exists('throw_if')) { + /** + * 按条件抛异常 + * + * @param mixed $condition + * @param Throwable|string $exception + * @param array ...$parameters + * @return mixed + * + * @throws Throwable + */ + function throw_if($condition, $exception, ...$parameters) + { + if ($condition) { + throw (is_string($exception) ? new $exception(...$parameters) : $exception); + } + + return $condition; + } +} + +if (!function_exists('throw_unless')) { + /** + * 按条件抛异常 + * + * @param mixed $condition + * @param Throwable|string $exception + * @param array ...$parameters + * @return mixed + * @throws Throwable + */ + function throw_unless($condition, $exception, ...$parameters) + { + if (!$condition) { + throw (is_string($exception) ? new $exception(...$parameters) : $exception); + } + + return $condition; + } +} + +if (!function_exists('tap')) { + /** + * 对一个值调用给定的闭包,然后返回该值 + * + * @param mixed $value + * @param callable|null $callback + * @return mixed + */ + function tap($value, $callback = null) + { + if (is_null($callback)) { + return $value; + } + + $callback($value); + + return $value; + } +} + +if (!function_exists('value')) { + /** + * Return the default value of the given value. + * + * @param mixed $value + * @return mixed + */ + function value($value) + { + return $value instanceof Closure ? $value() : $value; + } +} + +if (!function_exists('collect')) { + /** + * Create a collection from the given value. + * + * @param mixed $value + * @return Collection + */ + function collect($value = null) + { + return new Collection($value); + } +} + +if (!function_exists('data_fill')) { + /** + * Fill in data where it's missing. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @return mixed + */ + function data_fill(&$target, $key, $value) + { + return data_set($target, $key, $value, false); + } +} + +if (!function_exists('data_get')) { + /** + * Get an item from an array or object using "dot" notation. + * + * @param mixed $target + * @param string|array|int $key + * @param mixed $default + * @return mixed + */ + function data_get($target, $key, $default = null) + { + if (is_null($key)) { + return $target; + } + + $key = is_array($key) ? $key : explode('.', $key); + + while (!is_null($segment = array_shift($key))) { + if ('*' === $segment) { + if ($target instanceof Collection) { + $target = $target->all(); + } elseif (!is_array($target)) { + return value($default); + } + + $result = []; + + foreach ($target as $item) { + $result[] = data_get($item, $key); + } + + return in_array('*', $key) ? Arr::collapse($result) : $result; + } + + if (Arr::accessible($target) && Arr::exists($target, $segment)) { + $target = $target[$segment]; + } elseif (is_object($target) && isset($target->{$segment})) { + $target = $target->{$segment}; + } else { + return value($default); + } + } + + return $target; + } +} + +if (!function_exists('data_set')) { + /** + * Set an item on an array or object using dot notation. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @param bool $overwrite + * @return mixed + */ + function data_set(&$target, $key, $value, $overwrite = true) + { + $segments = is_array($key) ? $key : explode('.', $key); + + if (($segment = array_shift($segments)) === '*') { + if (!Arr::accessible($target)) { + $target = []; + } + + if ($segments) { + foreach ($target as &$inner) { + data_set($inner, $segments, $value, $overwrite); + } + } elseif ($overwrite) { + foreach ($target as &$inner) { + $inner = $value; + } + } + } elseif (Arr::accessible($target)) { + if ($segments) { + if (!Arr::exists($target, $segment)) { + $target[$segment] = []; + } + + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite || !Arr::exists($target, $segment)) { + $target[$segment] = $value; + } + } elseif (is_object($target)) { + if ($segments) { + if (!isset($target->{$segment})) { + $target->{$segment} = []; + } + + data_set($target->{$segment}, $segments, $value, $overwrite); + } elseif ($overwrite || !isset($target->{$segment})) { + $target->{$segment} = $value; + } + } else { + $target = []; + + if ($segments) { + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite) { + $target[$segment] = $value; + } + } + + return $target; + } +} + +if (!function_exists('trait_uses_recursive')) { + /** + * 获取一个trait里所有引用到的trait + * + * @param string $trait Trait + * @return array + */ + function trait_uses_recursive(string $trait): array + { + $traits = class_uses($trait); + foreach ($traits as $trait) { + $traits += trait_uses_recursive($trait); + } + + return $traits; + } +} + +if (!function_exists('class_basename')) { + /** + * 获取类名(不包含命名空间) + * + * @param mixed $class 类名 + * @return string + */ + function class_basename($class): string + { + $class = is_object($class) ? get_class($class) : $class; + return basename(str_replace('\\', '/', $class)); + } +} + +if (!function_exists('class_uses_recursive')) { + /** + *获取一个类里所有用到的trait,包括父类的 + * + * @param mixed $class 类名 + * @return array + */ + function class_uses_recursive($class): array + { + if (is_object($class)) { + $class = get_class($class); + } + + $results = []; + $classes = array_merge([$class => $class], class_parents($class)); + foreach ($classes as $class) { + $results += trait_uses_recursive($class); + } + + return array_unique($results); + } +} diff --git a/vendor/topthink/think-helper/src/helper/Arr.php b/vendor/topthink/think-helper/src/helper/Arr.php new file mode 100644 index 0000000..ed4d6a9 --- /dev/null +++ b/vendor/topthink/think-helper/src/helper/Arr.php @@ -0,0 +1,634 @@ + +// +---------------------------------------------------------------------- + +namespace think\helper; + +use ArrayAccess; +use InvalidArgumentException; +use think\Collection; + +class Arr +{ + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * @return bool + */ + public static function accessible($value) + { + return is_array($value) || $value instanceof ArrayAccess; + } + + /** + * Add an element to an array using "dot" notation if it doesn't exist. + * + * @param array $array + * @param string $key + * @param mixed $value + * @return array + */ + public static function add($array, $key, $value) + { + if (is_null(static::get($array, $key))) { + static::set($array, $key, $value); + } + + return $array; + } + + /** + * Collapse an array of arrays into a single array. + * + * @param array $array + * @return array + */ + public static function collapse($array) + { + $results = []; + + foreach ($array as $values) { + if ($values instanceof Collection) { + $values = $values->all(); + } elseif (!is_array($values)) { + continue; + } + + $results = array_merge($results, $values); + } + + return $results; + } + + /** + * Cross join the given arrays, returning all possible permutations. + * + * @param array ...$arrays + * @return array + */ + public static function crossJoin(...$arrays) + { + $results = [[]]; + + foreach ($arrays as $index => $array) { + $append = []; + + foreach ($results as $product) { + foreach ($array as $item) { + $product[$index] = $item; + + $append[] = $product; + } + } + + $results = $append; + } + + return $results; + } + + /** + * Divide an array into two arrays. One with keys and the other with values. + * + * @param array $array + * @return array + */ + public static function divide($array) + { + return [array_keys($array), array_values($array)]; + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @param array $array + * @param string $prepend + * @return array + */ + public static function dot($array, $prepend = '') + { + $results = []; + + foreach ($array as $key => $value) { + if (is_array($value) && !empty($value)) { + $results = array_merge($results, static::dot($value, $prepend . $key . '.')); + } else { + $results[$prepend . $key] = $value; + } + } + + return $results; + } + + /** + * Get all of the given array except for a specified array of keys. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function except($array, $keys) + { + static::forget($array, $keys); + + return $array; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int $key + * @return bool + */ + public static function exists($array, $key) + { + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Return the first element in an array passing a given truth test. + * + * @param array $array + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + public static function first($array, callable $callback = null, $default = null) + { + if (is_null($callback)) { + if (empty($array)) { + return value($default); + } + + foreach ($array as $item) { + return $item; + } + } + + foreach ($array as $key => $value) { + if (call_user_func($callback, $value, $key)) { + return $value; + } + } + + return value($default); + } + + /** + * Return the last element in an array passing a given truth test. + * + * @param array $array + * @param callable|null $callback + * @param mixed $default + * @return mixed + */ + public static function last($array, callable $callback = null, $default = null) + { + if (is_null($callback)) { + return empty($array) ? value($default) : end($array); + } + + return static::first(array_reverse($array, true), $callback, $default); + } + + /** + * Flatten a multi-dimensional array into a single level. + * + * @param array $array + * @param int $depth + * @return array + */ + public static function flatten($array, $depth = INF) + { + $result = []; + + foreach ($array as $item) { + $item = $item instanceof Collection ? $item->all() : $item; + + if (!is_array($item)) { + $result[] = $item; + } elseif ($depth === 1) { + $result = array_merge($result, array_values($item)); + } else { + $result = array_merge($result, static::flatten($item, $depth - 1)); + } + } + + return $result; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array $array + * @param array|string $keys + * @return void + */ + public static function forget(&$array, $keys) + { + $original = &$array; + + $keys = (array) $keys; + + if (count($keys) === 0) { + return; + } + + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + + continue; + } + + $parts = explode('.', $key); + + // clean up before each pass + $array = &$original; + + while (count($parts) > 1) { + $part = array_shift($parts); + + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + + unset($array[array_shift($parts)]); + } + } + + /** + * Get an item from an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function get($array, $key, $default = null) + { + if (!static::accessible($array)) { + return value($default); + } + + if (is_null($key)) { + return $array; + } + + if (static::exists($array, $key)) { + return $array[$key]; + } + + if (strpos($key, '.') === false) { + return $array[$key] ?? value($default); + } + + foreach (explode('.', $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return value($default); + } + } + + return $array; + } + + /** + * Check if an item or items exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * @return bool + */ + public static function has($array, $keys) + { + $keys = (array) $keys; + + if (!$array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + $subKeyArray = $array; + + if (static::exists($array, $key)) { + continue; + } + + foreach (explode('.', $key) as $segment) { + if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { + $subKeyArray = $subKeyArray[$segment]; + } else { + return false; + } + } + } + + return true; + } + + /** + * Determines if an array is associative. + * + * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. + * + * @param array $array + * @return bool + */ + public static function isAssoc(array $array) + { + $keys = array_keys($array); + + return array_keys($keys) !== $keys; + } + + /** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function only($array, $keys) + { + return array_intersect_key($array, array_flip((array) $keys)); + } + + /** + * Pluck an array of values from an array. + * + * @param array $array + * @param string|array $value + * @param string|array|null $key + * @return array + */ + public static function pluck($array, $value, $key = null) + { + $results = []; + + [$value, $key] = static::explodePluckParameters($value, $key); + + foreach ($array as $item) { + $itemValue = data_get($item, $value); + + // If the key is "null", we will just append the value to the array and keep + // looping. Otherwise we will key the array using the value of the key we + // received from the developer. Then we'll return the final array form. + if (is_null($key)) { + $results[] = $itemValue; + } else { + $itemKey = data_get($item, $key); + + if (is_object($itemKey) && method_exists($itemKey, '__toString')) { + $itemKey = (string) $itemKey; + } + + $results[$itemKey] = $itemValue; + } + } + + return $results; + } + + /** + * Explode the "value" and "key" arguments passed to "pluck". + * + * @param string|array $value + * @param string|array|null $key + * @return array + */ + protected static function explodePluckParameters($value, $key) + { + $value = is_string($value) ? explode('.', $value) : $value; + + $key = is_null($key) || is_array($key) ? $key : explode('.', $key); + + return [$value, $key]; + } + + /** + * Push an item onto the beginning of an array. + * + * @param array $array + * @param mixed $value + * @param mixed $key + * @return array + */ + public static function prepend($array, $value, $key = null) + { + if (is_null($key)) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * Get a value from the array, and remove it. + * + * @param array $array + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function pull(&$array, $key, $default = null) + { + $value = static::get($array, $key, $default); + + static::forget($array, $key); + + return $value; + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random($array, $number = null) + { + $requested = is_null($number) ? 1 : $number; + + $count = count($array); + + if ($requested > $count) { + throw new InvalidArgumentException( + "You requested {$requested} items, but there are only {$count} items available." + ); + } + + if (is_null($number)) { + return $array[array_rand($array)]; + } + + if ((int) $number === 0) { + return []; + } + + $keys = array_rand($array, $number); + + $results = []; + + foreach ((array) $keys as $key) { + $results[] = $array[$key]; + } + + return $results; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param array $array + * @param string $key + * @param mixed $value + * @return array + */ + public static function set(&$array, $key, $value) + { + if (is_null($key)) { + return $array = $value; + } + + $keys = explode('.', $key); + + while (count($keys) > 1) { + $key = array_shift($keys); + + // If the key doesn't exist at this depth, we will just create an empty array + // to hold the next value, allowing us to create the arrays to hold final + // values at the correct depth. Then we'll keep digging into the array. + if (!isset($array[$key]) || !is_array($array[$key])) { + $array[$key] = []; + } + + $array = &$array[$key]; + } + + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Shuffle the given array and return the result. + * + * @param array $array + * @param int|null $seed + * @return array + */ + public static function shuffle($array, $seed = null) + { + if (is_null($seed)) { + shuffle($array); + } else { + srand($seed); + + usort($array, function () { + return rand(-1, 1); + }); + } + + return $array; + } + + /** + * Sort the array using the given callback or "dot" notation. + * + * @param array $array + * @param callable|string|null $callback + * @return array + */ + public static function sort($array, $callback = null) + { + return Collection::make($array)->sort($callback)->all(); + } + + /** + * Recursively sort an array by keys and values. + * + * @param array $array + * @return array + */ + public static function sortRecursive($array) + { + foreach ($array as &$value) { + if (is_array($value)) { + $value = static::sortRecursive($value); + } + } + + if (static::isAssoc($array)) { + ksort($array); + } else { + sort($array); + } + + return $array; + } + + /** + * Convert the array into a query string. + * + * @param array $array + * @return string + */ + public static function query($array) + { + return http_build_query($array, null, '&', PHP_QUERY_RFC3986); + } + + /** + * Filter the array using the given callback. + * + * @param array $array + * @param callable $callback + * @return array + */ + public static function where($array, callable $callback) + { + return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); + } + + /** + * If the given value is not an array and not null, wrap it in one. + * + * @param mixed $value + * @return array + */ + public static function wrap($value) + { + if (is_null($value)) { + return []; + } + + return is_array($value) ? $value : [$value]; + } +} \ No newline at end of file diff --git a/vendor/topthink/think-helper/src/helper/Str.php b/vendor/topthink/think-helper/src/helper/Str.php new file mode 100644 index 0000000..7391fbd --- /dev/null +++ b/vendor/topthink/think-helper/src/helper/Str.php @@ -0,0 +1,234 @@ + +// +---------------------------------------------------------------------- +namespace think\helper; + +class Str +{ + + protected static $snakeCache = []; + + protected static $camelCache = []; + + protected static $studlyCache = []; + + /** + * 检查字符串中是否包含某些字符串 + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function contains(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ('' != $needle && mb_strpos($haystack, $needle) !== false) { + return true; + } + } + + return false; + } + + /** + * 检查字符串是否以某些字符串结尾 + * + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function endsWith(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ((string) $needle === static::substr($haystack, -static::length($needle))) { + return true; + } + } + + return false; + } + + /** + * 检查字符串是否以某些字符串开头 + * + * @param string $haystack + * @param string|array $needles + * @return bool + */ + public static function startsWith(string $haystack, $needles): bool + { + foreach ((array) $needles as $needle) { + if ('' != $needle && mb_strpos($haystack, $needle) === 0) { + return true; + } + } + + return false; + } + + /** + * 获取指定长度的随机字母数字组合的字符串 + * + * @param int $length + * @param int $type + * @param string $addChars + * @return string + */ + public static function random(int $length = 6, int $type = null, string $addChars = ''): string + { + $str = ''; + switch ($type) { + case 0: + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars; + break; + case 1: + $chars = str_repeat('0123456789', 3); + break; + case 2: + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars; + break; + case 3: + $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars; + break; + case 4: + $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书" . $addChars; + break; + default: + $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars; + break; + } + if ($length > 10) { + $chars = $type == 1 ? str_repeat($chars, $length) : str_repeat($chars, 5); + } + if ($type != 4) { + $chars = str_shuffle($chars); + $str = substr($chars, 0, $length); + } else { + for ($i = 0; $i < $length; $i++) { + $str .= mb_substr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1); + } + } + return $str; + } + + /** + * 字符串转小写 + * + * @param string $value + * @return string + */ + public static function lower(string $value): string + { + return mb_strtolower($value, 'UTF-8'); + } + + /** + * 字符串转大写 + * + * @param string $value + * @return string + */ + public static function upper(string $value): string + { + return mb_strtoupper($value, 'UTF-8'); + } + + /** + * 获取字符串的长度 + * + * @param string $value + * @return int + */ + public static function length(string $value): int + { + return mb_strlen($value); + } + + /** + * 截取字符串 + * + * @param string $string + * @param int $start + * @param int|null $length + * @return string + */ + public static function substr(string $string, int $start, int $length = null): string + { + return mb_substr($string, $start, $length, 'UTF-8'); + } + + /** + * 驼峰转下划线 + * + * @param string $value + * @param string $delimiter + * @return string + */ + public static function snake(string $value, string $delimiter = '_'): string + { + $key = $value; + + if (isset(static::$snakeCache[$key][$delimiter])) { + return static::$snakeCache[$key][$delimiter]; + } + + if (!ctype_lower($value)) { + $value = preg_replace('/\s+/u', '', $value); + + $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value)); + } + + return static::$snakeCache[$key][$delimiter] = $value; + } + + /** + * 下划线转驼峰(首字母小写) + * + * @param string $value + * @return string + */ + public static function camel(string $value): string + { + if (isset(static::$camelCache[$value])) { + return static::$camelCache[$value]; + } + + return static::$camelCache[$value] = lcfirst(static::studly($value)); + } + + /** + * 下划线转驼峰(首字母大写) + * + * @param string $value + * @return string + */ + public static function studly(string $value): string + { + $key = $value; + + if (isset(static::$studlyCache[$key])) { + return static::$studlyCache[$key]; + } + + $value = ucwords(str_replace(['-', '_'], ' ', $value)); + + return static::$studlyCache[$key] = str_replace(' ', '', $value); + } + + /** + * 转为首字母大写的标题格式 + * + * @param string $value + * @return string + */ + public static function title(string $value): string + { + return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); + } +} diff --git a/vendor/topthink/think-installer/.gitignore b/vendor/topthink/think-installer/.gitignore new file mode 100644 index 0000000..8f4c02d --- /dev/null +++ b/vendor/topthink/think-installer/.gitignore @@ -0,0 +1,3 @@ +/.idea +composer.lock +/vendor \ No newline at end of file diff --git a/vendor/topthink/think-installer/composer.json b/vendor/topthink/think-installer/composer.json new file mode 100644 index 0000000..4005de2 --- /dev/null +++ b/vendor/topthink/think-installer/composer.json @@ -0,0 +1,25 @@ +{ + "name": "topthink/think-installer", + "type": "composer-plugin", + "require": { + "composer-plugin-api": "^1.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev" + }, + "license": "Apache-2.0", + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "autoload": { + "psr-4": { + "think\\composer\\": "src" + } + }, + "extra": { + "class": "think\\composer\\Plugin" + } +} diff --git a/vendor/topthink/think-installer/src/Plugin.php b/vendor/topthink/think-installer/src/Plugin.php new file mode 100644 index 0000000..757c30f --- /dev/null +++ b/vendor/topthink/think-installer/src/Plugin.php @@ -0,0 +1,26 @@ +getInstallationManager(); + + //框架核心 + $manager->addInstaller(new ThinkFramework($io, $composer)); + + //单元测试 + $manager->addInstaller(new ThinkTesting($io, $composer)); + + //扩展 + $manager->addInstaller(new ThinkExtend($io, $composer)); + + } +} \ No newline at end of file diff --git a/vendor/topthink/think-installer/src/ThinkExtend.php b/vendor/topthink/think-installer/src/ThinkExtend.php new file mode 100644 index 0000000..adf5ac2 --- /dev/null +++ b/vendor/topthink/think-installer/src/ThinkExtend.php @@ -0,0 +1,72 @@ + +// +---------------------------------------------------------------------- + +namespace think\composer; + +use Composer\Installer\LibraryInstaller; +use Composer\Package\PackageInterface; +use Composer\Repository\InstalledRepositoryInterface; + +class ThinkExtend extends LibraryInstaller +{ + + public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + { + parent::install($repo, $package); + $this->copyExtraFiles($package); + } + + public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) + { + parent::update($repo, $initial, $target); + $this->copyExtraFiles($target); + + } + + protected function copyExtraFiles(PackageInterface $package) + { + if ($this->composer->getPackage()->getType() == 'project') { + + $extra = $package->getExtra(); + + if (!empty($extra['think-config'])) { + + $configDir = 'config'; + + $this->filesystem->ensureDirectoryExists($configDir); + + //配置文件 + foreach ((array) $extra['think-config'] as $name => $config) { + $target = $configDir . DIRECTORY_SEPARATOR . $name . '.php'; + $source = $this->getInstallPath($package) . DIRECTORY_SEPARATOR . $config; + + if (is_file($target)) { + $this->io->write("File {$target} exist!"); + continue; + } + + if (!is_file($source)) { + $this->io->write("File {$target} not exist!"); + continue; + } + + copy($source, $target); + } + + } + } + } + + public function supports($packageType) + { + return 'think-extend' === $packageType; + } +} \ No newline at end of file diff --git a/vendor/topthink/think-installer/src/ThinkFramework.php b/vendor/topthink/think-installer/src/ThinkFramework.php new file mode 100644 index 0000000..cdb7d84 --- /dev/null +++ b/vendor/topthink/think-installer/src/ThinkFramework.php @@ -0,0 +1,59 @@ +composer->getPackage()->getType() == 'project' && $package->getInstallationSource() != 'source') { + //remove tests dir + $this->filesystem->removeDirectory($this->getInstallPath($package) . DIRECTORY_SEPARATOR . 'tests'); + } + } + + /** + * {@inheritDoc} + */ + public function getInstallPath(PackageInterface $package) + { + if ('topthink/framework' !== $package->getPrettyName()) { + throw new \InvalidArgumentException('Unable to install this library!'); + } + + if ($this->composer->getPackage()->getType() !== 'project') { + return parent::getInstallPath($package); + } + + if ($this->composer->getPackage()) { + $extra = $this->composer->getPackage()->getExtra(); + if (!empty($extra['think-path'])) { + return $extra['think-path']; + } + } + + return 'thinkphp'; + } + + public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) + { + parent::update($repo, $initial, $target); + if ($this->composer->getPackage()->getType() == 'project' && $target->getInstallationSource() != 'source') { + //remove tests dir + $this->filesystem->removeDirectory($this->getInstallPath($target) . DIRECTORY_SEPARATOR . 'tests'); + } + } + + /** + * {@inheritDoc} + */ + public function supports($packageType) + { + return 'think-framework' === $packageType; + } +} \ No newline at end of file diff --git a/vendor/topthink/think-installer/src/ThinkTesting.php b/vendor/topthink/think-installer/src/ThinkTesting.php new file mode 100644 index 0000000..bf27f72 --- /dev/null +++ b/vendor/topthink/think-installer/src/ThinkTesting.php @@ -0,0 +1,68 @@ + +// +---------------------------------------------------------------------- + +namespace think\composer; + + +use Composer\Installer\LibraryInstaller; +use Composer\Package\PackageInterface; +use Composer\Repository\InstalledRepositoryInterface; + +class ThinkTesting extends LibraryInstaller +{ + /** + * {@inheritDoc} + */ + public function getInstallPath(PackageInterface $package) + { + if ('topthink/think-testing' !== $package->getPrettyName()) { + throw new \InvalidArgumentException('Unable to install this library!'); + } + + return parent::getInstallPath($package); + } + + public function install(InstalledRepositoryInterface $repo, PackageInterface $package) + { + parent::install($repo, $package); + + $this->copyTestDir($package); + + + } + + public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) + { + parent::update($repo, $initial, $target); + + $this->copyTestDir($target); + + } + + private function copyTestDir(PackageInterface $package) + { + $appDir = dirname($this->vendorDir); + $source = $this->getInstallPath($package) . DIRECTORY_SEPARATOR . 'example'; + if (!is_file($appDir . DIRECTORY_SEPARATOR . 'phpunit.xml')) { + $this->filesystem->copyThenRemove($source, $appDir); + } else { + $this->filesystem->removeDirectoryPhp($source); + } + } + + /** + * {@inheritDoc} + */ + public function supports($packageType) + { + return 'think-testing' === $packageType; + } +} \ No newline at end of file diff --git a/vendor/topthink/think-queue/.gitignore b/vendor/topthink/think-queue/.gitignore new file mode 100644 index 0000000..4aec782 --- /dev/null +++ b/vendor/topthink/think-queue/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +/.idea/ +/composer.lock +/thinkphp/ diff --git a/vendor/topthink/think-queue/LICENSE b/vendor/topthink/think-queue/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/vendor/topthink/think-queue/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/topthink/think-queue/README.md b/vendor/topthink/think-queue/README.md new file mode 100644 index 0000000..35dead1 --- /dev/null +++ b/vendor/topthink/think-queue/README.md @@ -0,0 +1,135 @@ +# think-queue for ThinkPHP5.1 + +## 安装 + +> composer require topthink/think-queue + +## 配置 + +> 配置文件位于 `config/queue.php` + +### 公共配置 + +``` +[ + 'connector'=>'sync' //驱动类型,可选择 sync(默认):同步执行,database:数据库驱动,redis:Redis驱动,topthink:Topthink驱动 + //或其他自定义的完整的类名 +] +``` + +### 驱动配置 +> 各个驱动的具体可用配置项在`think\queue\connector`目录下各个驱动类里的`options`属性中,写在上面的`queue`配置里即可覆盖 + + +## 使用 Database +> 创建如下数据表 + +``` +CREATE TABLE `prefix_jobs` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `queue` varchar(255) NOT NULL, + `payload` longtext NOT NULL, + `attempts` tinyint(3) unsigned NOT NULL, + `reserved` tinyint(3) unsigned NOT NULL, + `reserved_at` int(10) unsigned DEFAULT NULL, + `available_at` int(10) unsigned NOT NULL, + `created_at` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +## 创建任务类 +> 单模块项目推荐使用 `app\job` 作为任务类的命名空间 +> 多模块项目可用使用 `app\module\job` 作为任务类的命名空间 +> 也可以放在任意可以自动加载到的地方 + +任务类不需继承任何类,如果这个类只有一个任务,那么就只需要提供一个`fire`方法就可以了,如果有多个小任务,就写多个方法,下面发布任务的时候会有区别 +每个方法会传入两个参数 `think\queue\Job $job`(当前的任务对象) 和 `$data`(发布任务时自定义的数据) + +还有个可选的任务失败执行的方法 `failed` 传入的参数为`$data`(发布任务时自定义的数据) + +### 下面写两个例子 + +``` +namespace app\job; + +use think\queue\Job; + +class Job1{ + + public function fire(Job $job, $data){ + + //....这里执行具体的任务 + + if ($job->attempts() > 3) { + //通过这个方法可以检查这个任务已经重试了几次了 + } + + + //如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法 + $job->delete(); + + // 也可以重新发布这个任务 + $job->release($delay); //$delay为延迟时间 + + } + + public function failed($data){ + + // ...任务达到最大重试次数后,失败了 + } + +} + +``` + +``` + +namespace app\lib\job; + +use think\queue\Job; + +class Job2{ + + public function task1(Job $job, $data){ + + + } + + public function task2(Job $job, $data){ + + + } + + public function failed($data){ + + + } + +} + +``` + + +## 发布任务 +> `think\Queue::push($job, $data = '', $queue = null)` 和 `think\Queue::later($delay, $job, $data = '', $queue = null)` 两个方法,前者是立即执行,后者是在`$delay`秒后执行 + +`$job` 是任务名 +单模块的,且命名空间是`app\job`的,比如上面的例子一,写`Job1`类名即可 +多模块的,且命名空间是`app\module\job`的,写`model/Job1`即可 +其他的需要些完整的类名,比如上面的例子二,需要写完整的类名`app\lib\job\Job2` +如果一个任务类里有多个小任务的话,如上面的例子二,需要用@+方法名`app\lib\job\Job2@task1`、`app\lib\job\Job2@task2` + +`$data` 是你要传到任务里的参数 + +`$queue` 队列名,指定这个任务是在哪个队列上执行,同下面监控队列的时候指定的队列名,可不填 + +## 监听任务并执行 + +> php think queue:listen + +> php think queue:work --daemon(不加--daemon为执行单个任务) + +两种,具体的可选参数可以输入命令加 --help 查看 + +>可配合supervisor使用,保证进程常驻 diff --git a/vendor/topthink/think-queue/composer.json b/vendor/topthink/think-queue/composer.json new file mode 100644 index 0000000..0ed3b4e --- /dev/null +++ b/vendor/topthink/think-queue/composer.json @@ -0,0 +1,30 @@ +{ + "name": "topthink/think-queue", + "description": "The ThinkPHP5 Queue Package", + "type": "think-extend", + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "license": "Apache-2.0", + "autoload": { + "psr-4": { + "think\\": "src" + }, + "files": [ + "src/common.php" + ] + }, + "require": { + "topthink/think-helper": ">=1.0.4", + "topthink/think-installer": "^2.0", + "topthink/framework": "5.1.*" + }, + "extra": { + "think-config": { + "queue": "src/config.php" + } + } +} diff --git a/vendor/topthink/think-queue/src/Queue.php b/vendor/topthink/think-queue/src/Queue.php new file mode 100644 index 0000000..531169f --- /dev/null +++ b/vendor/topthink/think-queue/src/Queue.php @@ -0,0 +1,49 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\helper\Str; +use think\queue\Connector; + +/** + * Class Queue + * @package think\queue + * + * @method static push($job, $data = '', $queue = null) + * @method static later($delay, $job, $data = '', $queue = null) + * @method static pop($queue = null) + * @method static marshal() + */ +class Queue +{ + /** @var Connector */ + protected static $connector; + + private static function buildConnector() + { + $options = \think\facade\Config::pull('queue'); + $type = !empty($options['connector']) ? $options['connector'] : 'Sync'; + + if (!isset(self::$connector)) { + + $class = false !== strpos($type, '\\') ? $type : '\\think\\queue\\connector\\' . Str::studly($type); + + self::$connector = new $class($options); + } + return self::$connector; + } + + public static function __callStatic($name, $arguments) + { + return call_user_func_array([self::buildConnector(), $name], $arguments); + } +} diff --git a/vendor/topthink/think-queue/src/common.php b/vendor/topthink/think-queue/src/common.php new file mode 100644 index 0000000..81b35d1 --- /dev/null +++ b/vendor/topthink/think-queue/src/common.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +\think\Console::addDefaultCommands([ + "think\\queue\\command\\Work", + "think\\queue\\command\\Restart", + "think\\queue\\command\\Listen", + "think\\queue\\command\\Subscribe" +]); + +if (!function_exists('queue')) { + + /** + * 添加到队列 + * @param $job + * @param string $data + * @param int $delay + * @param null $queue + */ + function queue($job, $data = '', $delay = 0, $queue = null) + { + if ($delay > 0) { + \think\Queue::later($delay, $job, $data, $queue); + } else { + \think\Queue::push($job, $data, $queue); + } + } +} diff --git a/vendor/topthink/think-queue/src/config.php b/vendor/topthink/think-queue/src/config.php new file mode 100644 index 0000000..9223ef6 --- /dev/null +++ b/vendor/topthink/think-queue/src/config.php @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- + +return [ + 'connector' => 'Sync' +]; diff --git a/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php b/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php new file mode 100644 index 0000000..0f1a627 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/CallQueuedHandler.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +class CallQueuedHandler +{ + + public function call(Job $job, array $data) + { + $command = unserialize($data['command']); + + call_user_func([$command, 'handle']); + + if (!$job->isDeletedOrReleased()) { + $job->delete(); + } + } + + public function failed(array $data) + { + $command = unserialize($data['command']); + + if (method_exists($command, 'failed')) { + $command->failed(); + } + } +} diff --git a/vendor/topthink/think-queue/src/queue/Connector.php b/vendor/topthink/think-queue/src/queue/Connector.php new file mode 100644 index 0000000..b069437 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/Connector.php @@ -0,0 +1,69 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +use InvalidArgumentException; + +abstract class Connector +{ + protected $options = []; + + abstract public function push($job, $data = '', $queue = null); + + abstract public function later($delay, $job, $data = '', $queue = null); + + abstract public function pop($queue = null); + + public function marshal() + { + throw new \RuntimeException('pop queues not support for this type'); + } + + protected function createPayload($job, $data = '', $queue = null) + { + if (is_object($job)) { + $payload = json_encode([ + 'job' => 'think\queue\CallQueuedHandler@call', + 'data' => [ + 'commandName' => get_class($job), + 'command' => serialize(clone $job), + ], + ]); + } else { + $payload = json_encode($this->createPlainPayload($job, $data)); + } + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg()); + } + + return $payload; + } + + protected function createPlainPayload($job, $data) + { + return ['job' => $job, 'data' => $data]; + } + + protected function setMeta($payload, $key, $value) + { + $payload = json_decode($payload, true); + $payload[$key] = $value; + $payload = json_encode($payload); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg()); + } + + return $payload; + } +} diff --git a/vendor/topthink/think-queue/src/queue/Job.php b/vendor/topthink/think-queue/src/queue/Job.php new file mode 100644 index 0000000..618769e --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/Job.php @@ -0,0 +1,213 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +use DateTime; +use think\facade\Env; + +abstract class Job +{ + + /** + * The job handler instance. + * @var mixed + */ + protected $instance; + + /** + * The name of the queue the job belongs to. + * @var string + */ + protected $queue; + + /** + * Indicates if the job has been deleted. + * @var bool + */ + protected $deleted = false; + + /** + * Indicates if the job has been released. + * @var bool + */ + protected $released = false; + + /** + * Fire the job. + * @return void + */ + abstract public function fire(); + + /** + * Delete the job from the queue. + * @return void + */ + public function delete() + { + $this->deleted = true; + } + + /** + * Determine if the job has been deleted. + * @return bool + */ + public function isDeleted() + { + return $this->deleted; + } + + /** + * Release the job back into the queue. + * @param int $delay + * @return void + */ + public function release($delay = 0) + { + $this->released = true; + } + + /** + * Determine if the job was released back into the queue. + * @return bool + */ + public function isReleased() + { + return $this->released; + } + + /** + * Determine if the job has been deleted or released. + * @return bool + */ + public function isDeletedOrReleased() + { + return $this->isDeleted() || $this->isReleased(); + } + + /** + * Get the number of times the job has been attempted. + * @return int + */ + abstract public function attempts(); + + /** + * Get the raw body string for the job. + * @return string + */ + abstract public function getRawBody(); + + /** + * Resolve and fire the job handler method. + * @param array $payload + * @return void + */ + protected function resolveAndFire(array $payload) + { + list($class, $method) = $this->parseJob($payload['job']); + + $this->instance = $this->resolve($class); + if ($this->instance) { + $this->instance->{$method}($this, $payload['data']); + } + } + + /** + * Parse the job declaration into class and method. + * @param string $job + * @return array + */ + protected function parseJob($job) + { + $segments = explode('@', $job); + + return count($segments) > 1 ? $segments : [$segments[0], 'fire']; + } + + /** + * Resolve the given job handler. + * @param string $name + * @return mixed + */ + protected function resolve($name) + { + if (strpos($name, '\\') === false) { + + if (strpos($name, '/') === false) { + $module = ''; + } else { + list($module, $name) = explode('/', $name, 2); + } + + $name = Env::get('app_namespace') . ($module ? '\\' . strtolower($module) : '') . '\\job\\' . $name; + } + if (class_exists($name)) { + return new $name(); + } + } + + /** + * Call the failed method on the job instance. + * @return void + */ + public function failed() + { + $payload = json_decode($this->getRawBody(), true); + + list($class, $method) = $this->parseJob($payload['job']); + + $this->instance = $this->resolve($class); + if ($this->instance && method_exists($this->instance, 'failed')) { + $this->instance->failed($payload['data']); + } + } + + /** + * Calculate the number of seconds with the given delay. + * @param \DateTime|int $delay + * @return int + */ + protected function getSeconds($delay) + { + if ($delay instanceof DateTime) { + return max(0, $delay->getTimestamp() - $this->getTime()); + } + + return (int) $delay; + } + + /** + * Get the current system time. + * @return int + */ + protected function getTime() + { + return time(); + } + + /** + * Get the name of the queued job class. + * @return string + */ + public function getName() + { + return json_decode($this->getRawBody(), true)['job']; + } + + /** + * Get the name of the queue the job belongs to. + * @return string + */ + public function getQueue() + { + return $this->queue; + } +} diff --git a/vendor/topthink/think-queue/src/queue/Listener.php b/vendor/topthink/think-queue/src/queue/Listener.php new file mode 100644 index 0000000..e8fcaa4 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/Listener.php @@ -0,0 +1,164 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +use Closure; +use think\Process; + +class Listener +{ + + /** + * @var string + */ + protected $commandPath; + + /** + * @var int + */ + protected $sleep = 3; + + /** + * @var int + */ + protected $maxTries = 0; + + /** + * @var string + */ + protected $workerCommand; + + /** + * @var \Closure|null + */ + protected $outputHandler; + + /** + * @param string $commandPath + */ + public function __construct($commandPath) + { + $this->commandPath = $commandPath; + $this->workerCommand = + '"' . PHP_BINARY . '" think queue:work --queue="%s" --delay=%s --memory=%s --sleep=%s --tries=%s'; + } + + /** + * @param string $queue + * @param string $delay + * @param string $memory + * @param int $timeout + * @return void + */ + public function listen($queue, $delay, $memory, $timeout = 60) + { + $process = $this->makeProcess($queue, $delay, $memory, $timeout); + + while (true) { + $this->runProcess($process, $memory); + } + } + + /** + * @param \Think\Process $process + * @param int $memory + */ + public function runProcess(Process $process, $memory) + { + $process->run(function ($type, $line) { + $this->handleWorkerOutput($type, $line); + }); + + if ($this->memoryExceeded($memory)) { + $this->stop(); + } + } + + /** + * @param string $queue + * @param int $delay + * @param int $memory + * @param int $timeout + * @return \think\Process + */ + public function makeProcess($queue, $delay, $memory, $timeout) + { + $string = $this->workerCommand; + $command = sprintf($string, $queue, $delay, $memory, $this->sleep, $this->maxTries); + + return new Process($command, $this->commandPath, null, null, $timeout); + } + + /** + * @param int $type + * @param string $line + * @return void + */ + protected function handleWorkerOutput($type, $line) + { + if (isset($this->outputHandler)) { + call_user_func($this->outputHandler, $type, $line); + } + } + + /** + * @param int $memoryLimit + * @return bool + */ + public function memoryExceeded($memoryLimit) + { + return (memory_get_usage() / 1024 / 1024) >= $memoryLimit; + } + + /** + * @return void + */ + public function stop() + { + die; + } + + /** + * @param \Closure $outputHandler + * @return void + */ + public function setOutputHandler(Closure $outputHandler) + { + $this->outputHandler = $outputHandler; + } + + /** + * @return int + */ + public function getSleep() + { + return $this->sleep; + } + + /** + * @param int $sleep + * @return void + */ + public function setSleep($sleep) + { + $this->sleep = $sleep; + } + + /** + * @param int $tries + * @return void + */ + public function setMaxTries($tries) + { + $this->maxTries = $tries; + } +} diff --git a/vendor/topthink/think-queue/src/queue/Queueable.php b/vendor/topthink/think-queue/src/queue/Queueable.php new file mode 100644 index 0000000..2a6ec4e --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/Queueable.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +trait Queueable +{ + + /** @var string 队列名称 */ + public $queue; + + /** @var integer 延迟时间 */ + public $delay; + + /** + * 设置队列名 + * @param $queue + * @return $this + */ + public function queue($queue) + { + $this->queue = $queue; + + return $this; + } + + /** + * 设置延迟时间 + * @param $delay + * @return $this + */ + public function delay($delay) + { + $this->delay = $delay; + + return $this; + } +} diff --git a/vendor/topthink/think-queue/src/queue/ShouldQueue.php b/vendor/topthink/think-queue/src/queue/ShouldQueue.php new file mode 100644 index 0000000..cb49c12 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/ShouldQueue.php @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +interface ShouldQueue +{ + +} diff --git a/vendor/topthink/think-queue/src/queue/Worker.php b/vendor/topthink/think-queue/src/queue/Worker.php new file mode 100644 index 0000000..a008d3c --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/Worker.php @@ -0,0 +1,119 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue; + +use Exception; +use think\facade\Hook; +use think\Queue; + +class Worker +{ + + /** + * 执行下个任务 + * @param string $queue + * @param int $delay + * @param int $sleep + * @param int $maxTries + * @return array + */ + public function pop($queue = null, $delay = 0, $sleep = 3, $maxTries = 0) + { + + $job = $this->getNextJob($queue); + + if (!is_null($job)) { + Hook::listen('worker_before_process', $queue); + return $this->process($job, $maxTries, $delay); + } + + Hook::listen('worker_before_sleep', $queue); + $this->sleep($sleep); + + return ['job' => null, 'failed' => false]; + } + + /** + * 获取下个任务 + * @param string $queue + * @return Job + */ + protected function getNextJob($queue) + { + if (is_null($queue)) { + return Queue::pop(); + } + + foreach (explode(',', $queue) as $queue) { + if (!is_null($job = Queue::pop($queue))) { + return $job; + } + } + } + + /** + * Process a given job from the queue. + * @param \think\queue\Job $job + * @param int $maxTries + * @param int $delay + * @return array + * @throws Exception + */ + public function process(Job $job, $maxTries = 0, $delay = 0) + { + if ($maxTries > 0 && $job->attempts() > $maxTries) { + return $this->logFailedJob($job); + } + + try { + $job->fire(); + + return ['job' => $job, 'failed' => false]; + } catch (Exception $e) { + if (!$job->isDeleted()) { + $job->release($delay); + } + + throw $e; + } + } + + /** + * Log a failed job into storage. + * @param \Think\Queue\Job $job + * @return array + */ + protected function logFailedJob(Job $job) + { + if (!$job->isDeleted()) { + try { + $job->delete(); + $job->failed(); + } finally { + Hook::listen('queue_failed', $job); + } + } + + return ['job' => $job, 'failed' => true]; + } + + /** + * Sleep the script for a given number of seconds. + * @param int $seconds + * @return void + */ + public function sleep($seconds) + { + sleep($seconds); + } + +} diff --git a/vendor/topthink/think-queue/src/queue/command/Listen.php b/vendor/topthink/think-queue/src/queue/command/Listen.php new file mode 100644 index 0000000..3f6cd63 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/command/Listen.php @@ -0,0 +1,60 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Option; +use think\console\Output; +use think\queue\Listener; + +class Listen extends Command +{ + /** @var Listener */ + protected $listener; + + public function configure() + { + $this->setName('queue:listen') + ->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on', null) + ->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0) + ->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128) + ->addOption('timeout', null, Option::VALUE_OPTIONAL, 'Seconds a job may run before timing out', 60) + ->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Seconds to wait before checking queue for jobs', 3) + ->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0) + ->setDescription('Listen to a given queue'); + } + + public function initialize(Input $input, Output $output) + { + $this->listener = new Listener(getcwd()); + $this->listener->setSleep($input->getOption('sleep')); + $this->listener->setMaxTries($input->getOption('tries')); + + $this->listener->setOutputHandler(function ($type, $line) use ($output) { + $output->write($line); + }); + } + + public function execute(Input $input, Output $output) + { + $delay = $input->getOption('delay'); + + $memory = $input->getOption('memory'); + + $timeout = $input->getOption('timeout'); + + $queue = $input->getOption('queue') ?: 'default'; + + $this->listener->listen($queue, $delay, $memory, $timeout); + } +} diff --git a/vendor/topthink/think-queue/src/queue/command/Restart.php b/vendor/topthink/think-queue/src/queue/command/Restart.php new file mode 100644 index 0000000..6e9336b --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/command/Restart.php @@ -0,0 +1,31 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\command; + +use think\console\Command; +use think\console\Input; +use think\console\Output; +use think\facade\Cache; + +class Restart extends Command +{ + public function configure() + { + $this->setName('queue:restart')->setDescription('Restart queue worker daemons after their current job'); + } + + public function execute(Input $input, Output $output) + { + Cache::set('think:queue:restart', time()); + $output->writeln("Broadcasting queue restart signal."); + } +} diff --git a/vendor/topthink/think-queue/src/queue/command/Subscribe.php b/vendor/topthink/think-queue/src/queue/command/Subscribe.php new file mode 100644 index 0000000..cf266ea --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/command/Subscribe.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\input\Option; +use think\console\Output; +use think\facade\Url; +use think\Queue; + +class Subscribe extends Command +{ + public function configure() + { + $this->setName('queue:subscribe') + ->setDescription('Subscribe a URL to an push queue') + ->addArgument('name', Argument::REQUIRED, 'name') + ->addArgument('url', Argument::REQUIRED, 'The URL to be subscribed.') + ->addArgument('queue', Argument::OPTIONAL, 'The URL to be subscribed.') + ->addOption('option', null, Option::VALUE_IS_ARRAY | Option::VALUE_OPTIONAL, 'the options'); + } + + public function execute(Input $input, Output $output) + { + + $url = $input->getArgument('url'); + if (!preg_match('/^https?:\/\//', $url)) { + $url = Url::build($url); + } + + Queue::subscribe($input->getArgument('name'), $url, $input->getArgument('queue'), $input->getOption('option')); + + $output->write('Queue subscriber added: ' . $input->getArgument('url') . ''); + } +} diff --git a/vendor/topthink/think-queue/src/queue/command/Work.php b/vendor/topthink/think-queue/src/queue/command/Work.php new file mode 100644 index 0000000..06fcc16 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/command/Work.php @@ -0,0 +1,210 @@ + +// +---------------------------------------------------------------------- +namespace think\queue\command; + +use Exception; +use think\console\Command; +use think\console\Input; +use think\console\input\Option; +use think\console\Output; +use think\exception\Handle; +use think\exception\ThrowableError; +use think\facade\Cache; +use think\facade\Config; +use think\facade\Hook; +use think\queue\Job; +use think\queue\Worker; +use Throwable; + +class Work extends Command +{ + + /** + * The queue worker instance. + * @var \think\queue\Worker + */ + protected $worker; + + protected function initialize(Input $input, Output $output) + { + $this->worker = new Worker(); + } + + protected function configure() + { + $this->setName('queue:work') + ->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on') + ->addOption('daemon', null, Option::VALUE_NONE, 'Run the worker in daemon mode') + ->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0) + ->addOption('force', null, Option::VALUE_NONE, 'Force the worker to run even in maintenance mode') + ->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128) + ->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Number of seconds to sleep when no job is available', 3) + ->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0) + ->setDescription('Process the next job on a queue'); + } + + /** + * Execute the console command. + * @param Input $input + * @param Output $output + * @return int|null|void + */ + public function execute(Input $input, Output $output) + { + $queue = $input->getOption('queue'); + + $delay = $input->getOption('delay'); + + $memory = $input->getOption('memory'); + + if ($input->getOption('daemon')) { + Hook::listen('worker_daemon_start', $queue); + $this->daemon( + $queue, $delay, $memory, + $input->getOption('sleep'), $input->getOption('tries') + ); + } else { + $response = $this->worker->pop($queue, $delay, $input->getOption('sleep'), $input->getOption('tries')); + $this->output($response); + } + } + + protected function output($response) + { + if (!is_null($response['job'])) { + /** @var Job $job */ + $job = $response['job']; + if ($response['failed']) { + $this->output->writeln('Failed: ' . $job->getName()); + } else { + $this->output->writeln('Processed: ' . $job->getName()); + } + } + } + + /** + * 启动一个守护进程执行任务. + * + * @param string $queue + * @param int $delay + * @param int $memory + * @param int $sleep + * @param int $maxTries + * @return array + */ + protected function daemon($queue = null, $delay = 0, $memory = 128, $sleep = 3, $maxTries = 0) + { + $lastRestart = $this->getTimestampOfLastQueueRestart(); + + while (true) { + $this->runNextJobForDaemon( + $queue, $delay, $sleep, $maxTries + ); + + if ($this->memoryExceeded($memory)) { + Hook::listen('worker_memory_exceeded', $queue); + $this->stop(); + } + + if ($this->queueShouldRestart($lastRestart)) { + Hook::listen('worker_queue_restart', $queue); + $this->stop(); + } + } + } + + /** + * 以守护进程的方式执行下个任务. + * + * @param string $queue + * @param int $delay + * @param int $sleep + * @param int $maxTries + * @return void + */ + protected function runNextJobForDaemon($queue, $delay, $sleep, $maxTries) + { + try { + $response = $this->worker->pop($queue, $delay, $sleep, $maxTries); + + $this->output($response); + } catch (Exception $e) { + $this->getExceptionHandler()->report($e); + } catch (Throwable $e) { + $this->getExceptionHandler()->report(new ThrowableError($e)); + } + } + + /** + * 获取上次重启守护进程的时间 + * + * @return int|null + */ + protected function getTimestampOfLastQueueRestart() + { + return Cache::get('think:queue:restart'); + } + + /** + * 检查是否要重启守护进程 + * + * @param int|null $lastRestart + * @return bool + */ + protected function queueShouldRestart($lastRestart) + { + return $this->getTimestampOfLastQueueRestart() != $lastRestart; + } + + /** + * 检查内存是否超出 + * @param int $memoryLimit + * @return bool + */ + protected function memoryExceeded($memoryLimit) + { + return (memory_get_usage() / 1024 / 1024) >= $memoryLimit; + } + + /** + * 获取异常处理实例 + * + * @return \think\exception\Handle + */ + protected function getExceptionHandler() + { + static $handle; + + if (!$handle) { + + if ($class = Config::get('exception_handle')) { + if (class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) { + $handle = new $class; + } + } + if (!$handle) { + $handle = new Handle(); + } + } + + return $handle; + } + + /** + * 停止执行任务的守护进程. + * @return void + */ + public function stop() + { + die; + } + +} diff --git a/vendor/topthink/think-queue/src/queue/connector/Database.php b/vendor/topthink/think-queue/src/queue/connector/Database.php new file mode 100644 index 0000000..868ee27 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/connector/Database.php @@ -0,0 +1,169 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\connector; + +use think\Db; +use think\queue\Connector; +use think\queue\job\Database as DatabaseJob; + +class Database extends Connector +{ + + protected $options = [ + 'expire' => 60, + 'default' => 'default', + 'table' => 'jobs' + ]; + + public function __construct(array $options) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + } + + public function push($job, $data = '', $queue = null) + { + return $this->pushToDatabase(0, $queue, $this->createPayload($job, $data)); + } + + public function later($delay, $job, $data = '', $queue = null) + { + return $this->pushToDatabase($delay, $queue, $this->createPayload($job, $data)); + } + + public function pop($queue = null) + { + $queue = $this->getQueue($queue); + + if (!is_null($this->options['expire'])) { + $this->releaseJobsThatHaveBeenReservedTooLong($queue); + } + + if ($job = $this->getNextAvailableJob($queue)) { + $this->markJobAsReserved($job->id); + + Db::commit(); + + return new DatabaseJob($this, $job, $queue); + } + + Db::commit(); + } + + /** + * 重新发布任务 + * + * @param string $queue + * @param \StdClass $job + * @param int $delay + * @return mixed + */ + public function release($queue, $job, $delay) + { + return $this->pushToDatabase($delay, $queue, $job->payload, $job->attempts); + } + + /** + * Push a raw payload to the database with a given delay. + * + * @param \DateTime|int $delay + * @param string|null $queue + * @param string $payload + * @param int $attempts + * @return mixed + */ + protected function pushToDatabase($delay, $queue, $payload, $attempts = 0) + { + return Db::name($this->options['table'])->insert([ + 'queue' => $this->getQueue($queue), + 'payload' => $payload, + 'attempts' => $attempts, + 'reserved' => 0, + 'reserved_at' => null, + 'available_at' => time() + $delay, + 'created_at' => time(), + ]); + } + + /** + * 获取下个有效任务 + * + * @param string|null $queue + * @return \StdClass|null + */ + protected function getNextAvailableJob($queue) + { + Db::startTrans(); + + $job = Db::name($this->options['table']) + ->lock(true) + ->where('queue', $this->getQueue($queue)) + ->where('reserved', 0) + ->where('available_at', '<=', time()) + ->order('id', 'asc') + ->find(); + + return $job ? (object) $job : null; + } + + /** + * 标记任务正在执行. + * + * @param string $id + * @return void + */ + protected function markJobAsReserved($id) + { + Db::name($this->options['table'])->where('id', $id)->update([ + 'reserved' => 1, + 'reserved_at' => time(), + ]); + } + + /** + * 重新发布超时的任务 + * + * @param string $queue + * @return void + */ + protected function releaseJobsThatHaveBeenReservedTooLong($queue) + { + $expired = time() - $this->options['expire']; + + Db::name($this->options['table']) + ->where('queue', $this->getQueue($queue)) + ->where('reserved', 1) + ->where('reserved_at', '<=', $expired) + ->update([ + 'reserved' => 0, + 'reserved_at' => null, + 'attempts' => ['inc', 1], + ]); + } + + /** + * 删除任务 + * + * @param string $id + * @return void + */ + public function deleteReserved($id) + { + Db::name($this->options['table'])->delete($id); + } + + protected function getQueue($queue) + { + return $queue ?: $this->options['default']; + } +} diff --git a/vendor/topthink/think-queue/src/queue/connector/Redis.php b/vendor/topthink/think-queue/src/queue/connector/Redis.php new file mode 100644 index 0000000..c3b2b56 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/connector/Redis.php @@ -0,0 +1,236 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\connector; + +use Exception; +use think\helper\Str; +use think\queue\Connector; +use think\queue\job\Redis as RedisJob; + +class Redis extends Connector +{ + /** @var \Redis */ + protected $redis; + + protected $options = [ + 'expire' => 60, + 'default' => 'default', + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'select' => 0, + 'timeout' => 0, + 'persistent' => false, + ]; + + public function __construct(array $options) + { + if (!extension_loaded('redis')) { + throw new Exception('redis扩展未安装'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + + $func = $this->options['persistent'] ? 'pconnect' : 'connect'; + $this->redis = new \Redis; + $this->redis->$func($this->options['host'], $this->options['port'], $this->options['timeout']); + + if ('' != $this->options['password']) { + $this->redis->auth($this->options['password']); + } + + if (0 != $this->options['select']) { + $this->redis->select($this->options['select']); + } + } + + public function push($job, $data = '', $queue = null) + { + return $this->pushRaw($this->createPayload($job, $data), $queue); + } + + public function later($delay, $job, $data = '', $queue = null) + { + $payload = $this->createPayload($job, $data); + + $this->redis->zAdd($this->getQueue($queue) . ':delayed', time() + $delay, $payload); + } + + public function pop($queue = null) + { + $original = $queue ?: $this->options['default']; + + $queue = $this->getQueue($queue); + + $this->migrateExpiredJobs($queue . ':delayed', $queue, false); + + if (!is_null($this->options['expire'])) { + $this->migrateExpiredJobs($queue . ':reserved', $queue); + } + + $job = $this->redis->lPop($queue); + + if (false !== $job) { + $this->redis->zAdd($queue . ':reserved', time() + $this->options['expire'], $job); + + return new RedisJob($this, $job, $original); + } + } + + /** + * 重新发布任务 + * + * @param string $queue + * @param string $payload + * @param int $delay + * @param int $attempts + * @return void + */ + public function release($queue, $payload, $delay, $attempts) + { + $payload = $this->setMeta($payload, 'attempts', $attempts); + + $this->redis->zAdd($this->getQueue($queue) . ':delayed', time() + $delay, $payload); + } + + public function pushRaw($payload, $queue = null) + { + $this->redis->rPush($this->getQueue($queue), $payload); + + return json_decode($payload, true)['id']; + } + + protected function createPayload($job, $data = '', $queue = null) + { + $payload = $this->setMeta( + parent::createPayload($job, $data), 'id', $this->getRandomId() + ); + + return $this->setMeta($payload, 'attempts', 1); + } + + /** + * 删除任务 + * + * @param string $queue + * @param string $job + * @return void + */ + public function deleteReserved($queue, $job) + { + $this->redis->zRem($this->getQueue($queue) . ':reserved', $job); + } + + /** + * 移动延迟任务 + * + * @param string $from + * @param string $to + * @param bool $attempt + */ + public function migrateExpiredJobs($from, $to, $attempt = true) + { + $this->redis->watch($from); + + $jobs = $this->getExpiredJobs( + $from, $time = time() + ); + if (count($jobs) > 0) { + $this->transaction(function () use ($from, $to, $time, $jobs, $attempt) { + $this->removeExpiredJobs($from, $time); + $this->pushExpiredJobsOntoNewQueue($to, $jobs, $attempt); + }); + } + $this->redis->unwatch(); + } + + /** + * redis事务 + * @param \Closure $closure + */ + protected function transaction(\Closure $closure) + { + $this->redis->multi(); + try { + call_user_func($closure); + if (!$this->redis->exec()) { + $this->redis->discard(); + } + } catch (Exception $e) { + $this->redis->discard(); + } + } + + /** + * 获取所有到期任务 + * + * @param string $from + * @param int $time + * @return array + */ + protected function getExpiredJobs($from, $time) + { + return $this->redis->zRangeByScore($from, '-inf', $time); + } + + /** + * 删除过期任务 + * + * @param string $from + * @param int $time + * @return void + */ + protected function removeExpiredJobs($from, $time) + { + $this->redis->zRemRangeByScore($from, '-inf', $time); + } + + /** + * 重新发布到期任务 + * + * @param string $to + * @param array $jobs + * @param boolean $attempt + */ + protected function pushExpiredJobsOntoNewQueue($to, $jobs, $attempt = true) + { + if ($attempt) { + foreach ($jobs as &$job) { + $attempts = json_decode($job, true)['attempts']; + $job = $this->setMeta($job, 'attempts', $attempts + 1); + } + } + call_user_func_array([$this->redis, 'rPush'], array_merge([$to], $jobs)); + } + + /** + * 随机id + * + * @return string + */ + protected function getRandomId() + { + return Str::random(32); + } + + /** + * 获取队列名 + * + * @param string|null $queue + * @return string + */ + protected function getQueue($queue) + { + return 'queues:' . ($queue ?: $this->options['default']); + } +} diff --git a/vendor/topthink/think-queue/src/queue/connector/Sync.php b/vendor/topthink/think-queue/src/queue/connector/Sync.php new file mode 100644 index 0000000..2fbdeda --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/connector/Sync.php @@ -0,0 +1,57 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\connector; + +use Exception; +use think\queue\Connector; +use think\queue\job\Sync as SyncJob; +use Throwable; + +class Sync extends Connector +{ + + public function push($job, $data = '', $queue = null) + { + $queueJob = $this->resolveJob($this->createPayload($job, $data, $queue)); + + try { + set_time_limit(0); + $queueJob->fire(); + } catch (Exception $e) { + $queueJob->failed(); + + throw $e; + } catch (Throwable $e) { + $queueJob->failed(); + + throw $e; + } + + return 0; + } + + public function later($delay, $job, $data = '', $queue = null) + { + return $this->push($job, $data, $queue); + } + + public function pop($queue = null) + { + + } + + protected function resolveJob($payload) + { + return new SyncJob($payload); + } + +} diff --git a/vendor/topthink/think-queue/src/queue/connector/Topthink.php b/vendor/topthink/think-queue/src/queue/connector/Topthink.php new file mode 100644 index 0000000..732da24 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/connector/Topthink.php @@ -0,0 +1,223 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\connector; + +use think\exception\HttpException; +use think\facade\Request; +use think\queue\Connector; +use think\queue\job\Topthink as TopthinkJob; +use think\Response; + +class Topthink extends Connector +{ + protected $options = [ + 'token' => '', + 'project_id' => '', + 'protocol' => 'https', + 'host' => 'qns.topthink.com', + 'port' => 443, + 'api_version' => 1, + 'max_retries' => 3, + 'default' => 'default', + ]; + + /** @var Request */ + protected $request; + + protected $url; + + protected $curl = null; + + protected $last_status; + + protected $headers = []; + + public function __construct(array $options) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + + $this->url = "{$this->options['protocol']}://{$this->options['host']}:{$this->options['port']}/v{$this->options['api_version']}/"; + + $this->headers['Authorization'] = "Bearer {$this->options['token']}"; + } + + public function push($job, $data = '', $queue = null) + { + return $this->pushRaw(0, $queue, $this->createPayload($job, $data)); + } + + public function later($delay, $job, $data = '', $queue = null) + { + return $this->pushRaw($delay, $queue, $this->createPayload($job, $data)); + } + + public function release($queue, $job, $delay) + { + return $this->pushRaw($delay, $queue, $job->payload, $job->attempts); + } + + public function marshal() + { + $job = new TopthinkJob($this, $this->marshalPushedJob(), Request::header('topthink-message-queue')); + if (Request::header('topthink-message-status') == 'success') { + $job->fire(); + } else { + $job->failed(); + } + return new Response('OK'); + } + + public function pushRaw($delay, $queue, $payload, $attempts = 0) + { + $queue_name = $this->getQueue($queue); + $queue = rawurlencode($queue_name); + $url = "project/{$this->options['project_id']}/queue/{$queue}/message"; + $message = [ + 'payload' => $payload, + 'attempts' => $attempts, + 'delay' => $delay, + ]; + + return $this->apiCall('POST', $url, $message)->id; + } + + public function deleteMessage($queue, $id) + { + $queue = rawurlencode($queue); + $url = "project/{$this->options['project_id']}/queue/{$queue}/message/{$id}"; + return $this->apiCall('DELETE', $url); + } + + protected function apiCall($type, $url, $params = []) + { + $url = "{$this->url}$url"; + + if (null == $this->curl) { + $this->curl = curl_init(); + } + + switch ($type = strtoupper($type)) { + case 'DELETE': + curl_setopt($this->curl, CURLOPT_URL, $url); + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $type); + curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($params)); + break; + case 'PUT': + curl_setopt($this->curl, CURLOPT_URL, $url); + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $type); + curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($params)); + break; + case 'POST': + curl_setopt($this->curl, CURLOPT_URL, $url); + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $type); + curl_setopt($this->curl, CURLOPT_POST, true); + curl_setopt($this->curl, CURLOPT_POSTFIELDS, $params); + break; + case 'GET': + curl_setopt($this->curl, CURLOPT_POSTFIELDS, null); + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $type); + curl_setopt($this->curl, CURLOPT_HTTPGET, true); + $url .= '?' . http_build_query($params); + curl_setopt($this->curl, CURLOPT_URL, $url); + break; + } + + curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); + + $headers = []; + foreach ($this->headers as $k => $v) { + if ('Connection' == $k) { + $v = 'Close'; + } + $headers[] = "$k: $v"; + } + + curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, 10); + + return $this->callWithRetries(); + } + + protected function callWithRetries() + { + for ($retry = 0; $retry < $this->options['max_retries']; $retry++) { + $out = curl_exec($this->curl); + if (false === $out) { + $this->reportHttpError(0, curl_error($this->curl)); + } + $this->last_status = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); + + if ($this->last_status >= 200 && $this->last_status < 300) { + return self::jsonDecode($out); + } elseif ($this->last_status >= 500) { + self::waitRandomInterval($retry); + } else { + $this->reportHttpError($this->last_status, $out); + } + } + $this->reportHttpError($this->last_status, "Service unavailable"); + return; + } + + protected static function jsonDecode($response) + { + $data = json_decode($response); + + $json_error = json_last_error(); + if (JSON_ERROR_NONE != $json_error) { + throw new \RuntimeException($json_error); + } + + return $data; + } + + protected static function waitRandomInterval($retry) + { + $max_delay = pow(4, $retry) * 100 * 1000; + usleep(rand(0, $max_delay)); + } + + protected function reportHttpError($status, $text) + { + throw new HttpException($status, "http error: {$status} | {$text}"); + } + + /** + * Marshal out the pushed job and payload. + * + * @return object + */ + protected function marshalPushedJob() + { + return (object) [ + 'id' => Request::header('topthink-message-id'), + 'payload' => Request::getContent(), + 'attempts' => Request::header('topthink-message-attempts'), + ]; + } + + public function __destruct() + { + if (null != $this->curl) { + curl_close($this->curl); + $this->curl = null; + } + } + + public function pop($queue = null) + { + throw new \RuntimeException('pop queues not support for this type'); + } +} diff --git a/vendor/topthink/think-queue/src/queue/job/Database.php b/vendor/topthink/think-queue/src/queue/job/Database.php new file mode 100644 index 0000000..06064b3 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/job/Database.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace think\queue\job; + +use think\queue\Job; +use think\queue\connector\Database as DatabaseQueue; + +class Database extends Job +{ + /** + * The database queue instance. + * @var DatabaseQueue + */ + protected $database; + + /** + * The database job payload. + * @var Object + */ + protected $job; + + public function __construct(DatabaseQueue $database, $job, $queue) + { + $this->job = $job; + $this->queue = $queue; + $this->database = $database; + $this->job->attempts = $this->job->attempts + 1; + } + + /** + * 执行任务 + * @return void + */ + public function fire() + { + $this->resolveAndFire(json_decode($this->job->payload, true)); + } + + /** + * 删除任务 + * @return void + */ + public function delete() + { + parent::delete(); + $this->database->deleteReserved($this->job->id); + } + + /** + * 重新发布任务 + * @param int $delay + * @return void + */ + public function release($delay = 0) + { + parent::release($delay); + + $this->delete(); + + $this->database->release($this->queue, $this->job, $delay); + } + + /** + * 获取当前任务尝试次数 + * @return int + */ + public function attempts() + { + return (int) $this->job->attempts; + } + + /** + * Get the raw body string for the job. + * @return string + */ + public function getRawBody() + { + return $this->job->payload; + } +} diff --git a/vendor/topthink/think-queue/src/queue/job/Redis.php b/vendor/topthink/think-queue/src/queue/job/Redis.php new file mode 100644 index 0000000..10477ce --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/job/Redis.php @@ -0,0 +1,92 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\job; + +use think\queue\Job; +use think\queue\connector\Redis as RedisQueue; + +class Redis extends Job +{ + + /** + * The redis queue instance. + * @var RedisQueue + */ + protected $redis; + + /** + * The database job payload. + * @var Object + */ + protected $job; + + public function __construct(RedisQueue $redis, $job, $queue) + { + $this->job = $job; + $this->queue = $queue; + $this->redis = $redis; + } + + /** + * Fire the job. + * @return void + */ + public function fire() + { + $this->resolveAndFire(json_decode($this->getRawBody(), true)); + } + + /** + * Get the number of times the job has been attempted. + * @return int + */ + public function attempts() + { + return json_decode($this->job, true)['attempts']; + } + + /** + * Get the raw body string for the job. + * @return string + */ + public function getRawBody() + { + return $this->job; + } + + /** + * 删除任务 + * + * @return void + */ + public function delete() + { + parent::delete(); + + $this->redis->deleteReserved($this->queue, $this->job); + } + + /** + * 重新发布任务 + * + * @param int $delay + * @return void + */ + public function release($delay = 0) + { + parent::release($delay); + + $this->delete(); + + $this->redis->release($this->queue, $this->job, $delay, $this->attempts() + 1); + } +} diff --git a/vendor/topthink/think-queue/src/queue/job/Sync.php b/vendor/topthink/think-queue/src/queue/job/Sync.php new file mode 100644 index 0000000..e81a758 --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/job/Sync.php @@ -0,0 +1,56 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\job; + +use think\queue\Job; + +class Sync extends Job +{ + /** + * The queue message data. + * + * @var string + */ + protected $payload; + + public function __construct($payload) + { + $this->payload = $payload; + } + + /** + * Fire the job. + * @return void + */ + public function fire() + { + $this->resolveAndFire(json_decode($this->payload, true)); + } + + /** + * Get the number of times the job has been attempted. + * @return int + */ + public function attempts() + { + return 1; + } + + /** + * Get the raw body string for the job. + * @return string + */ + public function getRawBody() + { + return $this->payload; + } +} diff --git a/vendor/topthink/think-queue/src/queue/job/Topthink.php b/vendor/topthink/think-queue/src/queue/job/Topthink.php new file mode 100644 index 0000000..b98b59c --- /dev/null +++ b/vendor/topthink/think-queue/src/queue/job/Topthink.php @@ -0,0 +1,85 @@ + +// +---------------------------------------------------------------------- + +namespace think\queue\job; + +use think\queue\Job; +use think\queue\connector\Topthink as TopthinkQueue; + +class Topthink extends Job +{ + + /** + * The Iron queue instance. + * + * @var TopthinkQueue + */ + protected $topthink; + + /** + * The IronMQ message instance. + * + * @var object + */ + protected $job; + + public function __construct(TopthinkQueue $topthink, $job, $queue) + { + $this->topthink = $topthink; + $this->job = $job; + $this->queue = $queue; + $this->job->attempts = $this->job->attempts + 1; + } + + /** + * Fire the job. + * @return void + */ + public function fire() + { + $this->resolveAndFire(json_decode($this->job->payload, true)); + } + + /** + * Get the number of times the job has been attempted. + * @return int + */ + public function attempts() + { + return (int) $this->job->attempts; + } + + public function delete() + { + parent::delete(); + + $this->topthink->deleteMessage($this->queue, $this->job->id); + } + + public function release($delay = 0) + { + parent::release($delay); + + $this->delete(); + + $this->topthink->release($this->queue, $this->job, $delay); + } + + /** + * Get the raw body string for the job. + * @return string + */ + public function getRawBody() + { + return $this->job->payload; + } + +} diff --git a/vendor/topthink/think-swoole/.gitignore b/vendor/topthink/think-swoole/.gitignore new file mode 100644 index 0000000..108d490 --- /dev/null +++ b/vendor/topthink/think-swoole/.gitignore @@ -0,0 +1,4 @@ + +.DS_Store +*.xml +.idea/think-swoole.iml diff --git a/vendor/topthink/think-swoole/LICENSE b/vendor/topthink/think-swoole/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/vendor/topthink/think-swoole/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/topthink/think-swoole/README.md b/vendor/topthink/think-swoole/README.md new file mode 100644 index 0000000..085aa82 --- /dev/null +++ b/vendor/topthink/think-swoole/README.md @@ -0,0 +1,482 @@ +ThinkPHP 5.1 Swoole 扩展 +=============== + +## 安装 + +首先按照Swoole官网说明安装swoole扩展,然后使用 +~~~ +composer require topthink/think-swoole +~~~ +安装swoole扩展。 + +## 使用方法 + +### HttpServer + +直接在命令行下启动服务端。 + +~~~ +php think swoole +~~~ + +启动完成后,会在0.0.0.0:9501启动一个HTTP Server,可以直接访问当前的应用。 + +swoole的参数可以在应用配置目录下的swoole.php里面配置(具体参考配置文件内容)。 + +如果需要使用守护进程方式运行,可以使用 +~~~ +php think swoole -d +~~~ +或者在swoole.php文件中设置 +~~~ +'daemonize' => true +~~~ + +注意:由于onWorkerStart运行的时候没有HTTP_HOST,因此最好在应用配置文件中设置app_host + +支持的操作包括 +~~~ +php think swoole [start|stop|reload|restart] +~~~ + +### Server + +可以支持直接启动一个Swoole server + +~~~ +php think swoole:server +~~~ +会在0.0.0.0:9508启动一个Websocket服务。 + +如果需要自定义参数,可以在config/swoole_server.php中进行配置,包括: + +配置参数 | 描述 +--- | --- +type| 服务类型 +host | 监听地址 +port | 监听端口 +mode | 运行模式 +sock_type | Socket type + + +并且支持swoole所有的参数。 +也支持使用闭包方式定义相关事件回调。 + +~~~ +return [ + // 扩展自身配置 + 'host' => '0.0.0.0', // 监听地址 + 'port' => 9501, // 监听端口 + 'type' => 'socket', // 服务类型 支持 socket http server + 'mode' => SWOOLE_PROCESS, + 'sock_type' => SWOOLE_SOCK_TCP, + + // 可以支持swoole的所有配置参数 + 'daemonize' => false, + + // 事件回调定义 + 'onOpen' => function ($server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }, + + 'onMessage' => function ($server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }, + + 'onRequest' => function ($request, $response) { + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }, + + 'onClose' => function ($ser, $fd) { + echo "client {$fd} closed\n"; + }, +]; +~~~ + +也可以使用自定义的服务类 + +~~~ + 4, + 'daemonize' => true, + 'backlog' => 128 + ]; + + public function onReceive($server, $fd, $from_id, $data) + { + $server->send($fd, 'Swoole: '.$data); + } +} +~~~ + +支持swoole所有的回调方法定义(回调方法必须是public类型) +serverType 属性定义为 socket或者http 则支持swoole的swoole_websocket_server和swoole_http_server + +然后在swoole_server.php中增加配置参数: +~~~ +return [ + 'swoole_class' => 'app\http\Swoole', +]; +~~~ + +定义该参数后,其它配置参数均不再有效。 + +在命令行启动服务端 +~~~ +php think swoole:server +~~~ + + +支持reload|restart|stop|status 操作 +~~~ +php think swoole:server reload +~~~ + + +### 配置信息详解 + +swoole.php + +```php + +// +---------------------------------------------------------------------- +use think\facade\Env; +// +---------------------------------------------------------------------- +// | Swoole设置 php think swoole命令行下有效 +// +---------------------------------------------------------------------- +return [ + // 扩展自身配置 + 'host' => '0.0.0.0', // 监听地址 + 'port' => 9501, // 监听端口 + 'mode' => '', // 运行模式 默认为SWOOLE_PROCESS + 'sock_type' => '', // sock type 默认为SWOOLE_SOCK_TCP + 'app_path' => '', // 应用地址 如果开启了 'daemonize'=>true 必须设置(使用绝对路径) + 'file_monitor' => false, // 是否开启PHP文件更改监控(调试模式下自动开启) + 'file_monitor_interval' => 2, // 文件变化监控检测时间间隔(秒) + 'file_monitor_path' => [], // 文件监控目录 默认监控application和config目录 + // 可以支持swoole的所有配置参数 + 'pid_file' => Env::get('runtime_path') . 'swoole.pid',//swoole主进程pid存放文件 + 'log_file' => Env::get('runtime_path') . 'swoole.log',//swoole日志存放文件 + 'document_root' => Env::get('root_path') . 'public',//设置静态服务根目录 + 'enable_static_handler' => true,//是否由SWOOLE底层自动处理静态文件,TRUE表示SWOOLE判断是否存在静态文件,如果存在则直接返回静态文件信息 + 'timer' => true,//是否开启系统定时器 + 'interval' => 500,//系统定时器 时间间隔 + 'task_worker_num' => 1,//swoole 任务工作进程数量 + 'user' =>'www',//表示swoole worker进程所属的管理员名称,如果要绑定1024以下端口则必须要求具有root权限,如果设置了该项,则除主进程外的所有进程都运行于指定用户下 +]; +``` + +timer.php + +```php + "类" + * *中间一个空格 + * 系统定时任务需要在swoole.php中开启 + * 自定义定时器不受其影响 + */ +return [ + '*/5 * * * * *' => '\\app\\lib\\Timer',//时间配置方式参考crontab,主要是增加秒,其他和crontab一致,对应Value为定时器接口实现类的完整命名空间(包含类名) +]; +``` + +### 异步任务投递 + +1.异步任务接口实现 + +```php +function($server, $worker_id){ + //如果只在一个进程处理 则可以这样 + + if (0==$worker_id){ + //这样只会在第一个woker进程处理 + } +} +``` + +如果需要实现Workerstart的接口,可以这样实现 + +```php +'\\app\\lib\\WorkStart' +``` + +### Websocket支持 +需要在swoole.php修改 + +```a +'server_type' => 'websocket', // 服务类型 支持 http websocket +``` + +由于Swoole\WebSocket\Server继承于Swoole\Http\Server顾Http服务具有的功能Websocket也具有。如果采用如下websocket服务, +通讯数据格式已经固定,如采用自定义数据结构,请采用自定义服务 + +WebSocket通讯数据结构 +```json +{ + "event":"",//事件名称 + "url":"",//目标地址 + "arguments":{//客户端投递数据 + "post":[],//post数据 + "get":[],//get数据 + "cookie":[],//cookie数据 + } +} +``` +详解 + +* event 该参数为事件名称,用于触发客户端事件,即接收服务器推送数据处理指定任务 + +* url,该为访问的目标地址,相当于http模式下http://xxx.com/url + +* arguments 客户端投递的所有数据,如果希望可以按照原来POST,GET的方式处理数据,可以提交时按照上述方式提交,会自动处理 + +客户端JS可以参考example里websocketclient.js + + +服务端发送数据 + +```php +use think\swoole\WebSocketFrame; + +$client=WebSocketFrame::getInstance(); +//发送数据给当前请求的客户端 +$client->pushToClient([]);//参数为数组,字符串,数字 +//发送给所有客户端 +$client->pushToClients([]);//参数为数组,字符串,数字 + +$client->getArgs();//获取arguments里的参数 + +$client->getData();//获取客户端发送给的所有数据 + +$client->getServer();//获取当前server + +$client->getFrame();//获取当前客户端给发送的原始数据 +``` + + +新增队列支持 + +think-queue,是一个非常好用的队列服务,xavier-swoole的队列服务依赖于think-queue,使用方法和think-queue保持一致 + +主要增加如下功能 + +1. 采用多进程模式运行或采用Task异步执行 +2. 可以指定同时消耗队列任务的进程数量 +3. Task模式和Server共享Task进程,充分利用资源 +4. Process模式采用进程模式,各个任务之间相互隔离,执行一定次数后,重启进程,防止内存泄露 + + +如果任务较多且复杂,推荐采用Process模式 + +```php +'queue_type'=>'task',//task or process + 'queue'=>[ + "Index"=>[ + "delay"=>0,//延迟时间 + "sleep"=>3,//休息时间 + "maxTries"=>0,//重试次数 + "nums"=>2//进程数量 + ], + ] +``` + diff --git a/vendor/topthink/think-swoole/composer.json b/vendor/topthink/think-swoole/composer.json new file mode 100644 index 0000000..e8f309f --- /dev/null +++ b/vendor/topthink/think-swoole/composer.json @@ -0,0 +1,35 @@ +{ + "name": "topthink/think-swoole", + "description": "Swoole extend for thinkphp5.1", + "license": "Apache-2.0", + "type": "think-extend", + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "require": { + "ext-swoole": ">=1.9.5", + "topthink/think-installer": "^2.0", + "topthink/framework": "~5.1.20", + "jeremeamia/superclosure": "^2.4", + "xavier/xcron-expression": "^0.11", + "topthink/think-queue": "^2.0" + }, + "autoload": { + "psr-4": { + "think\\swoole\\": "src" + }, + "files": [ + "src/command.php" + ] + }, + "extra": { + "think-config": { + "swoole": "src/config/swoole.php", + "swoole_server": "src/config/server.php", + "timer": "src/config/timer.php" + } + } +} \ No newline at end of file diff --git a/vendor/topthink/think-swoole/src/Application.php b/vendor/topthink/think-swoole/src/Application.php new file mode 100644 index 0000000..736c99d --- /dev/null +++ b/vendor/topthink/think-swoole/src/Application.php @@ -0,0 +1,218 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole; + +use Swoole\Http\Request; +use Swoole\Http\Response; +use think\App; +use think\Db; +use think\Error; +use think\exception\HttpException; +use think\facade\Config; + +/** + * Swoole应用对象 + */ +class Application extends App +{ + /** + * 处理Swoole请求 + * @access public + * @param \Swoole\Http\Request $request + * @param \Swoole\Http\Response $response + * @param void + */ + public function swoole(Request $request, Response $response) + { + try { + ob_start(); + + // 重置应用的开始时间和内存占用 + $this->beginTime = microtime(true); + $this->beginMem = memory_get_usage(); + + // 重置数据库查询次数 + Db::$queryTimes = 0; + + // 重置数据库执行次数 + Db::$executeTimes = 0; + + // 销毁当前请求对象实例 + $this->delete('think\Request'); + + // 设置Cookie类Response + $this->cookie->setResponse($response); + + $_COOKIE = $request->cookie ?: []; + $_GET = $request->get ?: []; + $_POST = $request->post ?: []; + $_FILES = $request->files ?: []; + $header = $request->header ?: []; + $server = $request->server ?: []; + + if (isset($header['x-requested-with'])) { + $server['HTTP_X_REQUESTED_WITH'] = $header['x-requested-with']; + } + + if (isset($header['referer'])) { + $server['http_referer'] = $header['referer']; + } + + if (isset($_GET[$this->config->get('var_pathinfo')])) { + $server['path_info'] = $_GET[$this->config->get('var_pathinfo')]; + } + + $_SERVER = array_change_key_case($server, CASE_UPPER); + + // 重新实例化请求对象 处理swoole请求数据 + $this->request->withHeader($header) + ->withServer($_SERVER) + ->withGet($_GET) + ->withPost($_POST) + ->withCookie($_COOKIE) + ->withInput($request->rawContent()) + ->withFiles($_FILES) + ->setBaseUrl($request->server['request_uri']) + ->setUrl($request->server['request_uri'] . (!empty($request->server['query_string']) ? '&' . $request->server['query_string'] : '')) + ->setHost($request->header['host']) + ->setPathinfo(ltrim($request->server['path_info'], '/')); + + // 更新请求对象实例 + $this->route->setRequest($this->request); + + // 重新加载全局中间件 + if (is_file($this->appPath . 'middleware.php')) { + $middleware = include $this->appPath . 'middleware.php'; + if (is_array($middleware)) { + $this->middleware->import($middleware); + } + } + + $resp = $this->run(); + $resp->send(); + + $content = ob_get_clean(); + $status = $resp->getCode(); + + // Trace调试注入 + if ($this->env->get('app_trace', $this->config->get('app_trace'))) { + $this->debug->inject($resp, $content); + } + + // 清除中间件数据 + $this->middleware->clear(); + + // 发送状态码 + $response->status($status); + + // 发送Header + foreach ($resp->getHeader() as $key => $val) { + $response->header($key, $val); + } + + $response->write($content); + $response->end(); + } catch (HttpException $e) { + $this->exception($response, $e); + } catch (\Exception $e) { + $this->exception($response, $e); + } catch (\Throwable $e) { + $this->exception($response, $e); + } + } + + public function swooleWebSocket($server, $frame) + { + try { + // 重置应用的开始时间和内存占用 + $this->beginTime = microtime(true); + $this->beginMem = memory_get_usage(); + + // 销毁当前请求对象实例 + $this->delete('think\Request'); + WebSocketFrame::destroy(); + $request = $frame->data; + $request = json_decode($request, true); + + // 重置应用的开始时间和内存占用 + $this->beginTime = microtime(true); + $this->beginMem = memory_get_usage(); + WebSocketFrame::getInstance($server, $frame); + + $_COOKIE = isset($request['arguments']['cookie']) ? $request['arguments']['cookie'] : []; + $_GET = isset($request['arguments']['get']) ? $request['arguments']['get'] : []; + $_POST = isset($request['arguments']['post']) ? $request['arguments']['post'] : []; + $_FILES = isset($request['arguments']['files']) ? $request['arguments']['files'] : []; + + $_SERVER["PATH_INFO"] = $request['url'] ?: '/'; + $_SERVER["REQUEST_URI"] = $request['url'] ?: '/'; + $_SERVER["SERVER_PROTOCOL"] = 'http'; + $_SERVER["REQUEST_METHOD"] = 'post'; + + // 重新实例化请求对象 处理swoole请求数据 + $this->request + ->withServer($_SERVER) + ->withGet($_GET) + ->withPost($_POST) + ->withCookie($_COOKIE) + ->withFiles($_FILES) + ->setBaseUrl($request['url']) + ->setUrl($request['url']) + ->setHost(Config::get("app_host")) + ->setPathinfo(ltrim($request['url'], '/')); + + // 更新请求对象实例 + $this->route->setRequest($this->request); + + $resp = $this->run(); + $resp->send(); + + } catch (HttpException $e) { + $this->webSocketException($server, $frame, $e); + } catch (\Exception $e) { + $this->webSocketException($server, $frame, $e); + } catch (\Throwable $e) { + $this->webSocketException($server, $frame, $e); + } + } + + protected function exception($response, $e) + { + if ($e instanceof \Exception) { + $handler = Error::getExceptionHandler(); + $handler->report($e); + + $resp = $handler->render($e); + $content = $resp->getContent(); + $code = $resp->getCode(); + + $response->status($code); + $response->end($content); + } else { + $response->status(500); + $response->end($e->getMessage()); + } + + throw $e; + } + + protected function webSocketException($server, $frame, $e) + { + $response = [ + 'code' => $e->getCode(), + 'content' => $e->getMessage(), + ]; + $server->push($frame->fd, json_encode($response)); + + throw $e; + } +} diff --git a/vendor/topthink/think-swoole/src/CacheTable.php b/vendor/topthink/think-swoole/src/CacheTable.php new file mode 100644 index 0000000..9f6f12f --- /dev/null +++ b/vendor/topthink/think-swoole/src/CacheTable.php @@ -0,0 +1,93 @@ +table = new \swoole_table($cache_size); + $this->table->column('time', \swoole_table::TYPE_INT, 15); + $this->table->column('data', \swoole_table::TYPE_STRING, $cache_data_size); + $this->table->create(); + } + + public function getTable() + { + return $this->table; + } + + public function set($key, $value) + { + $this->table->set($key, ['time' => 0, 'data' => $value]); + } + + public function setex($key, $expire, $value) + { + $this->table->set($key, ['time' => time() + $expire, 'data' => $value]); + } + + public function incr($key, $column, $incrby = 1) + { + $this->table->incr($key, $column, $incrby); + } + + public function decr($key, $column, $decrby = 1) + { + $this->table->decr($key, $column, $decrby); + } + + public function get($key, $field = null) + { + $data = $this->table->get($key, $field); + if (false == $data) { + return $data; + } + if (0 == $data['time']) { + return $data['data']; + } + if (0 <= $data['time'] && $data['time'] < time()) { + $this->del($key); + return false; + } + return $data['data']; + } + + public function exist($key) + { + return $this->table->exist($key); + } + + public function del($key) + { + return $this->table->del($key); + } + + public function clear() + { + foreach ($this->table as $key => $val) { + $this->del($key); + } + } +} diff --git a/vendor/topthink/think-swoole/src/Cookie.php b/vendor/topthink/think-swoole/src/Cookie.php new file mode 100644 index 0000000..8745a8e --- /dev/null +++ b/vendor/topthink/think-swoole/src/Cookie.php @@ -0,0 +1,52 @@ + +// +---------------------------------------------------------------------- +namespace think\swoole; + +use Swoole\Http\Response; +use think\Cookie as BaseCookie; + +/** + * Swoole Cookie类 + */ +class Cookie extends BaseCookie +{ + protected $response; + + /** + * Cookie初始化 + * @access public + * @param array $config + * @return void + */ + public function init(array $config = []) + { + $this->config = array_merge($this->config, array_change_key_case($config)); + } + + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Cookie 设置保存 + * + * @access public + * @param string $name cookie名称 + * @param mixed $value cookie值 + * @param array $option 可选参数 + * @return void + */ + protected function setCookie($name, $value, $expire, $option = []) + { + $this->response->cookie($name, $value, $expire, $option['path'], $option['domain'], $option['secure'], $option['httponly']); + } +} diff --git a/vendor/topthink/think-swoole/src/Http.php b/vendor/topthink/think-swoole/src/Http.php new file mode 100644 index 0000000..58ce1f8 --- /dev/null +++ b/vendor/topthink/think-swoole/src/Http.php @@ -0,0 +1,340 @@ + +// +---------------------------------------------------------------------- +namespace think\swoole; + +use Swoole\Http\Server as HttpServer; +use Swoole\Table; +use think\Facade; +use think\facade\Config; +use think\Loader; +use think\swoole\facade\Timer as TimerF; +use think\Container; +use think\swoole\queue\Task as QueueTask; +use think\swoole\queue\Process as QueueProcess; +use Swoole\WebSocket\Server as WebSocketServer; + +/** + * Swoole Http Server 命令行服务类 + */ +class Http extends Server +{ + protected $app; + protected $appPath; + protected $table; + protected $cachetable; + protected $monitor; + protected $server_type; + protected $lastMtime; + protected $fieldType = [ + 'int' => Table::TYPE_INT, + 'string' => Table::TYPE_STRING, + 'float' => Table::TYPE_FLOAT, + ]; + + protected $fieldSize = [ + Table::TYPE_INT => 4, + Table::TYPE_STRING => 32, + Table::TYPE_FLOAT => 8, + ]; + + /** + * 架构函数 + * @access public + */ + public function __construct($host, $port, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP) + { + $this->server_type = Config::get('swoole.server_type'); + switch ($this->server_type) { + case 'websocket': + $this->swoole = new WebSocketServer($host, $port, $mode, SWOOLE_SOCK_TCP); + break; + default: + $this->swoole = new HttpServer($host, $port, $mode, SWOOLE_SOCK_TCP); + } + if ("process" == Config::get('swoole.queue_type')) { + $process = new QueueProcess(); + $process->run($this->swoole); + } + } + + public function setAppPath($path) + { + $this->appPath = $path; + } + + public function setMonitor($interval = 2, $path = []) + { + $this->monitor['interval'] = $interval; + $this->monitor['path'] = (array) $path; + } + + public function table(array $option) + { + $size = !empty($option['size']) ? $option['size'] : 1024; + $this->table = new Table($size); + + foreach ($option['column'] as $field => $type) { + $length = null; + + if (is_array($type)) { + list($type, $length) = $type; + } + + if (isset($this->fieldType[$type])) { + $type = $this->fieldType[$type]; + } + + $this->table->column($field, $type, isset($length) ? $length : $this->fieldSize[$type]); + } + + $this->table->create(); + } + + public function cachetable() + { + $this->cachetable = new CacheTable(); + } + + public function option(array $option) + { + // 设置参数 + if (!empty($option)) { + $this->swoole->set($option); + } + + foreach ($this->event as $event) { + // 自定义回调 + if (!empty($option[$event])) { + $this->swoole->on($event, $option[$event]); + } elseif (method_exists($this, 'on' . $event)) { + $this->swoole->on($event, [$this, 'on' . $event]); + } + } + if ("websocket" == $this->server_type) { + foreach ($this->event as $event) { + if (method_exists($this, 'Websocketon' . $event)) { + $this->swoole->on($event, [$this, 'Websocketon' . $event]); + } + } + } + } + + /** + * 此事件在Worker进程/Task进程启动时发生,这里创建的对象可以在进程生命周期内使用 + * + * @param $server + * @param $worker_id + */ + public function onWorkerStart($server, $worker_id) + { + // 应用实例化 + $this->app = new Application($this->appPath); + $this->lastMtime = time(); + + //swoole server worker启动行为 + $hook = Container::get('hook'); + $hook->listen('swoole_worker_start', ['server' => $server, 'worker_id' => $worker_id]); + + // Swoole Server保存到容器 + $this->app->swoole = $server; + + if ($this->table) { + $this->app['swoole_table'] = $this->table; + } + + $this->app->cachetable = $this->cachetable; + + // 指定日志类驱动 + Loader::addClassMap([ + 'think\\log\\driver\\File' => __DIR__ . '/log/File.php', + 'think\\cache\\driver\\Table' => __DIR__ . '/cache/driver/Table.php', + ]); + + Facade::bind([ + 'think\facade\Cookie' => Cookie::class, + 'think\facade\Session' => Session::class, + facade\Application::class => Application::class, + facade\Http::class => Http::class, + facade\Task::class => Task::class, + facade\Timer::class => Timer::class, + ]); + + // 应用初始化 + $this->app->initialize(); + + $this->app->bindTo([ + 'cookie' => Cookie::class, + 'session' => Session::class, + ]); + + $this->initServer($server, $worker_id); + + if (0 == $worker_id && $this->monitor) { + $this->monitor($server); + } + + //只在一个进程内执行定时任务 + if (0 == $worker_id) { + $this->timer($server); + } + } + + /** + * 自定义初始化Swoole + * @param $server + * @param $worker_id + */ + public function initServer($server, $worker_id) + { + $wokerStart = Config::get('swoole.wokerstart'); + if ($wokerStart) { + if (is_string($wokerStart) && class_exists($wokerStart)) { + $obj = new $wokerStart($server, $worker_id); + $obj->run(); + unset($obj); + } elseif ($wokerStart instanceof \Closure) { + $wokerStart($server, $worker_id); + } + } + } + + /** + * 文件监控 + * + * @param $server + */ + protected function monitor($server) + { + $paths = $this->monitor['path'] ?: [$this->app->getAppPath(), $this->app->getConfigPath()]; + $timer = $this->monitor['interval'] ?: 2; + + $server->tick($timer, function () use ($paths, $server) { + foreach ($paths as $path) { + $dir = new \RecursiveDirectoryIterator($path); + $iterator = new \RecursiveIteratorIterator($dir); + + foreach ($iterator as $file) { + if (pathinfo($file, PATHINFO_EXTENSION) != 'php') { + continue; + } + + if ($this->lastMtime < $file->getMTime()) { + $this->lastMtime = $file->getMTime(); + echo '[update]' . $file . " reload...\n"; + $server->reload(); + return; + } + } + } + }); + } + + /** + * 系统定时器 + * + * @param $server + */ + public function timer($server) + { + $timer = Config::get('swoole.timer'); + $interval = intval(Config::get('swoole.interval')); + if ($timer) { + $interval = $interval > 0 ? $interval : 1000; + $server->tick($interval, function () use ($server) { + TimerF::run($server); + }); + } + $queue_type = Config::get('swoole.queue_type'); + $task = QueueTask::instance(); + if ("task" == $queue_type) { + $server->tick(1000, function () use ($queue_type, $task) { + $task->run(); + }); + } + } + + /** + * request回调 + * @param $request + * @param $response + */ + public function onRequest($request, $response) + { + // 执行应用并响应 + $this->app->swoole($request, $response); + } + + /** + * Message回调 + * @param $server + * @param $frame + */ + public function WebsocketonMessage($server, $frame) + { + // 执行应用并响应 + $this->app->swooleWebSocket($server, $frame); + } + + /** + * Close + */ + public function WebsocketonClose($server, $fd, $reactorId) + { + $data = [$server, $fd, $reactorId]; + $hook = Container::get('hook'); + $hook->listen('swoole_websocket_on_close', $data); + } + + /** + * 任务投递 + * @param HttpServer $serv + * @param $task_id + * @param $fromWorkerId + * @param $data + * @return mixed|null + */ + public function onTask(HttpServer $serv, $task_id, $fromWorkerId, $data) + { + if (is_string($data) && class_exists($data)) { + $taskObj = new $data; + if (method_exists($taskObj, 'run')) { + $taskObj->run($serv, $task_id, $fromWorkerId); + unset($taskObj); + return true; + } + } + + if (is_object($data) && method_exists($data, 'run')) { + $data->run($serv, $task_id, $fromWorkerId); + unset($data); + return true; + } + + if ($data instanceof SuperClosure) { + return $data($serv, $task_id, $data); + } else { + $serv->finish($data); + } + } + + /** + * 任务结束,如果有自定义任务结束回调方法则不会触发该方法 + * @param HttpServer $serv + * @param $task_id + * @param $data + */ + public function onFinish(HttpServer $serv, $task_id, $data) + { + if ($data instanceof SuperClosure) { + $data($serv, $task_id, $data); + } + } +} diff --git a/vendor/topthink/think-swoole/src/Server.php b/vendor/topthink/think-swoole/src/Server.php new file mode 100644 index 0000000..6228032 --- /dev/null +++ b/vendor/topthink/think-swoole/src/Server.php @@ -0,0 +1,123 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole; + +use Swoole\Http\Server as HttpServer; +use Swoole\Server as SwooleServer; +use Swoole\Websocket\Server as Websocket; + +/** + * Swoole Server扩展类 + */ +abstract class Server +{ + /** + * Swoole对象 + * @var object + */ + protected $swoole; + + /** + * SwooleServer类型 + * @var string + */ + protected $serverType = 'http'; + + /** + * Socket的类型 + * @var int + */ + protected $sockType = SWOOLE_SOCK_TCP; + + /** + * 运行模式 + * @var int + */ + protected $mode = SWOOLE_PROCESS; + + /** + * 监听地址 + * @var string + */ + protected $host = '0.0.0.0'; + + /** + * 监听端口 + * @var int + */ + protected $port = 9501; + + /** + * 配置 + * @var array + */ + protected $option = []; + + /** + * 支持的响应事件 + * @var array + */ + protected $event = ['Start', 'Shutdown', 'WorkerStart', 'WorkerStop', 'WorkerExit', 'Connect', 'Receive', 'Packet', 'Close', 'BufferFull', 'BufferEmpty', 'Task', 'Finish', 'PipeMessage', 'WorkerError', 'ManagerStart', 'ManagerStop', 'Open', 'Message', 'HandShake', 'Request']; + + /** + * 架构函数 + * @access public + */ + public function __construct() + { + // 实例化 Swoole 服务 + switch ($this->serverType) { + case 'socket': + $this->swoole = new Websocket($this->host, $this->port, $this->mode, $this->sockType); + break; + case 'http': + $this->swoole = new HttpServer($this->host, $this->port, $this->mode, $this->sockType); + break; + default: + $this->swoole = new SwooleServer($this->host, $this->port, $this->mode, $this->sockType); + } + + // 设置参数 + if (!empty($this->option)) { + $this->swoole->set($this->option); + } + + // 设置回调 + foreach ($this->event as $event) { + if (method_exists($this, 'on' . $event)) { + $this->swoole->on($event, [$this, 'on' . $event]); + } + } + + // 初始化 + $this->init(); + + // 启动服务 + $this->swoole->start(); + } + + protected function init() + { + } + + /** + * 魔术方法 有不存在的操作的时候执行 + * @access public + * @param string $method 方法名 + * @param array $args 参数 + * @return mixed + */ + public function __call($method, $args) + { + call_user_func_array([$this->swoole, $method], $args); + } +} diff --git a/vendor/topthink/think-swoole/src/Session.php b/vendor/topthink/think-swoole/src/Session.php new file mode 100644 index 0000000..fc1f1be --- /dev/null +++ b/vendor/topthink/think-swoole/src/Session.php @@ -0,0 +1,435 @@ + +// +---------------------------------------------------------------------- +namespace think\swoole; + +use Swoole\Table; +use think\Container; +use think\facade\Cache; +use think\facade\Cookie as ThinkCookie; +use think\Session as BaseSession; + +/** + * Swoole Cookie类 + */ +class Session extends BaseSession +{ + /** + * Session数据 + * @var array + */ + protected $data = []; + + /** + * 记录Session name + * @var string + */ + protected $sessionName = 'PHPSESSID'; + + /** + * Session有效期 + * @var int + */ + protected $expire = 0; + + /** + * Swoole_table对象 + * @var Table + */ + protected $swooleTable; + + /** + * session初始化 + * @access public + * @param array $config + * @return void + * @throws \think\Exception + */ + public function init(array $config = []) + { + $config = $config ?: $this->config; + + if (!empty($config['name'])) { + $this->sessionName = $config['name']; + } + + if (!empty($config['expire'])) { + $this->expire = $config['expire']; + } + + if (!empty($config['auto_start'])) { + $this->start(); + } else { + $this->init = false; + } + + return $this; + } + + /** + * session自动启动或者初始化 + * @access public + * @return void + */ + public function boot() + { + if (is_null($this->init)) { + $this->init(); + } + + if (false === $this->init) { + $this->start(); + } + } + + public function name($name) + { + $this->sessionName = $name; + } + + /** + * session_id设置 + * @access public + * @param string $id session_id + * @param int $expire Session有效期 + * @return void + */ + public function setId($id, $expire = null) + { + ThinkCookie::set($this->sessionName, $id, $expire); + } + + /** + * 获取session_id + * @access public + * @param bool $regenerate 不存在是否自动生成 + * @return string + */ + public function getId($regenerate = true) + { + $sessionId = ThinkCookie::get($this->sessionName) ?: ''; + + if (!$sessionId && $regenerate) { + $sessionId = $this->regenerate(); + } + + return $sessionId; + } + + /** + * session设置 + * @access public + * @param string $name session名称 + * @param mixed $value session值 + * @return void + */ + public function set($name, $value, $prefix = null) + { + empty($this->init) && $this->boot(); + + $sessionId = $this->getId(); + + $this->setSession($sessionId, $name, $value); + } + + /** + * session设置 + * @access protected + * @param string $sessionId session_id + * @param string $name session名称 + * @param mixed $value session值 + * @param string|null $prefix 作用域(前缀) + * @return void + */ + protected function setSession($sessionId, $name, $value) + { + if (strpos($name, '.')) { + // 二维数组赋值 + list($name1, $name2) = explode('.', $name); + + $this->data[$sessionId][$name1][$name2] = $value; + } else { + $this->data[$sessionId][$name] = $value; + } + + // 持久化session数据 + $this->writeSessionData($sessionId); + } + + /** + * session获取 + * @access public + * @param string $name session名称 + * @return mixed + */ + public function get($name = '', $prefix = null) + { + empty($this->init) && $this->boot(); + + $sessionId = $this->getId(); + + return $this->readSession($sessionId, $name); + } + + /** + * session获取 + * @access protected + * @param string $sessionId session_id + * @param string $name session名称 + * @return mixed + */ + protected function readSession($sessionId, $name = '') + { + $value = isset($this->data[$sessionId]) ? $this->data[$sessionId] : []; + + if (!is_array($value)) { + $value = []; + } + + if ('' != $name) { + $name = explode('.', $name); + + foreach ($name as $val) { + if (isset($value[$val])) { + $value = $value[$val]; + } else { + $value = null; + break; + } + } + } + + return $value; + } + + /** + * 删除session数据 + * @access public + * @param string|array $name session名称 + * @return void + */ + public function delete($name, $prefix = null) + { + empty($this->init) && $this->boot(); + + $sessionId = $this->getId(false); + + if ($sessionId) { + $this->deleteSession($sessionId, $name); + + // 持久化session数据 + $this->writeSessionData($sessionId); + } + } + + /** + * 删除session数据 + * @access protected + * @param string $sessionId session_id + * @param string|array $name session名称 + * @return void + */ + protected function deleteSession($sessionId, $name) + { + if (is_array($name)) { + foreach ($name as $key) { + $this->deleteSession($sessionId, $key); + } + } elseif (strpos($name, '.')) { + list($name1, $name2) = explode('.', $name); + unset($this->data[$sessionId][$name1][$name2]); + } else { + unset($this->data[$sessionId][$name]); + } + } + + protected function writeSessionData($sessionId) + { + if ($this->swooleTable) { + $this->swooleTable->set('sess_' . $sessionId, [ + 'data' => json_encode($this->data[$sessionId]), + 'expire' => time() + $this->expire, + ]); + } else { + Cache::set('sess_' . $sessionId, $this->data[$sessionId], $this->expire); + } + } + + /** + * 清空session数据 + * @access public + * @return void + */ + public function clear($prefix = null) + { + empty($this->init) && $this->boot(); + + $sessionId = $this->getId(false); + + if ($sessionId) { + $this->clearSession($sessionId); + } + } + + /** + * 清空session数据 + * @access protected + * @param string $sessionId session_id + * @return void + */ + protected function clearSession($sessionId) + { + $this->data[$sessionId] = []; + + if ($this->swooleTable) { + $this->swooleTable->del('sess_' . $sessionId); + } else { + Cache::rm('sess_' . $sessionId); + } + } + + /** + * 判断session数据 + * @access public + * @param string $name session名称 + * @return bool + */ + public function has($name, $prefix = null) + { + empty($this->init) && $this->boot(); + + $sessionId = $this->getId(false); + + if ($sessionId) { + return $this->hasSession($sessionId, $name); + } + + return false; + } + + /** + * 判断session数据 + * @access protected + * @param string $sessionId session_id + * @param string $name session名称 + * @return bool + */ + protected function hasSession($sessionId, $name) + { + $value = isset($this->data[$sessionId]) ? $this->data[$sessionId] : []; + + $name = explode('.', $name); + + foreach ($name as $val) { + if (!isset($value[$val])) { + return false; + } else { + $value = $value[$val]; + } + } + + return true; + } + + /** + * 启动session + * @access public + * @return void + */ + public function start() + { + $sessionId = $this->getId(); + + // 读取缓存数据 + if (empty($this->data[$sessionId])) { + if (!empty($this->config['use_swoole_table'])) { + $this->swooleTable = Container::get('swoole_table'); + + $result = $this->swooleTable->get('sess_' . $sessionId); + + if (0 == $result['expire'] || time() <= $result['expire']) { + $data = $result['data']; + } + } else { + $data = Cache::get('sess_' . $sessionId); + } + + if (!empty($data)) { + $this->data[$sessionId] = $data; + } + } + + $this->init = true; + } + + /** + * 销毁session + * @access public + * @return void + */ + public function destroy() + { + $sessionId = $this->getId(false); + + if ($sessionId) { + $this->destroySession($sessionId); + } + + $this->init = null; + } + + /** + * 销毁session + * @access protected + * @param string $sessionId session_id + * @return void + */ + protected function destroySession($sessionId) + { + if (isset($this->data[$sessionId])) { + unset($this->data[$sessionId]); + + if ($this->swooleTable) { + $this->swooleTable->del('sess_' . $sessionId); + } else { + Cache::rm('sess_' . $sessionId); + } + } + } + + /** + * 生成session_id + * @access public + * @param bool $delete 是否删除关联会话文件 + * @return string + */ + public function regenerate($delete = false) + { + if ($delete) { + $this->destroy(); + } + + $sessionId = md5(microtime(true) . uniqid()); + + $this->setId($sessionId); + + return $sessionId; + } + + /** + * 暂停session + * @access public + * @return void + */ + public function pause() + { + $this->init = false; + } +} diff --git a/vendor/topthink/think-swoole/src/SuperClosure.php b/vendor/topthink/think-swoole/src/SuperClosure.php new file mode 100644 index 0000000..02b40ec --- /dev/null +++ b/vendor/topthink/think-swoole/src/SuperClosure.php @@ -0,0 +1,46 @@ +closure = $closure; + } + + final public function __sleep() + { + $serializer = new Serializer(); + $this->serialized = $serializer->serialize($this->closure); + unset($this->closure); + + return ['serialized']; + } + + final public function __wakeup() + { + $serializer = new Serializer(); + $this->closure = $serializer->unserialize($this->serialized); + } + + final public function __invoke(...$args) + { + return Container::getInstance()->invokeFunction($this->closure, $args); + } + + final public function call(...$args) + { + return Container::getInstance()->invokeFunction($this->closure, $args); + } +} diff --git a/vendor/topthink/think-swoole/src/Task.php b/vendor/topthink/think-swoole/src/Task.php new file mode 100644 index 0000000..1a9762d --- /dev/null +++ b/vendor/topthink/think-swoole/src/Task.php @@ -0,0 +1,35 @@ +task($task, $taskWorkerId, $finishCallback); + } +} diff --git a/vendor/topthink/think-swoole/src/Timer.php b/vendor/topthink/think-swoole/src/Timer.php new file mode 100644 index 0000000..4151012 --- /dev/null +++ b/vendor/topthink/think-swoole/src/Timer.php @@ -0,0 +1,153 @@ +config = Config::pull('timer'); + + if (empty($this->config)) { + $this->config = []; + } + } + + /** + * 开始执行定时器任务 + * @param $serv 服务对象 + */ + public function run($serv) + { + if (count(self::$timerlists) > 0) { + $this->startTask(); + } else { + $this->initimerlists(); + } + } + + /** + * 到期后执行定时任务 + */ + public function startTask() + { + foreach (self::$timerlists as &$one) { + if ($one['next_time'] <= time()) { + $cron = CronExpression::factory($one['key']); + + $one['next_time'] = $cron->getNextRunDate()->getTimestamp(); + + $this->syncTask($one['val']); + } + } + unset($one); + } + + /** + * 根据定时配置计算下次执行时间并存储相关信息 + * @throws \Exception + */ + public function initimerlists() + { + $i = 0; + foreach ($this->config as $key => $val) { + try { + $cron = CronExpression::factory($key); + $time = $cron->getNextRunDate()->getTimestamp(); + + self::$timerlists[$i]['key'] = $key; + self::$timerlists[$i]['val'] = $val; + self::$timerlists[$i]['next_time'] = $time; + } catch (\Exception $e) { + var_dump($e); + throw new \Exception("定时器异常"); + } + $i++; + } + } + + /** + * 异步投递任务到task worker + * @param string $class + */ + public function syncTask($class) + { + if (is_string($class) && class_exists($class)) { + TaskF::async(function () use ($class) { + $obj = new $class(); + $obj->run(); + unset($obj); + }); + } + } + + /** + * 每隔固定时间执行一次 + * @param int $time 间隔时间 + * @param mixed $callback 可以是回调 可以是定时器任务模板 + * @return bool + */ + public function tick($time, $callback) + { + if ($callback instanceof \Closure) { + return SwooleTimer::tick($time, $callback); + } elseif (is_object($callback) && method_exists($callback, 'run')) { + return SwooleTimer::tick($time, function () use ($callback) { + $callback->run(); + }); + } + + return false; + } + + /** + * 延迟执行 + * @param int $time 间隔时间 + * @param mixed $callback 可以是回调 可以是定时器任务模板 + * @return bool + */ + public function after($time, $callback) + { + if ($callback instanceof \Closure) { + return SwooleTimer::after($time, $callback); + } elseif (is_object($callback) && method_exists($callback, 'run')) { + return SwooleTimer::after($time, function () use ($callback) { + $callback->run(); + unset($callback); + }); + } + + return false; + } + + /** + * 清除定时器 + * @param int $timerId + * @return bool + */ + public function clear($timerId) + { + return SwooleTimer::clear($timerId); + } +} diff --git a/vendor/topthink/think-swoole/src/WebSocketFrame.php b/vendor/topthink/think-swoole/src/WebSocketFrame.php new file mode 100644 index 0000000..f1d0cd6 --- /dev/null +++ b/vendor/topthink/think-swoole/src/WebSocketFrame.php @@ -0,0 +1,117 @@ +server = $server; + $this->data = null; + if (!empty($frame)) { + $this->frame = $frame; + $this->data = json_decode($this->frame->data, true); + } + } + + public static function getInstance($server = null, $frame = null) + { + if (empty(self::$instance)) { + if (empty($server)) { + $swoole = Container::get('swoole'); + $server = $swoole; + } + self::$instance = new static($server, $frame); + } + return self::$instance; + } + + public static function destroy() + { + self::$instance = null; + } + + public function getServer() + { + return $this->server; + } + + public function getFrame() + { + return $this->frame; + } + + public function getData() + { + return $this->data; + } + + public function getArgs() + { + return isset($this->data['arguments']) ? $this->data['arguments'] : null; + } + + public function __call($method, $params) + { + return call_user_func_array([$this->server, $method], $params); + } + + public function pushToClient($data, $event = true) + { + if ($event) { + $eventname = isset($this->data['event']) ? $this->data['event'] : false; + if ($eventname) { + $data['event'] = $eventname; + } + } + $this->sendToClient($this->frame->fd, $data); + } + + public function sendToClient($fd, $data) + { + if (is_string($data)) { + $this->server->push($fd, $data); + } elseif (is_array($data)) { + $this->server->push($fd, json_encode($data)); + } + } + + public function pushToClients($data) + { + foreach ($this->server->connections as $fd) { + $this->sendToClient($fd, $data); + } + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]) ? true : false; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } +} diff --git a/vendor/topthink/think-swoole/src/cache/driver/Table.php b/vendor/topthink/think-swoole/src/cache/driver/Table.php new file mode 100644 index 0000000..dddc970 --- /dev/null +++ b/vendor/topthink/think-swoole/src/cache/driver/Table.php @@ -0,0 +1,111 @@ + 0, + 'prefix' => '', + 'serialize' => true, + ]; + public function __construct($options = []) + { + $this->handler = Container::get('cachetable'); + } + + public function set($name, $value, $expire = null) + { + $this->writeTimes++; + + if (is_null($expire)) { + $expire = $this->options['expire']; + } + + if ($this->tag && !$this->has($name)) { + $first = true; + } + + $key = $this->getCacheKey($name); + $expire = $this->getExpireTime($expire); + + $value = $this->serialize($value); + + if ($expire) { + $result = $this->handler->setex($key, $expire, $value); + } else { + $result = $this->handler->set($key, $value); + } + + isset($first) && $this->setTagItem($key); + + return $result; + } + + public function dec($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + $expire = $this->expire; + } else { + $value = -$step; + $expire = 0; + } + + return $this->set($name, $value, $expire) ? $value : false; + } + + public function clear($tag = null) + { + $this->writeTimes++; + + return $this->handler->clear(); + } + + public function get($name, $default = false) + { + $this->readTimes++; + + $value = $this->handler->get($this->getCacheKey($name)); + + if (is_null($value) || false === $value) { + return $default; + } + + return $this->unserialize($value); + } + + public function has($name) + { + return $this->handler->exists($this->getCacheKey($name)); + } + + public function rm($name) + { + $this->writeTimes++; + return $this->handler->del($this->getCacheKey($name)); + } + + public function inc($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + $expire = $this->expire; + } else { + $value = $step; + $expire = 0; + } + + return $this->set($name, $value, $expire) ? $value : false; + } +} diff --git a/vendor/topthink/think-swoole/src/command.php b/vendor/topthink/think-swoole/src/command.php new file mode 100644 index 0000000..190b362 --- /dev/null +++ b/vendor/topthink/think-swoole/src/command.php @@ -0,0 +1,16 @@ + +// +---------------------------------------------------------------------- + +// 注册命令行指令 +\think\Console::addDefaultCommands([ + 'swoole' => '\\think\\swoole\\command\\Swoole', + 'swoole:server' => '\\think\\swoole\\command\\Server', +]); diff --git a/vendor/topthink/think-swoole/src/command/Server.php b/vendor/topthink/think-swoole/src/command/Server.php new file mode 100644 index 0000000..cc73826 --- /dev/null +++ b/vendor/topthink/think-swoole/src/command/Server.php @@ -0,0 +1,163 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole\command; + +use Swoole\Process; +use think\console\input\Argument; +use think\console\input\Option; +use think\facade\Config; +use think\facade\Env; +use think\swoole\Server as ThinkServer; + +/** + * Swoole 命令行,支持操作:start|stop|restart|reload + * 支持应用配置目录下的swoole_server.php文件进行参数配置 + */ +class Server extends Swoole +{ + public function configure() + { + $this->setName('swoole:server') + ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload", 'start') + ->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of swoole server.', null) + ->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of swoole server.', null) + ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the swoole server in daemon mode.') + ->setDescription('Swoole Server for ThinkPHP'); + } + + protected function init() + { + $this->config = Config::pull('swoole_server'); + + if (empty($this->config['pid_file'])) { + $this->config['pid_file'] = Env::get('runtime_path') . 'swoole_server.pid'; + } + + // 避免pid混乱 + $this->config['pid_file'] .= '_' . $this->getPort(); + } + + /** + * 启动server + * @access protected + * @return void + */ + protected function start() + { + $pid = $this->getMasterPid(); + + if ($this->isRunning($pid)) { + $this->output->writeln('swoole server process is already running.'); + return false; + } + + $this->output->writeln('Starting swoole server...'); + + if (!empty($this->config['swoole_class'])) { + $class = $this->config['swoole_class']; + + if (class_exists($class)) { + $swoole = new $class; + if (!$swoole instanceof ThinkServer) { + $this->output->writeln("Swoole Server Class Must extends \\think\\swoole\\Server"); + return false; + } + } else { + $this->output->writeln("Swoole Server Class Not Exists : {$class}"); + return false; + } + } else { + $host = $this->getHost(); + $port = $this->getPort(); + $type = !empty($this->config['type']) ? $this->config['type'] : 'socket'; + $mode = !empty($this->config['mode']) ? $this->config['mode'] : SWOOLE_PROCESS; + $sockType = !empty($this->config['sock_type']) ? $this->config['sock_type'] : SWOOLE_SOCK_TCP; + + switch ($type) { + case 'socket': + $swooleClass = 'Swoole\Websocket\Server'; + break; + case 'http': + $swooleClass = 'Swoole\Http\Server'; + break; + default: + $swooleClass = 'Swoole\Server'; + } + + $swoole = new $swooleClass($host, $port, $mode, $sockType); + + // 开启守护进程模式 + if ($this->input->hasOption('daemon')) { + $this->config['daemonize'] = true; + } + + foreach ($this->config as $name => $val) { + if (0 === strpos($name, 'on')) { + $swoole->on(substr($name, 2), $val); + unset($this->config[$name]); + } + } + + // 设置服务器参数 + $swoole->set($this->config); + + $this->output->writeln("Swoole {$type} server started: <{$host}:{$port}>"); + $this->output->writeln('You can exit with `CTRL-C`'); + + // 启动服务 + $swoole->start(); + } + } + + /** + * 柔性重启server + * @access protected + * @return void + */ + protected function reload() + { + // 柔性重启使用管理PID + $pid = $this->getMasterPid(); + + if (!$this->isRunning($pid)) { + $this->output->writeln('no swoole server process running.'); + return false; + } + + $this->output->writeln('Reloading swoole server...'); + Process::kill($pid, SIGUSR1); + $this->output->writeln('> success'); + } + + /** + * 停止server + * @access protected + * @return void + */ + protected function stop() + { + $pid = $this->getMasterPid(); + + if (!$this->isRunning($pid)) { + $this->output->writeln('no swoole server process running.'); + return false; + } + + $this->output->writeln('Stopping swoole server...'); + + Process::kill($pid, SIGTERM); + $this->removePid(); + + $this->output->writeln('> success'); + } + +} diff --git a/vendor/topthink/think-swoole/src/command/Swoole.php b/vendor/topthink/think-swoole/src/command/Swoole.php new file mode 100644 index 0000000..880d5c1 --- /dev/null +++ b/vendor/topthink/think-swoole/src/command/Swoole.php @@ -0,0 +1,260 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole\command; + +use Swoole\Process; +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\input\Option; +use think\console\Output; +use think\facade\Config; +use think\facade\Env; +use think\swoole\Http as HttpServer; +use think\Container; + +/** + * Swoole HTTP 命令行,支持操作:start|stop|restart|reload + * 支持应用配置目录下的swoole.php文件进行参数配置 + */ +class Swoole extends Command +{ + protected $config = []; + + public function configure() + { + $this->setName('swoole') + ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload", 'start') + ->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of swoole server.', null) + ->addOption('port', 'p', Option::VALUE_OPTIONAL, 'the port of swoole server.', null) + ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run the swoole server in daemon mode.') + ->setDescription('Swoole HTTP Server for ThinkPHP'); + } + + public function execute(Input $input, Output $output) + { + $action = $input->getArgument('action'); + + $this->init(); + + if (in_array($action, ['start', 'stop', 'reload', 'restart'])) { + $this->$action(); + } else { + $output->writeln("Invalid argument action:{$action}, Expected start|stop|restart|reload ."); + } + } + + protected function init() + { + $this->config = Config::pull('swoole'); + + if (empty($this->config['pid_file'])) { + $this->config['pid_file'] = Env::get('runtime_path') . 'swoole.pid'; + } + + // 避免pid混乱 + $this->config['pid_file'] .= '_' . $this->getPort(); + } + + protected function getHost() + { + if ($this->input->hasOption('host')) { + $host = $this->input->getOption('host'); + } else { + $host = !empty($this->config['host']) ? $this->config['host'] : '0.0.0.0'; + } + + return $host; + } + + protected function getPort() + { + if ($this->input->hasOption('port')) { + $port = $this->input->getOption('port'); + } else { + $port = !empty($this->config['port']) ? $this->config['port'] : 9501; + } + + return $port; + } + + /** + * 启动server + * @access protected + * @return void + */ + protected function start() + { + $pid = $this->getMasterPid(); + + if ($this->isRunning($pid)) { + $this->output->writeln('swoole http server process is already running.'); + return false; + } + + $this->output->writeln('Starting swoole http server...'); + + $host = $this->getHost(); + $port = $this->getPort(); + $mode = !empty($this->config['mode']) ? $this->config['mode'] : SWOOLE_PROCESS; + $type = !empty($this->config['sock_type']) ? $this->config['sock_type'] : SWOOLE_SOCK_TCP; + + $ssl = !empty($this->config['ssl']) || !empty($this->config['open_http2_protocol']); + if ($ssl) { + $type = SWOOLE_SOCK_TCP | SWOOLE_SSL; + } + + $swoole = new HttpServer($host, $port, $mode, $type); + + // 开启守护进程模式 + if ($this->input->hasOption('daemon')) { + $this->config['daemonize'] = true; + } + + // 设置应用目录 + $swoole->setAppPath($this->config['app_path']); + + // 创建内存表 + if (!empty($this->config['table'])) { + $swoole->table($this->config['table']); + unset($this->config['table']); + } + + $swoole->cachetable(); + + // 设置文件监控 调试模式自动开启 + if (Env::get('app_debug') || !empty($this->config['file_monitor'])) { + $interval = isset($this->config['file_monitor_interval']) ? $this->config['file_monitor_interval'] : 2; + $paths = isset($this->config['file_monitor_path']) ? $this->config['file_monitor_path'] : []; + $swoole->setMonitor($interval, $paths); + unset($this->config['file_monitor'], $this->config['file_monitor_interval'], $this->config['file_monitor_path']); + } + + // 设置服务器参数 + if (isset($this->config['pid_file'])) { + + } + $swoole->option($this->config); + + $this->output->writeln("Swoole http server started: "); + $this->output->writeln('You can exit with `CTRL-C`'); + + $hook = Container::get('hook'); + $hook->listen("swoole_server_start", $swoole); + + $swoole->start(); + } + + /** + * 柔性重启server + * @access protected + * @return void + */ + protected function reload() + { + $pid = $this->getMasterPid(); + + if (!$this->isRunning($pid)) { + $this->output->writeln('no swoole http server process running.'); + return false; + } + + $this->output->writeln('Reloading swoole http server...'); + Process::kill($pid, SIGUSR1); + $this->output->writeln('> success'); + } + + /** + * 停止server + * @access protected + * @return void + */ + protected function stop() + { + $pid = $this->getMasterPid(); + + if (!$this->isRunning($pid)) { + $this->output->writeln('no swoole http server process running.'); + return false; + } + + $this->output->writeln('Stopping swoole http server...'); + + Process::kill($pid, SIGTERM); + $this->removePid(); + + $this->output->writeln('> success'); + } + + /** + * 重启server + * @access protected + * @return void + */ + protected function restart() + { + $pid = $this->getMasterPid(); + + if ($this->isRunning($pid)) { + $this->stop(); + } + + $this->start(); + } + + /** + * 获取主进程PID + * @access protected + * @return int + */ + protected function getMasterPid() + { + $pidFile = $this->config['pid_file']; + + if (is_file($pidFile)) { + $masterPid = (int) file_get_contents($pidFile); + } else { + $masterPid = 0; + } + + return $masterPid; + } + + /** + * 删除PID文件 + * @access protected + * @return void + */ + protected function removePid() + { + $masterPid = $this->config['pid_file']; + + if (is_file($masterPid)) { + unlink($masterPid); + } + } + + /** + * 判断PID是否在运行 + * @access protected + * @param int $pid + * @return bool + */ + protected function isRunning($pid) + { + if (empty($pid)) { + return false; + } + + return Process::kill($pid, 0); + } +} diff --git a/vendor/topthink/think-swoole/src/config/server.php b/vendor/topthink/think-swoole/src/config/server.php new file mode 100644 index 0000000..0058ff4 --- /dev/null +++ b/vendor/topthink/think-swoole/src/config/server.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +use think\facade\Env; + +// +---------------------------------------------------------------------- +// | Swoole设置 php think swoole:server 命令行下有效 +// +---------------------------------------------------------------------- +return [ + // 扩展自身配置 + 'host' => '0.0.0.0', // 监听地址 + 'port' => 9508, // 监听端口 + 'type' => 'socket', // 服务类型 支持 socket http server + 'mode' => '', // 运行模式 默认为SWOOLE_PROCESS + 'sock_type' => '', // sock type 默认为SWOOLE_SOCK_TCP + 'swoole_class' => '', // 自定义服务类名称 + + // 可以支持swoole的所有配置参数 + 'daemonize' => false, + 'pid_file' => Env::get('runtime_path') . 'swoole_server.pid', + 'log_file' => Env::get('runtime_path') . 'swoole_server.log', + + // 事件回调定义 + 'onOpen' => function ($server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }, + + 'onMessage' => function ($server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }, + + 'onRequest' => function ($request, $response) { + $response->end("

Hello Swoole. #" . rand(1000, 9999) . "

"); + }, + + 'onClose' => function ($ser, $fd) { + echo "client {$fd} closed\n"; + }, +]; diff --git a/vendor/topthink/think-swoole/src/config/swoole.php b/vendor/topthink/think-swoole/src/config/swoole.php new file mode 100644 index 0000000..92eafba --- /dev/null +++ b/vendor/topthink/think-swoole/src/config/swoole.php @@ -0,0 +1,37 @@ + +// +---------------------------------------------------------------------- + +use think\facade\Env; + +// +---------------------------------------------------------------------- +// | Swoole设置 php think swoole命令行下有效 +// +---------------------------------------------------------------------- +return [ + // 扩展自身配置 + 'host' => '0.0.0.0', // 监听地址 + 'port' => 9501, // 监听端口 + 'mode' => '', // 运行模式 默认为SWOOLE_PROCESS + 'sock_type' => '', // sock type 默认为SWOOLE_SOCK_TCP + 'server_type' => 'http', // 服务类型 支持 http websocket + 'app_path' => '', // 应用地址 如果开启了 'daemonize'=>true 必须设置(使用绝对路径) + 'file_monitor' => false, // 是否开启PHP文件更改监控(调试模式下自动开启) + 'file_monitor_interval' => 2, // 文件变化监控检测时间间隔(秒) + 'file_monitor_path' => [], // 文件监控目录 默认监控application和config目录 + + // 可以支持swoole的所有配置参数 + 'pid_file' => Env::get('runtime_path') . 'swoole.pid', + 'log_file' => Env::get('runtime_path') . 'swoole.log', + 'document_root' => Env::get('root_path') . 'public', + 'enable_static_handler' => true, + 'timer' => true,//是否开启系统定时器 + 'interval' => 500,//系统定时器 时间间隔 + 'task_worker_num' => 1,//swoole 任务工作进程数量 +]; diff --git a/vendor/topthink/think-swoole/src/config/timer.php b/vendor/topthink/think-swoole/src/config/timer.php new file mode 100644 index 0000000..0b74b2d --- /dev/null +++ b/vendor/topthink/think-swoole/src/config/timer.php @@ -0,0 +1,16 @@ + "类" + * *中间一个空格 + * 系统定时任务需要在swoole.php中开启 + * 自定义定时器不受其影响 + */ + +return [ + '*/5 * * * * *' => '\\app\\lib\\Timer', +]; diff --git a/vendor/topthink/think-swoole/src/facade/Application.php b/vendor/topthink/think-swoole/src/facade/Application.php new file mode 100644 index 0000000..66a7963 --- /dev/null +++ b/vendor/topthink/think-swoole/src/facade/Application.php @@ -0,0 +1,26 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole\facade; + +use Swoole\Http\Request; +use Swoole\Http\Response; +use think\Facade; + +/** + * @see \think\swoole\Application + * @mixin \think\swoole\Application + * @method void initialize() static 初始化应用 + * @method void swoole(Request $request, Response $response) static 处理Swoole请求 + */ +class Application extends Facade +{ +} diff --git a/vendor/topthink/think-swoole/src/facade/Http.php b/vendor/topthink/think-swoole/src/facade/Http.php new file mode 100644 index 0000000..134adae --- /dev/null +++ b/vendor/topthink/think-swoole/src/facade/Http.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- + +namespace think\swoole\facade; + +use think\Facade; + +/** + * @see \think\swoole\Http + * @mixin \think\swoole\Http + * @method void option(array $option) static 参数设置 + * @method void start() static 启动服务 + * @method void stop() static 停止服务 + */ +class Http extends Facade +{ +} diff --git a/vendor/topthink/think-swoole/src/facade/Task.php b/vendor/topthink/think-swoole/src/facade/Task.php new file mode 100644 index 0000000..9b13783 --- /dev/null +++ b/vendor/topthink/think-swoole/src/facade/Task.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- + +namespace think\log\driver; + +use think\App; + +/** + * 本地化调试输出到文件 + */ +class File +{ + protected $config = [ + 'time_format' => ' c ', + 'single' => false, + 'file_size' => 2097152, + 'path' => '', + 'apart_level' => [], + 'max_files' => 0, + 'json' => false, + ]; + + protected $app; + + // 实例化并传入参数 + public function __construct(App $app, $config = []) + { + $this->app = $app; + + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } + + if (empty($this->config['path'])) { + $this->config['path'] = $this->app->getRuntimePath() . 'log' . DIRECTORY_SEPARATOR; + } elseif (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) { + $this->config['path'] .= DIRECTORY_SEPARATOR; + } + } + + /** + * 日志写入接口 + * @access public + * @param array $log 日志信息 + * @param bool $append 是否追加请求信息 + * @return bool + */ + public function save(array $log = [], $append = false) + { + $destination = $this->getMasterLogFile(); + + $path = dirname($destination); + !is_dir($path) && mkdir($path, 0755, true); + + $info = []; + + foreach ($log as $type => $val) { + + foreach ($val as $msg) { + if (!is_string($msg)) { + $msg = var_export($msg, true); + } + + $info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg; + } + + if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) { + // 独立记录的日志级别 + $filename = $this->getApartLevelFile($path, $type); + + $this->write($info[$type], $filename, true, $append); + + unset($info[$type]); + } + } + + if ($info) { + return $this->write($info, $destination, false, $append); + } + + return true; + } + + /** + * 日志写入 + * @access protected + * @param array $message 日志信息 + * @param string $destination 日志文件 + * @param bool $apart 是否独立文件写入 + * @param bool $append 是否追加请求信息 + * @return bool + */ + protected function write($message, $destination, $apart = false, $append = false) + { + // 检测日志文件大小,超过配置大小则备份日志文件重新生成 + $this->checkLogSize($destination); + + // 日志信息封装 + $info['timestamp'] = date($this->config['time_format']); + + foreach ($message as $type => $msg) { + $info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg; + } + + if (PHP_SAPI == 'cli') { + $message = $this->parseCliLog($info); + } else { + // 添加调试日志 + $this->getDebugLog($info, $append, $apart); + + $message = $this->parseLog($info); + } + + return error_log($message, 3, $destination); + } + + /** + * 获取主日志文件名 + * @access public + * @return string + */ + protected function getMasterLogFile() + { + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'swoole'; + + $destination = $this->config['path'] . $name . '.log'; + } else { + if ($this->config['max_files']) { + $filename = date('Ymd') . '_swoole.log'; + $files = glob($this->config['path'] . '*.log'); + + try { + if (count($files) > $this->config['max_files']) { + unlink($files[0]); + } + } catch (\Exception $e) { + } + } else { + $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . '_swoole.log'; + } + + $destination = $this->config['path'] . $filename; + } + + return $destination; + } + + /** + * 获取独立日志文件名 + * @access public + * @param string $path 日志目录 + * @param string $type 日志类型 + * @return string + */ + protected function getApartLevelFile($path, $type) + { + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'swoole'; + + $name .= '_' . $type; + } elseif ($this->config['max_files']) { + $name = date('Ymd') . '_' . $type . '_swoole'; + } else { + $name = date('d') . '_' . $type . '_swoole'; + } + + return $path . DIRECTORY_SEPARATOR . $name . '.log'; + } + + /** + * 检查日志文件大小并自动生成备份文件 + * @access protected + * @param string $destination 日志文件 + * @return void + */ + protected function checkLogSize($destination) + { + if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { + try { + rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination)); + } catch (\Exception $e) { + } + } + } + + /** + * CLI日志解析 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseCliLog($info) + { + if ($this->config['json']) { + $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } else { + $now = $info['timestamp']; + unset($info['timestamp']); + + $message = implode("\r\n", $info); + + $message = "[{$now}]" . $message . "\r\n"; + } + + return $message; + } + + /** + * 解析日志 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseLog($info) + { + $requestInfo = [ + 'ip' => $this->app['request']->ip(), + 'method' => $this->app['request']->method(), + 'host' => $this->app['request']->host(), + 'uri' => $this->app['request']->url(), + ]; + + if ($this->config['json']) { + $info = $requestInfo + $info; + return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } + + array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}"); + unset($info['timestamp']); + + return implode("\r\n", $info) . "\r\n"; + } + + protected function getDebugLog(&$info, $append, $apart) + { + if ($this->app->isDebug() && $append) { + + if ($this->config['json']) { + // 获取基本信息 + $runtime = round(microtime(true) - $this->app->getBeginTime(), 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2); + + $info = [ + 'runtime' => number_format($runtime, 6) . 's', + 'reqs' => $reqs . 'req/s', + 'memory' => $memory_use . 'kb', + 'file' => count(get_included_files()), + ] + $info; + + } elseif (!$apart) { + // 增加额外的调试信息 + $runtime = round(microtime(true) - $this->app->getBeginTime(), 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2); + + $time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]'; + $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + + array_unshift($info, $time_str . $memory_str . $file_load); + } + } + } +} diff --git a/vendor/topthink/think-swoole/src/queue/Process.php b/vendor/topthink/think-swoole/src/queue/Process.php new file mode 100644 index 0000000..8f26ebe --- /dev/null +++ b/vendor/topthink/think-swoole/src/queue/Process.php @@ -0,0 +1,62 @@ +getConfig(); + $maxtimes = Config::get('swoole.queue_maxtimes'); + $this->maxtimes = $maxtimes ? $maxtimes : 10000; + } + + public function run($server) + { + $list = $this->config; + foreach ($list as $key => $val) { + for ($i = 0; $i < $val['nums']; $i++) { + $p = $this->CreateProcess($key, $val); + $server->addProcess($p); + } + } + } + + public function CreateProcess($key = null, $val) + { + $index = $key . $val['nums']; + $process = new \swoole_process(function ($process) use ($index, $key, $val) { + if (is_null($index)) { + $index = $this->new_index; + $this->new_index++; + } + \swoole_set_process_name(sprintf('php-ps:%s', $index)); + + while (true) { + $this->job($key, $val); + self::$times++; + if (self::$times > $this->maxtimes) {//一定次数后挂掉进程,master进程会重新启动进程,这样可以防止长时间运行造成的内存泄露 + $process->exit(); + } + } + }, false, false); + return $process; + } + + public function job($key, $val) + { + $worker = new Worker(); + $worker->pop($key, $val['delay'], $val['sleep'], $val['maxTries']); + unset($worker); + } +} diff --git a/vendor/topthink/think-swoole/src/queue/Queue.php b/vendor/topthink/think-swoole/src/queue/Queue.php new file mode 100644 index 0000000..4ad3632 --- /dev/null +++ b/vendor/topthink/think-swoole/src/queue/Queue.php @@ -0,0 +1,42 @@ +[ + * "delay"=>0,//延迟时间 + * "sleep"=>3,//休息时间 + * "maxTries"=>0,//重试次数 + * "nums"=>2//进程数量 + * ], + * ] + */ + public function getConfig() + { + $config = Config::get('swoole.queue'); + $this->config = $this->getDefaultsValue($config); + return $this->config; + } + + public function getDefaultsValue($config = null) + { + if (!empty($config) && $config) { + foreach ($config as $key => $val) { + isset($config[$key]["delay"]) ? $config[$key] : ($config[$key]["delay"] = 0); + isset($config[$key]["sleep"]) ? $config[$key] : ($config[$key]["sleep"] = 3); + isset($config[$key]["maxTries"]) ? $config[$key] : ($config[$key]["maxTries"] = 0); + isset($config[$key]["nums"]) ? $config[$key] : ($config[$key]["nums"] = 1); + } + return $config; + } + return false; + } +} diff --git a/vendor/topthink/think-swoole/src/queue/Task.php b/vendor/topthink/think-swoole/src/queue/Task.php new file mode 100644 index 0000000..6e08f81 --- /dev/null +++ b/vendor/topthink/think-swoole/src/queue/Task.php @@ -0,0 +1,66 @@ +getConfig(); + $this->initTimerLists(); + } + + public static function instance() + { + if (is_null(self::$instance)) { + self::$instance = new static(); + return self::$instance; + } + return self::$instance; + } + + /** + * 备注:和Process模式不同,这里属于定时执行,每隔一段时间执行一下任务, + * 如果任务较多,可能存在都被投递到Task进程, + * 因而推荐采用Process模式,这里可能会造成Task繁忙导致其他进程任务无法消耗 + */ + public function run() + { + foreach ($this->config as $key => $val) { + if ($this->config[$key]["nexttime"] <= time()) { + $this->config[$key]["nexttime"] = time() + $val['sleep']; + for ($i = 0; $i < $val['nums']; $i++) { + $this->job($key, $val); + } + } + } + } + + public function initTimerLists() + { + if ($this->config) { + foreach ($this->config as $key => $val) { + $this->config[$key]["nexttime"] = time(); + } + } + } + + public function job($key, $val) + { + SwooleTask::async(function ($serv, $task_id, $data) use ($key, $val) { + $worker = new Worker(); + $worker->pop($key, $val['delay'], 0, $val['maxTries']); + unset($worker); + }); + } +} diff --git a/vendor/topthink/think-swoole/src/template/Task.php b/vendor/topthink/think-swoole/src/template/Task.php new file mode 100644 index 0000000..efeadea --- /dev/null +++ b/vendor/topthink/think-swoole/src/template/Task.php @@ -0,0 +1,23 @@ +initialize($args); + } + + abstract public function initialize($args); + + abstract public function run($serv, $taskId, $fromWorkerId); + +} diff --git a/vendor/topthink/think-swoole/src/template/Timer.php b/vendor/topthink/think-swoole/src/template/Timer.php new file mode 100644 index 0000000..0078469 --- /dev/null +++ b/vendor/topthink/think-swoole/src/template/Timer.php @@ -0,0 +1,23 @@ +initialize($args); + } + + abstract public function initialize($args); + + abstract public function run(); +} diff --git a/vendor/topthink/think-swoole/src/template/WorkerStart.php b/vendor/topthink/think-swoole/src/template/WorkerStart.php new file mode 100644 index 0000000..7d31d4d --- /dev/null +++ b/vendor/topthink/think-swoole/src/template/WorkerStart.php @@ -0,0 +1,27 @@ +server = $server; + $this->worker_id = $worker_id; + $this->_initialize($server, $worker_id); + } + + abstract public function _initialize($server, $worker_id); + + abstract public function run(); +} diff --git a/vendor/xavier/xcron-expression/CHANGELOG.md b/vendor/xavier/xcron-expression/CHANGELOG.md new file mode 100644 index 0000000..336e69a --- /dev/null +++ b/vendor/xavier/xcron-expression/CHANGELOG.md @@ -0,0 +1,47 @@ +# Change Log + +## [Unreleased] +### Added + +### Changed +- Dropped support for PHP 5.x +- Dropped support for the YEAR field, as it was not part of the cron standard + +### Fixed +- Reworked validation for all the field types +- Stepping should now work for 1-indexed fields like Month (#153) + +## [1.2.0] - 2017-01-22 +### Added +- Added IDE, CodeSniffer, and StyleCI.IO support + +### Changed +- Switched to PSR-4 Autoloading + +### Fixed +- 0 step expressions are handled better +- Fixed `DayOfMonth` validation to be more strict +- Typos + +## [1.1.0] - 2016-01-26 +### Added +- Support for non-hourly offset timezones +- Checks for valid expressions + +### Changed +- Max Iterations no longer hardcoded for `getRunDate()` +- Supports DateTimeImmutable for newer PHP verions + +### Fixed +- Fixed looping bug for PHP 7 when determining the last specified weekday of a month + +## [1.0.3] - 2013-11-23 +### Added +- Now supports expressions with any number of extra spaces, tabs, or newlines + +### Changed +- Using static instead of self in `CronExpression::factory` + +### Fixed +- Fixes issue [#28](https://github.com/mtdowling/cron-expression/issues/28) where PHP increments of ranges were failing due to PHP casting hyphens to 0 +- Only set default timezone if the given $currentTime is not a DateTime instance ([#34](https://github.com/mtdowling/cron-expression/issues/34)) diff --git a/vendor/xavier/xcron-expression/LICENSE b/vendor/xavier/xcron-expression/LICENSE new file mode 100644 index 0000000..c6d88ac --- /dev/null +++ b/vendor/xavier/xcron-expression/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling and contributors + +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/xavier/xcron-expression/README.md b/vendor/xavier/xcron-expression/README.md new file mode 100644 index 0000000..89a9dfb --- /dev/null +++ b/vendor/xavier/xcron-expression/README.md @@ -0,0 +1,76 @@ +PHP Cron Expression Parser +========================== + +[![Latest Stable Version](https://poser.pugx.org/mtdowling/cron-expression/v/stable.png)](https://packagist.org/packages/mtdowling/cron-expression) [![Total Downloads](https://poser.pugx.org/mtdowling/cron-expression/downloads.png)](https://packagist.org/packages/mtdowling/cron-expression) [![Build Status](https://secure.travis-ci.org/mtdowling/cron-expression.png)](http://travis-ci.org/mtdowling/cron-expression) + +**NOTE** This fork has been deprecated and development moved to [https://github.com/dragonmantank/cron-expression](https://github.com/dragonmantank/cron-expression). More information can be found in the blog post [here](http://ctankersley.com/2017/10/12/cron-expression-update/). tl;dr - v2.0.0 is a major breaking change, and @dragonmantank can better take care of the project in a separate fork. + +The PHP cron expression parser can parse a CRON expression, determine if it is +due to run, calculate the next run date of the expression, and calculate the previous +run date of the expression. You can calculate dates far into the future or past by +skipping n number of matching dates. + +The parser can handle increments of ranges (e.g. */12, 2-59/3), intervals (e.g. 0-9), +lists (e.g. 1,2,3), W to find the nearest weekday for a given day of the month, L to +find the last day of the month, L to find the last given weekday of a month, and hash +(#) to find the nth weekday of a given month. + +Installing +========== + +Add the dependency to your project: + +```bash +composer require mtdowling/cron-expression +``` + +Usage +===== +```php +isDue(); +echo $cron->getNextRunDate()->format('Y-m-d H:i:s'); +echo $cron->getPreviousRunDate()->format('Y-m-d H:i:s'); + +// Works with complex expressions +$cron = Cron\CronExpression::factory('3-59/15 2,6-12 */15 1 2-5'); +echo $cron->getNextRunDate()->format('Y-m-d H:i:s'); + +// Calculate a run date two iterations into the future +$cron = Cron\CronExpression::factory('@daily'); +echo $cron->getNextRunDate(null, 2)->format('Y-m-d H:i:s'); + +// Calculate a run date relative to a specific time +$cron = Cron\CronExpression::factory('@monthly'); +echo $cron->getNextRunDate('2010-01-12 00:00:00')->format('Y-m-d H:i:s'); +``` + +CRON Expressions +================ + +A CRON expression is a string representing the schedule for a particular command to execute. The parts of a CRON schedule are as follows: + +基于Cron Expression开发,新增了对秒的判断 根据crontab时间格式计算下一次运行时间 + + + * * * * * * + - - - - - - + | | | | | | + | | | | | | + | | | | | +----- day of week (0 - 7) (Sunday=0 or 7) + | | | | +---------- month (1 - 12) + | | | +--------------- day of month (1 - 31) + | | +-------------------- hour (0 - 23) + | +------------------------- min (0 - 59) + +------------------------------ second (0-59) +Requirements +============ + +- PHP 7.0+ +- PHPUnit is required to run the unit tests +- Composer is required to run the unit tests diff --git a/vendor/xavier/xcron-expression/composer.json b/vendor/xavier/xcron-expression/composer.json new file mode 100644 index 0000000..9632f63 --- /dev/null +++ b/vendor/xavier/xcron-expression/composer.json @@ -0,0 +1,28 @@ +{ + "name": "xavier/xcron-expression", + "type": "library", + "description": "base on Michael Dowling`s cron-expression", + "keywords": ["cron", "schedule"], + "license": "MIT", + "authors": [{ + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }], + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~5.7" + }, + "autoload": { + "psr-4": { + "XCron\\": "src/Cron/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/Cron/" + } + } +} diff --git a/vendor/xavier/xcron-expression/src/.DS_Store b/vendor/xavier/xcron-expression/src/.DS_Store new file mode 100644 index 0000000..ce0308a Binary files /dev/null and b/vendor/xavier/xcron-expression/src/.DS_Store differ diff --git a/vendor/xavier/xcron-expression/src/Cron/.DS_Store b/vendor/xavier/xcron-expression/src/Cron/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/vendor/xavier/xcron-expression/src/Cron/.DS_Store differ diff --git a/vendor/xavier/xcron-expression/src/Cron/AbstractField.php b/vendor/xavier/xcron-expression/src/Cron/AbstractField.php new file mode 100644 index 0000000..d91ada6 --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/AbstractField.php @@ -0,0 +1,252 @@ +fullRange = range($this->rangeStart, $this->rangeEnd); + } + + /** + * Check to see if a field is satisfied by a value + * + * @param string $dateValue Date value to check + * @param string $value Value to test + * + * @return bool + */ + public function isSatisfied($dateValue, $value) + { + if ($this->isIncrementsOfRanges($value)) { + return $this->isInIncrementsOfRanges($dateValue, $value); + } elseif ($this->isRange($value)) { + return $this->isInRange($dateValue, $value); + } + + return $value == '*' || $dateValue == $value; + } + + /** + * Check if a value is a range + * + * @param string $value Value to test + * + * @return bool + */ + public function isRange($value) + { + return strpos($value, '-') !== false; + } + + /** + * Check if a value is an increments of ranges + * + * @param string $value Value to test + * + * @return bool + */ + public function isIncrementsOfRanges($value) + { + return strpos($value, '/') !== false; + } + + /** + * Test if a value is within a range + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInRange($dateValue, $value) + { + $parts = array_map('trim', explode('-', $value, 2)); + + return $dateValue >= $parts[0] && $dateValue <= $parts[1]; + } + + /** + * Test if a value is within an increments of ranges (offset[-to]/step size) + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInIncrementsOfRanges($dateValue, $value) + { + $chunks = array_map('trim', explode('/', $value, 2)); + $range = $chunks[0]; + $step = isset($chunks[1]) ? $chunks[1] : 0; + + // No step or 0 steps aren't cool + if (is_null($step) || '0' === $step || 0 === $step) { + return false; + } + + // Expand the * to a full range + if ('*' == $range) { + $range = $this->rangeStart . '-' . $this->rangeEnd; + } + + // Generate the requested small range + $rangeChunks = explode('-', $range, 2); + $rangeStart = $rangeChunks[0]; + $rangeEnd = isset($rangeChunks[1]) ? $rangeChunks[1] : $rangeStart; + + if ($rangeStart < $this->rangeStart || $rangeStart > $this->rangeEnd || $rangeStart > $rangeEnd) { + throw new \OutOfRangeException('Invalid range start requested'); + } + + if ($rangeEnd < $this->rangeStart || $rangeEnd > $this->rangeEnd || $rangeEnd < $rangeStart) { + throw new \OutOfRangeException('Invalid range end requested'); + } + + if ($step > ($rangeEnd - $rangeStart) + 1) { + throw new \OutOfRangeException('Step cannot be greater than total range'); + } + + $thisRange = range($rangeStart, $rangeEnd, $step); + + return in_array($dateValue, $thisRange); + } + + /** + * Returns a range of values for the given cron expression + * + * @param string $expression The expression to evaluate + * @param int $max Maximum offset for range + * + * @return array + */ + public function getRangeForExpression($expression, $max) + { + $values = array(); + + if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) { + if (!$this->isIncrementsOfRanges($expression)) { + list ($offset, $to) = explode('-', $expression); + $stepSize = 1; + } + else { + $range = array_map('trim', explode('/', $expression, 2)); + $stepSize = isset($range[1]) ? $range[1] : 0; + $range = $range[0]; + $range = explode('-', $range, 2); + $offset = $range[0]; + $to = isset($range[1]) ? $range[1] : $max; + } + $offset = $offset == '*' ? 0 : $offset; + for ($i = $offset; $i <= $to; $i += $stepSize) { + $values[] = $i; + } + sort($values); + } + else { + $values = array($expression); + } + + return $values; + } + + protected function convertLiterals($value) + { + if (count($this->literals)) { + $key = array_search($value, $this->literals); + if ($key !== false) { + return $key; + } + } + + return $value; + } + + /** + * Checks to see if a value is valid for the field + * + * @param string $value + * @return bool + */ + public function validate($value) + { + $value = $this->convertLiterals($value); + + // All fields allow * as a valid value + if ('*' === $value) { + return true; + } + + // You cannot have a range and a list at the same time + if (strpos($value, ',') !== false && strpos($value, '-') !== false) { + return false; + } + + if (strpos($value, '/') !== false) { + list($range, $step) = explode('/', $value); + return $this->validate($range) && filter_var($step, FILTER_VALIDATE_INT); + } + + if (strpos($value, '-') !== false) { + if (substr_count($value, '-') > 1) { + return false; + } + + $chunks = explode('-', $value); + $chunks[0] = $this->convertLiterals($chunks[0]); + $chunks[1] = $this->convertLiterals($chunks[1]); + + if ('*' == $chunks[0] || '*' == $chunks[1]) { + return false; + } + + return $this->validate($chunks[0]) && $this->validate($chunks[1]); + } + + // Validate each chunk of a list individually + if (strpos($value, ',') !== false) { + foreach (explode(',', $value) as $listItem) { + if (!$this->validate($listItem)) { + return false; + } + } + return true; + } + + // We should have a numeric by now, so coerce this into an integer + if (filter_var($value, FILTER_VALIDATE_INT) !== false) { + $value = (int) $value; + } + + return in_array($value, $this->fullRange, true); + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/CronExpression.php b/vendor/xavier/xcron-expression/src/Cron/CronExpression.php new file mode 100644 index 0000000..e8cdbcf --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/CronExpression.php @@ -0,0 +1,402 @@ + '0 0 0 1 1 *', + '@annually' => '0 0 0 1 1 *', + '@monthly' => '0 0 0 1 * *', + '@weekly' => '0 0 0 * * 0', + '@daily' => '0 0 0 * * *', + '@hourly' => '0 0 * * * *' + ); + + if (isset($mappings[$expression])) { + $expression = $mappings[$expression]; + } + + return new static($expression, $fieldFactory ?: new FieldFactory()); + } + + /** + * Validate a CronExpression. + * + * @param string $expression The CRON expression to validate. + * + * @return bool True if a valid CRON expression was passed. False if not. + * @see \Cron\CronExpression::factory + */ + public static function isValidExpression($expression) + { + try { + self::factory($expression); + } catch (InvalidArgumentException $e) { + return false; + } + + return true; + } + + /** + * Parse a CRON expression + * + * @param string $expression CRON expression (e.g. '8 * * * *') + * @param FieldFactory $fieldFactory Factory to create cron fields + */ + public function __construct($expression, FieldFactory $fieldFactory) + { + $this->fieldFactory = $fieldFactory; + $this->setExpression($expression); + } + + /** + * Set or change the CRON expression + * + * @param string $value CRON expression (e.g. 8 * * * *) + * + * @return CronExpression + * @throws \InvalidArgumentException if not a valid CRON expression + */ + public function setExpression($value) + { + $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY); + if (count($this->cronParts) < 6) { + throw new InvalidArgumentException( + $value . ' is not a valid CRON expression' + ); + } + foreach ($this->cronParts as $position => $part) { + $this->setPart($position, $part); + } + + return $this; + } + + /** + * Set part of the CRON expression + * + * @param int $position The position of the CRON expression to set + * @param string $value The value to set + * + * @return CronExpression + * @throws \InvalidArgumentException if the value is not valid for the part + */ + public function setPart($position, $value) + { + if (!$this->fieldFactory->getField($position)->validate($value)) { + throw new InvalidArgumentException( + 'Invalid CRON field value ' . $value . ' at position ' . $position + ); + } + + $this->cronParts[$position] = $value; + + return $this; + } + + /** + * Set max iteration count for searching next run dates + * + * @param int $maxIterationCount Max iteration count when searching for next run date + * + * @return CronExpression + */ + public function setMaxIterationCount($maxIterationCount) + { + $this->maxIterationCount = $maxIterationCount; + + return $this; + } + + /** + * Get a next run date relative to the current date or a specific date + * + * @param string|\DateTime $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning a + * matching next run date. 0, the default, will return the current + * date and time if the next run date falls on the current date and + * time. Setting this value to 1 will skip the first match and go to + * the second match. Setting this value to 2 will skip the first 2 + * matches and so on. + * @param bool $allowCurrentDate Set to TRUE to return the current date if + * it matches the cron expression. + * @param null|string $timeZone Timezone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + */ + public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false, $timeZone = null) + { + return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate, $timeZone); + } + + /** + * Get a previous run date relative to the current date or a specific date + * + * @param string|\DateTime $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param null|string $timeZone Timezone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + * @see \Cron\CronExpression::getNextRunDate + */ + public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false, $timeZone = null) + { + return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate, $timeZone); + } + + /** + * Get multiple run dates starting at the current date or a specific date + * + * @param int $total Set the total number of dates to calculate + * @param string|\DateTime $currentTime Relative calculation date + * @param bool $invert Set to TRUE to retrieve previous dates + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param null|string $timeZone Timezone to use instead of the system default + * + * @return array Returns an array of run dates + */ + public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false, $timeZone = null) + { + $matches = array(); + for ($i = 0; $i < max(0, $total); $i++) { + try { + $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate, $timeZone); + } catch (RuntimeException $e) { + break; + } + } + + return $matches; + } + + /** + * Get all or part of the CRON expression + * + * @param string $part Specify the part to retrieve or NULL to get the full + * cron schedule string. + * + * @return string|null Returns the CRON expression, a part of the + * CRON expression, or NULL if the part was specified but not found + */ + public function getExpression($part = null) + { + if (null === $part) { + return implode(' ', $this->cronParts); + } elseif (array_key_exists($part, $this->cronParts)) { + return $this->cronParts[$part]; + } + + return null; + } + + /** + * Helper method to output the full expression. + * + * @return string Full CRON expression + */ + public function __toString() + { + return $this->getExpression(); + } + + /** + * Determine if the cron is due to run based on the current date or a + * specific date. This method assumes that the current number of + * seconds are irrelevant, and should be called once per minute. + * + * @param string|\DateTime $currentTime Relative calculation date + * @param null|string $timeZone Timezone to use instead of the system default + * + * @return bool Returns TRUE if the cron is due to run or FALSE if not + */ + public function isDue($currentTime = 'now', $timeZone = null) + { + if (is_null($timeZone)) { + $timeZone = date_default_timezone_get(); + } + + if ('now' === $currentTime) { + $currentDate = date('Y-m-d H:i'); + $currentTime = strtotime($currentDate); + } elseif ($currentTime instanceof DateTime) { + $currentDate = clone $currentTime; + // Ensure time in 'current' timezone is used + $currentDate->setTimezone(new DateTimeZone($timeZone)); + $currentDate = $currentDate->format('Y-m-d H:i'); + $currentTime = strtotime($currentDate); + } elseif ($currentTime instanceof DateTimeImmutable) { + $currentDate = DateTime::createFromFormat('U', $currentTime->format('U')); + $currentDate->setTimezone(new DateTimeZone($timeZone)); + $currentDate = $currentDate->format('Y-m-d H:i'); + $currentTime = strtotime($currentDate); + } else { + $currentTime = new DateTime($currentTime); + $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0); + $currentDate = $currentTime->format('Y-m-d H:i'); + $currentTime = $currentTime->getTimeStamp(); + } + + try { + return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime; + } catch (Exception $e) { + return false; + } + } + + /** + * Get the next or previous run date of the expression relative to a date + * + * @param string|\DateTime $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning + * @param bool $invert Set to TRUE to go backwards in time + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param string|null $timeZone Timezone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + */ + protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false, $timeZone = null) + { + if (is_null($timeZone)) { + $timeZone = date_default_timezone_get(); + } + if ($currentTime instanceof DateTime) { + $currentDate = clone $currentTime; + } elseif ($currentTime instanceof DateTimeImmutable) { + $currentDate = DateTime::createFromFormat('U', $currentTime->format('U')); + $currentDate->setTimezone($currentTime->getTimezone()); + } else { + $currentDate = new DateTime($currentTime ?: 'now'); + $currentDate->setTimezone(new DateTimeZone($timeZone)); + } + + $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), $currentDate->format('s')); + $nextRun = clone $currentDate; + $nth = (int) $nth; + + // We don't have to satisfy * or null fields + $parts = array(); + $fields = array(); + foreach (self::$order as $position) { + $part = $this->getExpression($position); + if (null === $part || '*' === $part) { + continue; + } + $parts[$position] = $part; + $fields[$position] = $this->fieldFactory->getField($position); + } + + // Set a hard limit to bail on an impossible date + for ($i = 0; $i < $this->maxIterationCount; $i++) { + foreach ($parts as $position => $part) { + $satisfied = false; + // Get the field object used to validate this part + $field = $fields[$position]; + + // Check if this is singular or a list + if (strpos($part, ',') === false) { + $satisfied = $field->isSatisfiedBy($nextRun, $part); + } else { + foreach (array_map('trim', explode(',', $part)) as $listPart) { + if ($field->isSatisfiedBy($nextRun, $listPart)) { + $satisfied = true; + break; + } + } + } + + // If the field is not satisfied, then start over + if (!$satisfied) { + $field->increment($nextRun, $invert, $part); + continue 2; + } + + } + + // Skip this match if needed + if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) { + $this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null); + continue; + } + + return $nextRun; + } + + // @codeCoverageIgnoreStart + throw new RuntimeException('Impossible CRON expression'); + // @codeCoverageIgnoreEnd + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/DayOfMonthField.php b/vendor/xavier/xcron-expression/src/Cron/DayOfMonthField.php new file mode 100644 index 0000000..822206b --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/DayOfMonthField.php @@ -0,0 +1,131 @@ + + */ +class DayOfMonthField extends AbstractField +{ + protected $rangeStart = 1; + protected $rangeEnd = 31; + + /** + * Get the nearest day of the week for a given day in a month + * + * @param int $currentYear Current year + * @param int $currentMonth Current month + * @param int $targetDay Target day of the month + * + * @return \DateTime Returns the nearest date + */ + private static function getNearestWeekday($currentYear, $currentMonth, $targetDay) + { + $tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT); + $target = DateTime::createFromFormat('Y-m-d', "$currentYear-$currentMonth-$tday"); + $currentWeekday = (int) $target->format('N'); + + if ($currentWeekday < 6) { + return $target; + } + + $lastDayOfMonth = $target->format('t'); + + foreach (array(-1, 1, -2, 2) as $i) { + $adjusted = $targetDay + $i; + if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) { + $target->setDate($currentYear, $currentMonth, $adjusted); + if ($target->format('N') < 6 && $target->format('m') == $currentMonth) { + return $target; + } + } + } + } + + public function isSatisfiedBy(DateTime $date, $value) + { + // ? states that the field value is to be skipped + if ($value == '?') { + return true; + } + + $fieldValue = $date->format('d'); + + // Check to see if this is the last day of the month + if ($value == 'L') { + return $fieldValue == $date->format('t'); + } + + // Check to see if this is the nearest weekday to a particular value + if (strpos($value, 'W')) { + // Parse the target day + $targetDay = substr($value, 0, strpos($value, 'W')); + // Find out if the current day is the nearest day of the week + return $date->format('j') == self::getNearestWeekday( + $date->format('Y'), + $date->format('m'), + $targetDay + )->format('j'); + } + + return $this->isSatisfied($date->format('d'), $value); + } + + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('previous day'); + $date->setTime(23, 59); + } else { + $date->modify('next day'); + $date->setTime(0, 0); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function validate($value) + { + $basicChecks = parent::validate($value); + + // Validate that a list don't have W or L + if (strpos($value, ',') !== false && (strpos($value, 'W') !== false || strpos($value, 'L') !== false)) { + return false; + } + + if (!$basicChecks) { + + if ($value === 'L') { + return true; + } + + if (preg_match('/^(.*)W$/', $value, $matches)) { + return $this->validate($matches[1]); + } + + return false; + } + + return $basicChecks; + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/DayOfWeekField.php b/vendor/xavier/xcron-expression/src/Cron/DayOfWeekField.php new file mode 100644 index 0000000..8848093 --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/DayOfWeekField.php @@ -0,0 +1,170 @@ + 'MON', 2 => 'TUE', 3 => 'WED', 4 => 'THU', 5 => 'FRI', 6 => 'SAT', 7 => 'SUN']; + + public function __construct() + { + $this->nthRange = range(1, 5); + parent::__construct(); + } + + public function isSatisfiedBy(DateTime $date, $value) + { + if ($value == '?') { + return true; + } + + // Convert text day of the week values to integers + $value = $this->convertLiterals($value); + + $currentYear = $date->format('Y'); + $currentMonth = $date->format('m'); + $lastDayOfMonth = $date->format('t'); + + // Find out if this is the last specific weekday of the month + if (strpos($value, 'L')) { + $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L'))); + $tdate = clone $date; + $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth); + while ($tdate->format('w') != $weekday) { + $tdateClone = new DateTime(); + $tdate = $tdateClone + ->setTimezone($tdate->getTimezone()) + ->setDate($currentYear, $currentMonth, --$lastDayOfMonth); + } + + return $date->format('j') == $lastDayOfMonth; + } + + // Handle # hash tokens + if (strpos($value, '#')) { + list($weekday, $nth) = explode('#', $value); + + if (!is_numeric($nth)) { + throw new InvalidArgumentException("Hashed weekdays must be numeric, {$nth} given"); + } else { + $nth = (int) $nth; + } + + // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601 + if ($weekday === '0') { + $weekday = 7; + } + + $weekday = $this->convertLiterals($weekday); + + // Validate the hash fields + if ($weekday < 0 || $weekday > 7) { + throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given"); + } + + if (!in_array($nth, $this->nthRange)) { + throw new InvalidArgumentException("There are never more than 5 or less than 1 of a given weekday in a month, {$nth} given"); + } + + // The current weekday must match the targeted weekday to proceed + if ($date->format('N') != $weekday) { + return false; + } + + $tdate = clone $date; + $tdate->setDate($currentYear, $currentMonth, 1); + $dayCount = 0; + $currentDay = 1; + while ($currentDay < $lastDayOfMonth + 1) { + if ($tdate->format('N') == $weekday) { + if (++$dayCount >= $nth) { + break; + } + } + $tdate->setDate($currentYear, $currentMonth, ++$currentDay); + } + + return $date->format('j') == $currentDay; + } + + // Handle day of the week values + if (strpos($value, '-')) { + $parts = explode('-', $value); + if ($parts[0] == '7') { + $parts[0] = '0'; + } elseif ($parts[1] == '0') { + $parts[1] = '7'; + } + $value = implode('-', $parts); + } + + // Test to see which Sunday to use -- 0 == 7 == Sunday + $format = in_array(7, str_split($value)) ? 'N' : 'w'; + $fieldValue = $date->format($format); + + return $this->isSatisfied($fieldValue, $value); + } + + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('-1 day'); + $date->setTime(23, 59, 0); + } else { + $date->modify('+1 day'); + $date->setTime(0, 0, 0); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function validate($value) + { + $basicChecks = parent::validate($value); + + if (!$basicChecks) { + // Handle the # value + if (strpos($value, '#') !== false) { + $chunks = explode('#', $value); + $chunks[0] = $this->convertLiterals($chunks[0]); + + if (parent::validate($chunks[0]) && is_numeric($chunks[1]) && in_array($chunks[1], $this->nthRange)) { + return true; + } + } + + if (preg_match('/^(.*)L$/', $value, $matches)) { + return $this->validate($matches[1]); + } + + return false; + } + + return $basicChecks; + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/FieldFactory.php b/vendor/xavier/xcron-expression/src/Cron/FieldFactory.php new file mode 100644 index 0000000..809c28c --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/FieldFactory.php @@ -0,0 +1,56 @@ +fields[$position])) { + switch ($position) { + case 0: + $this->fields[$position] = new SecondsField(); + break; + case 1: + $this->fields[$position] = new MinutesField(); + break; + case 2: + $this->fields[$position] = new HoursField(); + break; + case 3: + $this->fields[$position] = new DayOfMonthField(); + break; + case 4: + $this->fields[$position] = new MonthField(); + break; + case 5: + $this->fields[$position] = new DayOfWeekField(); + break; + default: + throw new InvalidArgumentException( + $position . ' is not a valid position' + ); + } + } + return $this->fields[$position]; + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/FieldInterface.php b/vendor/xavier/xcron-expression/src/Cron/FieldInterface.php new file mode 100644 index 0000000..a9ae857 --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/FieldInterface.php @@ -0,0 +1,40 @@ +isSatisfied($date->format('H'), $value); + } + + public function increment(DateTime $date, $invert = false, $parts = null) + { + // Change timezone to UTC temporarily. This will + // allow us to go back or forwards and hour even + // if DST will be changed between the hours. + if (is_null($parts) || $parts == '*') { + $timezone = $date->getTimezone(); + $date->setTimezone(new DateTimeZone('UTC')); + if ($invert) { + $date->modify('-1 hour'); + } else { + $date->modify('+1 hour'); + } + $date->setTimezone($timezone); + + $date->setTime($date->format('H'), $invert ? 59 : 0); + return $this; + } + + $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); + $hours = array(); + foreach ($parts as $part) { + $hours = array_merge($hours, $this->getRangeForExpression($part, 23)); + } + + $current_hour = $date->format('H'); + $position = $invert ? count($hours) - 1 : 0; + if (count($hours) > 1) { + for ($i = 0; $i < count($hours) - 1; $i++) { + if ((!$invert && $current_hour >= $hours[$i] && $current_hour < $hours[$i + 1]) || + ($invert && $current_hour > $hours[$i] && $current_hour <= $hours[$i + 1])) { + $position = $invert ? $i : $i + 1; + break; + } + } + } + + $hour = $hours[$position]; + if ((!$invert && $date->format('H') >= $hour) || ($invert && $date->format('H') <= $hour)) { + $date->modify(($invert ? '-' : '+') . '1 day'); + $date->setTime($invert ? 23 : 0, $invert ? 59 : 0); + } + else { + $date->setTime($hour, $invert ? 59 : 0); + } + + return $this; + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/MinutesField.php b/vendor/xavier/xcron-expression/src/Cron/MinutesField.php new file mode 100644 index 0000000..e1df39e --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/MinutesField.php @@ -0,0 +1,60 @@ +isSatisfied($date->format('i'), $value); + } + + public function increment(DateTime $date, $invert = false, $parts = null) + { + if (is_null($parts)) { + if ($invert) { + $date->modify('-1 minute'); + } else { + $date->modify('+1 minute'); + } + return $this; + } + + $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); + $minutes = array(); + foreach ($parts as $part) { + $minutes = array_merge($minutes, $this->getRangeForExpression($part, 59)); + } + + $current_minute = $date->format('i'); + $position = $invert ? count($minutes) - 1 : 0; + if (count($minutes) > 1) { + for ($i = 0; $i < count($minutes) - 1; $i++) { + if ((!$invert && $current_minute >= $minutes[$i] && $current_minute < $minutes[$i + 1]) || + ($invert && $current_minute > $minutes[$i] && $current_minute <= $minutes[$i + 1])) { + $position = $invert ? $i : $i + 1; + break; + } + } + } + + if ((!$invert && $current_minute >= $minutes[$position]) || ($invert && $current_minute <= $minutes[$position])) { + $date->modify(($invert ? '-' : '+') . '1 hour'); + $date->setTime($date->format('H'), $invert ? 59 : 0); + } + else { + $date->setTime($date->format('H'), $minutes[$position]); + } + + return $this; + } +} diff --git a/vendor/xavier/xcron-expression/src/Cron/MonthField.php b/vendor/xavier/xcron-expression/src/Cron/MonthField.php new file mode 100644 index 0000000..0bf7579 --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/MonthField.php @@ -0,0 +1,38 @@ + 'JAN', 2 => 'FEB', 3 => 'MAR', 4 => 'APR', 5 => 'MAY', 6 => 'JUN', 7 => 'JUL', + 8 => 'AUG', 9 => 'SEP', 10 => 'OCT', 11 => 'NOV', 12 => 'DEC']; + + public function isSatisfiedBy(DateTime $date, $value) + { + $value = $this->convertLiterals($value); + + return $this->isSatisfied($date->format('m'), $value); + } + + public function increment(DateTime $date, $invert = false) + { + if ($invert) { + $date->modify('last day of previous month'); + $date->setTime(23, 59); + } else { + $date->modify('first day of next month'); + $date->setTime(0, 0); + } + + return $this; + } + + +} diff --git a/vendor/xavier/xcron-expression/src/Cron/SecondsField.php b/vendor/xavier/xcron-expression/src/Cron/SecondsField.php new file mode 100644 index 0000000..e4552d4 --- /dev/null +++ b/vendor/xavier/xcron-expression/src/Cron/SecondsField.php @@ -0,0 +1,58 @@ +isSatisfied($date->format('s'), $value); + } + + public function increment(DateTime $date, $invert = false, $parts = null) + { + if (is_null($parts)) { + if ($invert) { + $date->modify('-1 second'); + } else { + $date->modify('+1 second'); + } + return $this; + } + + $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); + $seconds = array(); + foreach ($parts as $part) { + $seconds = array_merge($seconds, $this->getRangeForExpression($part, 59)); + } + + $current_second = $date->format('s'); + $position = $invert ? count($seconds) - 1 : 0; + if (count($seconds) > 1) { + for ($i = 0; $i < count($seconds) - 1; $i++) { + if ((!$invert && $current_second >= $seconds[$i] && $current_second < $seconds[$i + 1]) || + ($invert && $current_second > $seconds[$i] && $current_second <= $seconds[$i + 1])) { + $position = $invert ? $i : $i + 1; + break; + } + } + } + if ((!$invert && $current_second >= $seconds[$position]) || ($invert && $current_second <= $seconds[$position])) { + $date->modify(($invert ? '-' : '+') . '1 minute'); + $date->setTime($date->format('H'), $date->format('i'),$invert ? 59 : 0); + } + else { + $date->setTime($date->format('H'), $date->format('i'),$seconds[$position]); + } + return $this; + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/AbstractFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/AbstractFieldTest.php new file mode 100644 index 0000000..8fac834 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/AbstractFieldTest.php @@ -0,0 +1,119 @@ + + */ +class AbstractFieldTest extends TestCase +{ + /** + * @covers \Cron\AbstractField::isRange + */ + public function testTestsIfRange() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isRange('1-2')); + $this->assertFalse($f->isRange('2')); + } + + /** + * @covers \Cron\AbstractField::isIncrementsOfRanges + */ + public function testTestsIfIncrementsOfRanges() + { + $f = new DayOfWeekField(); + $this->assertFalse($f->isIncrementsOfRanges('1-2')); + $this->assertTrue($f->isIncrementsOfRanges('1/2')); + $this->assertTrue($f->isIncrementsOfRanges('*/2')); + $this->assertTrue($f->isIncrementsOfRanges('3-12/2')); + } + + /** + * @covers \Cron\AbstractField::isInRange + */ + public function testTestsIfInRange() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isInRange('1', '1-2')); + $this->assertTrue($f->isInRange('2', '1-2')); + $this->assertTrue($f->isInRange('5', '4-12')); + $this->assertFalse($f->isInRange('3', '4-12')); + $this->assertFalse($f->isInRange('13', '4-12')); + } + + /** + * @covers \Cron\AbstractField::isInIncrementsOfRanges + */ + public function testTestsIfInIncrementsOfRangesOnZeroStartRange() + { + $f = new MinutesField(); + $this->assertTrue($f->isInIncrementsOfRanges('3', '3-59/2')); + $this->assertTrue($f->isInIncrementsOfRanges('13', '3-59/2')); + $this->assertTrue($f->isInIncrementsOfRanges('15', '3-59/2')); + $this->assertTrue($f->isInIncrementsOfRanges('14', '*/2')); + $this->assertFalse($f->isInIncrementsOfRanges('2', '3-59/13')); + $this->assertFalse($f->isInIncrementsOfRanges('14', '*/13')); + $this->assertFalse($f->isInIncrementsOfRanges('14', '3-59/2')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '2-59')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '2')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '*')); + $this->assertFalse($f->isInIncrementsOfRanges('0', '*/0')); + $this->assertFalse($f->isInIncrementsOfRanges('1', '*/0')); + + $this->assertTrue($f->isInIncrementsOfRanges('4', '4/1')); + $this->assertFalse($f->isInIncrementsOfRanges('14', '4/1')); + $this->assertFalse($f->isInIncrementsOfRanges('34', '4/1')); + } + + /** + * @covers \Cron\AbstractField::isInIncrementsOfRanges + */ + public function testTestsIfInIncrementsOfRangesOnOneStartRange() + { + $f = new MonthField(); + $this->assertTrue($f->isInIncrementsOfRanges('3', '3-12/2')); + $this->assertFalse($f->isInIncrementsOfRanges('13', '3-12/2')); + $this->assertFalse($f->isInIncrementsOfRanges('15', '3-12/2')); + $this->assertTrue($f->isInIncrementsOfRanges('3', '*/2')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '*/3')); + $this->assertTrue($f->isInIncrementsOfRanges('7', '*/3')); + $this->assertFalse($f->isInIncrementsOfRanges('14', '3-12/2')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '2-12')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '2')); + $this->assertFalse($f->isInIncrementsOfRanges('3', '*')); + $this->assertFalse($f->isInIncrementsOfRanges('0', '*/0')); + $this->assertFalse($f->isInIncrementsOfRanges('1', '*/0')); + + $this->assertTrue($f->isInIncrementsOfRanges('4', '4/1')); + $this->assertFalse($f->isInIncrementsOfRanges('14', '4/1')); + $this->assertFalse($f->isInIncrementsOfRanges('34', '4/1')); + } + + public function testStepCannotBeLargerThanRange() + { + $this->expectException(\OutOfRangeException::class); + $f = new MonthField(); + $f->isInIncrementsOfRanges('2', '3-12/13'); + } + + /** + * @covers \Cron\AbstractField::isSatisfied + */ + public function testTestsIfSatisfied() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isSatisfied('12', '3-13')); + $this->assertFalse($f->isSatisfied('15', '3-7/2')); + $this->assertTrue($f->isSatisfied('12', '*')); + $this->assertTrue($f->isSatisfied('12', '12')); + $this->assertFalse($f->isSatisfied('12', '3-11')); + $this->assertFalse($f->isSatisfied('12', '3-7/2')); + $this->assertFalse($f->isSatisfied('12', '11')); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/CronExpressionTest.php b/vendor/xavier/xcron-expression/tests/Cron/CronExpressionTest.php new file mode 100644 index 0000000..61e5137 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/CronExpressionTest.php @@ -0,0 +1,444 @@ + + */ +class CronExpressionTest extends TestCase +{ + /** + * @covers \Cron\CronExpression::factory + */ + public function testFactoryRecognizesTemplates() + { + $this->assertSame('0 0 1 1 *', CronExpression::factory('@annually')->getExpression()); + $this->assertSame('0 0 1 1 *', CronExpression::factory('@yearly')->getExpression()); + $this->assertSame('0 0 * * 0', CronExpression::factory('@weekly')->getExpression()); + } + + /** + * @covers \Cron\CronExpression::__construct + * @covers \Cron\CronExpression::getExpression + * @covers \Cron\CronExpression::__toString + */ + public function testParsesCronSchedule() + { + // '2010-09-10 12:00:00' + $cron = CronExpression::factory('1 2-4 * 4,5,6 */3'); + $this->assertSame('1', $cron->getExpression(CronExpression::MINUTE)); + $this->assertSame('2-4', $cron->getExpression(CronExpression::HOUR)); + $this->assertSame('*', $cron->getExpression(CronExpression::DAY)); + $this->assertSame('4,5,6', $cron->getExpression(CronExpression::MONTH)); + $this->assertSame('*/3', $cron->getExpression(CronExpression::WEEKDAY)); + $this->assertSame('1 2-4 * 4,5,6 */3', $cron->getExpression()); + $this->assertSame('1 2-4 * 4,5,6 */3', (string) $cron); + $this->assertNull($cron->getExpression('foo')); + } + + /** + * @covers \Cron\CronExpression::__construct + * @covers \Cron\CronExpression::getExpression + * @covers \Cron\CronExpression::__toString + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid CRON field value A at position 0 + */ + public function testParsesCronScheduleThrowsAnException() + { + CronExpression::factory('A 1 2 3 4'); + } + + /** + * @covers \Cron\CronExpression::__construct + * @covers \Cron\CronExpression::getExpression + * @dataProvider scheduleWithDifferentSeparatorsProvider + */ + public function testParsesCronScheduleWithAnySpaceCharsAsSeparators($schedule, array $expected) + { + $cron = CronExpression::factory($schedule); + $this->assertSame($expected[0], $cron->getExpression(CronExpression::MINUTE)); + $this->assertSame($expected[1], $cron->getExpression(CronExpression::HOUR)); + $this->assertSame($expected[2], $cron->getExpression(CronExpression::DAY)); + $this->assertSame($expected[3], $cron->getExpression(CronExpression::MONTH)); + $this->assertSame($expected[4], $cron->getExpression(CronExpression::WEEKDAY)); + } + + /** + * Data provider for testParsesCronScheduleWithAnySpaceCharsAsSeparators + * + * @return array + */ + public static function scheduleWithDifferentSeparatorsProvider() + { + return array( + array("*\t*\t*\t*\t*\t", array('*', '*', '*', '*', '*', '*')), + array("* * * * * ", array('*', '*', '*', '*', '*', '*')), + array("* \t * \t * \t * \t * \t", array('*', '*', '*', '*', '*', '*')), + array("*\t \t*\t \t*\t \t*\t \t*\t \t", array('*', '*', '*', '*', '*', '*')), + ); + } + + /** + * @covers \Cron\CronExpression::__construct + * @covers \Cron\CronExpression::setExpression + * @covers \Cron\CronExpression::setPart + * @expectedException InvalidArgumentException + */ + public function testInvalidCronsWillFail() + { + // Only four values + $cron = CronExpression::factory('* * * 1'); + } + + /** + * @covers \Cron\CronExpression::setPart + * @expectedException InvalidArgumentException + */ + public function testInvalidPartsWillFail() + { + // Only four values + $cron = CronExpression::factory('* * * * *'); + $cron->setPart(1, 'abc'); + } + + /** + * Data provider for cron schedule + * + * @return array + */ + public function scheduleProvider() + { + return array( + array('*/2 */2 * * *', '2015-08-10 21:47:27', '2015-08-10 22:00:00', false), + array('* * * * *', '2015-08-10 21:50:37', '2015-08-10 21:50:00', true), + array('* 20,21,22 * * *', '2015-08-10 21:50:00', '2015-08-10 21:50:00', true), + // Handles CSV values + array('* 20,22 * * *', '2015-08-10 21:50:00', '2015-08-10 22:00:00', false), + // CSV values can be complex + array('7-9 * */9 * *', '2015-08-10 22:02:33', '2015-08-10 22:07:00', false), + // 15th minute, of the second hour, every 15 days, in January, every Friday + array('1 * * * 7', '2015-08-10 21:47:27', '2015-08-16 00:01:00', false), + // Test with exact times + array('47 21 * * *', strtotime('2015-08-10 21:47:30'), '2015-08-10 21:47:00', true), + // Test Day of the week (issue #1) + // According cron implementation, 0|7 = sunday, 1 => monday, etc + array('* * * * 0', strtotime('2011-06-15 23:09:00'), '2011-06-19 00:00:00', false), + array('* * * * 7', strtotime('2011-06-15 23:09:00'), '2011-06-19 00:00:00', false), + array('* * * * 1', strtotime('2011-06-15 23:09:00'), '2011-06-20 00:00:00', false), + // Should return the sunday date as 7 equals 0 + array('0 0 * * MON,SUN', strtotime('2011-06-15 23:09:00'), '2011-06-19 00:00:00', false), + array('0 0 * * 1,7', strtotime('2011-06-15 23:09:00'), '2011-06-19 00:00:00', false), + array('0 0 * * 0-4', strtotime('2011-06-15 23:09:00'), '2011-06-16 00:00:00', false), + array('0 0 * * 7-4', strtotime('2011-06-15 23:09:00'), '2011-06-16 00:00:00', false), + array('0 0 * * 4-7', strtotime('2011-06-15 23:09:00'), '2011-06-16 00:00:00', false), + array('0 0 * * 7-3', strtotime('2011-06-15 23:09:00'), '2011-06-19 00:00:00', false), + array('0 0 * * 3-7', strtotime('2011-06-15 23:09:00'), '2011-06-16 00:00:00', false), + array('0 0 * * 3-7', strtotime('2011-06-18 23:09:00'), '2011-06-19 00:00:00', false), + // Test lists of values and ranges (Abhoryo) + array('0 0 * * 2-7', strtotime('2011-06-20 23:09:00'), '2011-06-21 00:00:00', false), + array('0 0 * * 2-7', strtotime('2011-06-18 23:09:00'), '2011-06-19 00:00:00', false), + array('0 0 * * 4-7', strtotime('2011-07-19 00:00:00'), '2011-07-21 00:00:00', false), + // Test increments of ranges + array('0-12/4 * * * *', strtotime('2011-06-20 12:04:00'), '2011-06-20 12:04:00', true), + array('4-59/2 * * * *', strtotime('2011-06-20 12:04:00'), '2011-06-20 12:04:00', true), + array('4-59/2 * * * *', strtotime('2011-06-20 12:06:00'), '2011-06-20 12:06:00', true), + array('4-59/3 * * * *', strtotime('2011-06-20 12:06:00'), '2011-06-20 12:07:00', false), + // Test Day of the Week and the Day of the Month (issue #1) + array('0 0 1 1 0', strtotime('2011-06-15 23:09:00'), '2012-01-01 00:00:00', false), + array('0 0 1 JAN 0', strtotime('2011-06-15 23:09:00'), '2012-01-01 00:00:00', false), + array('0 0 1 * 0', strtotime('2011-06-15 23:09:00'), '2012-01-01 00:00:00', false), + // Test the W day of the week modifier for day of the month field + array('0 0 2W * *', strtotime('2011-07-01 00:00:00'), '2011-07-01 00:00:00', true), + array('0 0 1W * *', strtotime('2011-05-01 00:00:00'), '2011-05-02 00:00:00', false), + array('0 0 1W * *', strtotime('2011-07-01 00:00:00'), '2011-07-01 00:00:00', true), + array('0 0 3W * *', strtotime('2011-07-01 00:00:00'), '2011-07-04 00:00:00', false), + array('0 0 16W * *', strtotime('2011-07-01 00:00:00'), '2011-07-15 00:00:00', false), + array('0 0 28W * *', strtotime('2011-07-01 00:00:00'), '2011-07-28 00:00:00', false), + array('0 0 30W * *', strtotime('2011-07-01 00:00:00'), '2011-07-29 00:00:00', false), + array('0 0 31W * *', strtotime('2011-07-01 00:00:00'), '2011-07-29 00:00:00', false), + // Test the last weekday of a month + array('* * * * 5L', strtotime('2011-07-01 00:00:00'), '2011-07-29 00:00:00', false), + array('* * * * 6L', strtotime('2011-07-01 00:00:00'), '2011-07-30 00:00:00', false), + array('* * * * 7L', strtotime('2011-07-01 00:00:00'), '2011-07-31 00:00:00', false), + array('* * * * 1L', strtotime('2011-07-24 00:00:00'), '2011-07-25 00:00:00', false), + array('* * * 1 5L', strtotime('2011-12-25 00:00:00'), '2012-01-27 00:00:00', false), + // Test the hash symbol for the nth weekday of a given month + array('* * * * 5#2', strtotime('2011-07-01 00:00:00'), '2011-07-08 00:00:00', false), + array('* * * * 5#1', strtotime('2011-07-01 00:00:00'), '2011-07-01 00:00:00', true), + array('* * * * 3#4', strtotime('2011-07-01 00:00:00'), '2011-07-27 00:00:00', false), + ); + } + + /** + * @covers \Cron\CronExpression::isDue + * @covers \Cron\CronExpression::getNextRunDate + * @covers \Cron\DayOfMonthField + * @covers \Cron\DayOfWeekField + * @covers \Cron\MinutesField + * @covers \Cron\HoursField + * @covers \Cron\MonthField + * @covers \Cron\CronExpression::getRunDate + * @dataProvider scheduleProvider + */ + public function testDeterminesIfCronIsDue($schedule, $relativeTime, $nextRun, $isDue) + { + $relativeTimeString = is_int($relativeTime) ? date('Y-m-d H:i:s', $relativeTime) : $relativeTime; + + // Test next run date + $cron = CronExpression::factory($schedule); + if (is_string($relativeTime)) { + $relativeTime = new DateTime($relativeTime); + } elseif (is_int($relativeTime)) { + $relativeTime = date('Y-m-d H:i:s', $relativeTime); + } + $this->assertSame($isDue, $cron->isDue($relativeTime)); + $next = $cron->getNextRunDate($relativeTime, 0, true); + $this->assertEquals(new DateTime($nextRun), $next); + } + + /** + * @covers \Cron\CronExpression::isDue + */ + public function testIsDueHandlesDifferentDates() + { + $cron = CronExpression::factory('* * * * *'); + $this->assertTrue($cron->isDue()); + $this->assertTrue($cron->isDue('now')); + $this->assertTrue($cron->isDue(new DateTime('now'))); + $this->assertTrue($cron->isDue(date('Y-m-d H:i'))); + } + + /** + * @covers \Cron\CronExpression::isDue + */ + public function testIsDueHandlesDifferentTimezones() + { + $cron = CronExpression::factory('0 15 * * 3'); //Wednesday at 15:00 + $date = '2014-01-01 15:00'; //Wednesday + $utc = new DateTimeZone('UTC'); + $amsterdam = new DateTimeZone('Europe/Amsterdam'); + $tokyo = new DateTimeZone('Asia/Tokyo'); + + date_default_timezone_set('UTC'); + $this->assertTrue($cron->isDue(new DateTime($date, $utc))); + $this->assertFalse($cron->isDue(new DateTime($date, $amsterdam))); + $this->assertFalse($cron->isDue(new DateTime($date, $tokyo))); + + date_default_timezone_set('Europe/Amsterdam'); + $this->assertFalse($cron->isDue(new DateTime($date, $utc))); + $this->assertTrue($cron->isDue(new DateTime($date, $amsterdam))); + $this->assertFalse($cron->isDue(new DateTime($date, $tokyo))); + + date_default_timezone_set('Asia/Tokyo'); + $this->assertFalse($cron->isDue(new DateTime($date, $utc))); + $this->assertFalse($cron->isDue(new DateTime($date, $amsterdam))); + $this->assertTrue($cron->isDue(new DateTime($date, $tokyo))); + } + + /** + * @covers Cron\CronExpression::isDue + */ + public function testIsDueHandlesDifferentTimezonesAsArgument() + { + $cron = CronExpression::factory('0 15 * * 3'); //Wednesday at 15:00 + $date = '2014-01-01 15:00'; //Wednesday + $utc = new \DateTimeZone('UTC'); + $amsterdam = new \DateTimeZone('Europe/Amsterdam'); + $tokyo = new \DateTimeZone('Asia/Tokyo'); + $this->assertTrue($cron->isDue(new DateTime($date, $utc), 'UTC')); + $this->assertFalse($cron->isDue(new DateTime($date, $amsterdam), 'UTC')); + $this->assertFalse($cron->isDue(new DateTime($date, $tokyo), 'UTC')); + $this->assertFalse($cron->isDue(new DateTime($date, $utc), 'Europe/Amsterdam')); + $this->assertTrue($cron->isDue(new DateTime($date, $amsterdam), 'Europe/Amsterdam')); + $this->assertFalse($cron->isDue(new DateTime($date, $tokyo), 'Europe/Amsterdam')); + $this->assertFalse($cron->isDue(new DateTime($date, $utc), 'Asia/Tokyo')); + $this->assertFalse($cron->isDue(new DateTime($date, $amsterdam), 'Asia/Tokyo')); + $this->assertTrue($cron->isDue(new DateTime($date, $tokyo), 'Asia/Tokyo')); + } + + /** + * @covers \Cron\CronExpression::getPreviousRunDate + */ + public function testCanGetPreviousRunDates() + { + $cron = CronExpression::factory('* * * * *'); + $next = $cron->getNextRunDate('now'); + $two = $cron->getNextRunDate('now', 1); + $this->assertEquals($next, $cron->getPreviousRunDate($two)); + + $cron = CronExpression::factory('* */2 * * *'); + $next = $cron->getNextRunDate('now'); + $two = $cron->getNextRunDate('now', 1); + $this->assertEquals($next, $cron->getPreviousRunDate($two)); + + $cron = CronExpression::factory('* * * */2 *'); + $next = $cron->getNextRunDate('now'); + $two = $cron->getNextRunDate('now', 1); + $this->assertEquals($next, $cron->getPreviousRunDate($two)); + } + + /** + * @covers \Cron\CronExpression::getMultipleRunDates + */ + public function testProvidesMultipleRunDates() + { + $cron = CronExpression::factory('*/2 * * * *'); + $this->assertEquals(array( + new DateTime('2008-11-09 00:00:00'), + new DateTime('2008-11-09 00:02:00'), + new DateTime('2008-11-09 00:04:00'), + new DateTime('2008-11-09 00:06:00') + ), $cron->getMultipleRunDates(4, '2008-11-09 00:00:00', false, true)); + } + + /** + * @covers \Cron\CronExpression::getMultipleRunDates + * @covers \Cron\CronExpression::setMaxIterationCount + */ + public function testProvidesMultipleRunDatesForTheFarFuture() { + // Fails with the default 1000 iteration limit + $cron = CronExpression::factory('0 0 12 1 *'); + $cron->setMaxIterationCount(2000); + $this->assertEquals(array( + new DateTime('2016-01-12 00:00:00'), + new DateTime('2017-01-12 00:00:00'), + new DateTime('2018-01-12 00:00:00'), + new DateTime('2019-01-12 00:00:00'), + new DateTime('2020-01-12 00:00:00'), + new DateTime('2021-01-12 00:00:00'), + new DateTime('2022-01-12 00:00:00'), + new DateTime('2023-01-12 00:00:00'), + new DateTime('2024-01-12 00:00:00'), + ), $cron->getMultipleRunDates(9, '2015-04-28 00:00:00', false, true)); + } + + /** + * @covers \Cron\CronExpression + */ + public function testCanIterateOverNextRuns() + { + $cron = CronExpression::factory('@weekly'); + $nextRun = $cron->getNextRunDate("2008-11-09 08:00:00"); + $this->assertEquals($nextRun, new DateTime("2008-11-16 00:00:00")); + + // true is cast to 1 + $nextRun = $cron->getNextRunDate("2008-11-09 00:00:00", true, true); + $this->assertEquals($nextRun, new DateTime("2008-11-16 00:00:00")); + + // You can iterate over them + $nextRun = $cron->getNextRunDate($cron->getNextRunDate("2008-11-09 00:00:00", 1, true), 1, true); + $this->assertEquals($nextRun, new DateTime("2008-11-23 00:00:00")); + + // You can skip more than one + $nextRun = $cron->getNextRunDate("2008-11-09 00:00:00", 2, true); + $this->assertEquals($nextRun, new DateTime("2008-11-23 00:00:00")); + $nextRun = $cron->getNextRunDate("2008-11-09 00:00:00", 3, true); + $this->assertEquals($nextRun, new DateTime("2008-11-30 00:00:00")); + } + + /** + * @covers \Cron\CronExpression::getRunDate + */ + public function testSkipsCurrentDateByDefault() + { + $cron = CronExpression::factory('* * * * *'); + $current = new DateTime('now'); + $next = $cron->getNextRunDate($current); + $nextPrev = $cron->getPreviousRunDate($next); + $this->assertSame($current->format('Y-m-d H:i:00'), $nextPrev->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\CronExpression::getRunDate + * @ticket 7 + */ + public function testStripsForSeconds() + { + $cron = CronExpression::factory('* * * * *'); + $current = new DateTime('2011-09-27 10:10:54'); + $this->assertSame('2011-09-27 10:11:00', $cron->getNextRunDate($current)->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\CronExpression::getRunDate + */ + public function testFixesPhpBugInDateIntervalMonth() + { + $cron = CronExpression::factory('0 0 27 JAN *'); + $this->assertSame('2011-01-27 00:00:00', $cron->getPreviousRunDate('2011-08-22 00:00:00')->format('Y-m-d H:i:s')); + } + + public function testIssue29() + { + $cron = CronExpression::factory('@weekly'); + $this->assertSame( + '2013-03-10 00:00:00', + $cron->getPreviousRunDate('2013-03-17 00:00:00')->format('Y-m-d H:i:s') + ); + } + + /** + * @see https://github.com/mtdowling/cron-expression/issues/20 + */ + public function testIssue20() { + $e = CronExpression::factory('* * * * MON#1'); + $this->assertTrue($e->isDue(new DateTime('2014-04-07 00:00:00'))); + $this->assertFalse($e->isDue(new DateTime('2014-04-14 00:00:00'))); + $this->assertFalse($e->isDue(new DateTime('2014-04-21 00:00:00'))); + + $e = CronExpression::factory('* * * * SAT#2'); + $this->assertFalse($e->isDue(new DateTime('2014-04-05 00:00:00'))); + $this->assertTrue($e->isDue(new DateTime('2014-04-12 00:00:00'))); + $this->assertFalse($e->isDue(new DateTime('2014-04-19 00:00:00'))); + + $e = CronExpression::factory('* * * * SUN#3'); + $this->assertFalse($e->isDue(new DateTime('2014-04-13 00:00:00'))); + $this->assertTrue($e->isDue(new DateTime('2014-04-20 00:00:00'))); + $this->assertFalse($e->isDue(new DateTime('2014-04-27 00:00:00'))); + } + + /** + * @covers \Cron\CronExpression::getRunDate + */ + public function testKeepOriginalTime() + { + $now = new \DateTime; + $strNow = $now->format(DateTime::ISO8601); + $cron = CronExpression::factory('0 0 * * *'); + $cron->getPreviousRunDate($now); + $this->assertSame($strNow, $now->format(DateTime::ISO8601)); + } + + /** + * @covers \Cron\CronExpression::__construct + * @covers \Cron\CronExpression::factory + * @covers \Cron\CronExpression::isValidExpression + * @covers \Cron\CronExpression::setExpression + * @covers \Cron\CronExpression::setPart + */ + public function testValidationWorks() + { + // Invalid. Only four values + $this->assertFalse(CronExpression::isValidExpression('* * * 1')); + // Valid + $this->assertTrue(CronExpression::isValidExpression('* * * * 1')); + + // Issue #156, 13 is an invalid month + $this->assertFalse(CronExpression::isValidExpression("* * * 13 * ")); + + // Issue #155, 90 is an invalid second + $this->assertFalse(CronExpression::isValidExpression('90 * * * *')); + + // Issue #154, 24 is an invalid hour + $this->assertFalse(CronExpression::isValidExpression("0 24 1 12 0")); + + // Issue #125, this is just all sorts of wrong + $this->assertFalse(CronExpression::isValidExpression('990 14 * * mon-fri0345345')); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/DayOfMonthFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/DayOfMonthFieldTest.php new file mode 100644 index 0000000..b728fe9 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/DayOfMonthFieldTest.php @@ -0,0 +1,63 @@ + + */ +class DayOfMonthFieldTest extends TestCase +{ + /** + * @covers \Cron\DayOfMonthField::validate + */ + public function testValidatesField() + { + $f = new DayOfMonthField(); + $this->assertTrue($f->validate('1')); + $this->assertTrue($f->validate('*')); + $this->assertTrue($f->validate('L')); + $this->assertTrue($f->validate('5W')); + $this->assertFalse($f->validate('5W,L')); + $this->assertFalse($f->validate('1.')); + } + + /** + * @covers \Cron\DayOfMonthField::isSatisfiedBy + */ + public function testChecksIfSatisfied() + { + $f = new DayOfMonthField(); + $this->assertTrue($f->isSatisfiedBy(new DateTime(), '?')); + } + + /** + * @covers \Cron\DayOfMonthField::increment + */ + public function testIncrementsDate() + { + $d = new DateTime('2011-03-15 11:15:00'); + $f = new DayOfMonthField(); + $f->increment($d); + $this->assertSame('2011-03-16 00:00:00', $d->format('Y-m-d H:i:s')); + + $d = new DateTime('2011-03-15 11:15:00'); + $f->increment($d, true); + $this->assertSame('2011-03-14 23:59:00', $d->format('Y-m-d H:i:s')); + } + + /** + * Day of the month cannot accept a 0 value, it must be between 1 and 31 + * See Github issue #120 + * + * @since 2017-01-22 + */ + public function testDoesNotAccept0Date() + { + $f = new DayOfMonthField(); + $this->assertFalse($f->validate(0)); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/DayOfWeekFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/DayOfWeekFieldTest.php new file mode 100644 index 0000000..f8b0232 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/DayOfWeekFieldTest.php @@ -0,0 +1,117 @@ + + */ +class DayOfWeekFieldTest extends TestCase +{ + /** + * @covers \Cron\DayOfWeekField::validate + */ + public function testValidatesField() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->validate('1')); + $this->assertTrue($f->validate('*')); + $this->assertFalse($f->validate('*/3,1,1-12')); + $this->assertTrue($f->validate('SUN-2')); + $this->assertFalse($f->validate('1.')); + } + + /** + * @covers \Cron\DayOfWeekField::isSatisfiedBy + */ + public function testChecksIfSatisfied() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isSatisfiedBy(new DateTime(), '?')); + } + + /** + * @covers \Cron\DayOfWeekField::increment + */ + public function testIncrementsDate() + { + $d = new DateTime('2011-03-15 11:15:00'); + $f = new DayOfWeekField(); + $f->increment($d); + $this->assertSame('2011-03-16 00:00:00', $d->format('Y-m-d H:i:s')); + + $d = new DateTime('2011-03-15 11:15:00'); + $f->increment($d, true); + $this->assertSame('2011-03-14 23:59:00', $d->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\DayOfWeekField::isSatisfiedBy + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Weekday must be a value between 0 and 7. 12 given + */ + public function testValidatesHashValueWeekday() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isSatisfiedBy(new DateTime(), '12#1')); + } + + /** + * @covers \Cron\DayOfWeekField::isSatisfiedBy + * @expectedException InvalidArgumentException + * @expectedExceptionMessage There are never more than 5 or less than 1 of a given weekday in a month + */ + public function testValidatesHashValueNth() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isSatisfiedBy(new DateTime(), '3#6')); + } + + /** + * @covers \Cron\DayOfWeekField::validate + */ + public function testValidateWeekendHash() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->validate('MON#1')); + $this->assertTrue($f->validate('TUE#2')); + $this->assertTrue($f->validate('WED#3')); + $this->assertTrue($f->validate('THU#4')); + $this->assertTrue($f->validate('FRI#5')); + $this->assertTrue($f->validate('SAT#1')); + $this->assertTrue($f->validate('SUN#3')); + $this->assertTrue($f->validate('MON#1,MON#3')); + } + + /** + * @covers \Cron\DayOfWeekField::isSatisfiedBy + */ + public function testHandlesZeroAndSevenDayOfTheWeekValues() + { + $f = new DayOfWeekField(); + $this->assertTrue($f->isSatisfiedBy(new DateTime('2011-09-04 00:00:00'), '0-2')); + $this->assertTrue($f->isSatisfiedBy(new DateTime('2011-09-04 00:00:00'), '6-0')); + + $this->assertTrue($f->isSatisfiedBy(new DateTime('2014-04-20 00:00:00'), 'SUN')); + $this->assertTrue($f->isSatisfiedBy(new DateTime('2014-04-20 00:00:00'), 'SUN#3')); + $this->assertTrue($f->isSatisfiedBy(new DateTime('2014-04-20 00:00:00'), '0#3')); + $this->assertTrue($f->isSatisfiedBy(new DateTime('2014-04-20 00:00:00'), '7#3')); + } + + /** + * @see https://github.com/mtdowling/cron-expression/issues/47 + */ + public function testIssue47() { + $f = new DayOfWeekField(); + $this->assertFalse($f->validate('mon,')); + $this->assertFalse($f->validate('mon-')); + $this->assertFalse($f->validate('*/2,')); + $this->assertFalse($f->validate('-mon')); + $this->assertFalse($f->validate(',1')); + $this->assertFalse($f->validate('*-')); + $this->assertFalse($f->validate(',-')); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/FieldFactoryTest.php b/vendor/xavier/xcron-expression/tests/Cron/FieldFactoryTest.php new file mode 100644 index 0000000..a6e66b0 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/FieldFactoryTest.php @@ -0,0 +1,42 @@ + + */ +class FieldFactoryTest extends TestCase +{ + /** + * @covers \Cron\FieldFactory::getField + */ + public function testRetrievesFieldInstances() + { + $mappings = array( + 0 => 'Cron\MinutesField', + 1 => 'Cron\HoursField', + 2 => 'Cron\DayOfMonthField', + 3 => 'Cron\MonthField', + 4 => 'Cron\DayOfWeekField', + ); + + $f = new FieldFactory(); + + foreach ($mappings as $position => $class) { + $this->assertSame($class, get_class($f->getField($position))); + } + } + + /** + * @covers \Cron\FieldFactory::getField + * @expectedException InvalidArgumentException + */ + public function testValidatesFieldPosition() + { + $f = new FieldFactory(); + $f->getField(-1); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/HoursFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/HoursFieldTest.php new file mode 100644 index 0000000..03f0909 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/HoursFieldTest.php @@ -0,0 +1,75 @@ + + */ +class HoursFieldTest extends TestCase +{ + /** + * @covers \Cron\HoursField::validate + */ + public function testValidatesField() + { + $f = new HoursField(); + $this->assertTrue($f->validate('1')); + $this->assertTrue($f->validate('*')); + $this->assertFalse($f->validate('*/3,1,1-12')); + } + + /** + * @covers \Cron\HoursField::increment + */ + public function testIncrementsDate() + { + $d = new DateTime('2011-03-15 11:15:00'); + $f = new HoursField(); + $f->increment($d); + $this->assertSame('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s')); + + $d->setTime(11, 15, 0); + $f->increment($d, true); + $this->assertSame('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\HoursField::increment + */ + public function testIncrementsDateWithThirtyMinuteOffsetTimezone() + { + $tz = date_default_timezone_get(); + date_default_timezone_set('America/St_Johns'); + $d = new DateTime('2011-03-15 11:15:00'); + $f = new HoursField(); + $f->increment($d); + $this->assertSame('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s')); + + $d->setTime(11, 15, 0); + $f->increment($d, true); + $this->assertSame('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s')); + date_default_timezone_set($tz); + } + + /** + * @covers \Cron\HoursField::increment + */ + public function testIncrementDateWithFifteenMinuteOffsetTimezone() + { + $tz = date_default_timezone_get(); + date_default_timezone_set('Asia/Kathmandu'); + $d = new DateTime('2011-03-15 11:15:00'); + $f = new HoursField(); + $f->increment($d); + $this->assertSame('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s')); + + $d->setTime(11, 15, 0); + $f->increment($d, true); + $this->assertSame('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s')); + date_default_timezone_set($tz); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/MinutesFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/MinutesFieldTest.php new file mode 100644 index 0000000..b91bffa --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/MinutesFieldTest.php @@ -0,0 +1,51 @@ + + */ +class MinutesFieldTest extends TestCase +{ + /** + * @covers \Cron\MinutesField::validate + */ + public function testValidatesField() + { + $f = new MinutesField(); + $this->assertTrue($f->validate('1')); + $this->assertTrue($f->validate('*')); + $this->assertFalse($f->validate('*/3,1,1-12')); + } + + /** + * @covers \Cron\MinutesField::increment + */ + public function testIncrementsDate() + { + $d = new DateTime('2011-03-15 11:15:00'); + $f = new MinutesField(); + $f->increment($d); + $this->assertSame('2011-03-15 11:16:00', $d->format('Y-m-d H:i:s')); + $f->increment($d, true); + $this->assertSame('2011-03-15 11:15:00', $d->format('Y-m-d H:i:s')); + } + + /** + * Various bad syntaxes that are reported to work, but shouldn't. + * + * @author Chris Tankersley + * @since 2017-08-18 + */ + public function testBadSyntaxesShouldNotValidate() + { + $f = new MinutesField(); + $this->assertFalse($f->validate('*-1')); + $this->assertFalse($f->validate('1-2-3')); + $this->assertFalse($f->validate('-1')); + } +} diff --git a/vendor/xavier/xcron-expression/tests/Cron/MonthFieldTest.php b/vendor/xavier/xcron-expression/tests/Cron/MonthFieldTest.php new file mode 100644 index 0000000..83f0f16 --- /dev/null +++ b/vendor/xavier/xcron-expression/tests/Cron/MonthFieldTest.php @@ -0,0 +1,81 @@ + + */ +class MonthFieldTest extends TestCase +{ + /** + * @covers \Cron\MonthField::validate + */ + public function testValidatesField() + { + $f = new MonthField(); + $this->assertTrue($f->validate('12')); + $this->assertTrue($f->validate('*')); + $this->assertFalse($f->validate('*/10,2,1-12')); + $this->assertFalse($f->validate('1.fix-regexp')); + } + + /** + * @covers \Cron\MonthField::increment + */ + public function testIncrementsDate() + { + $d = new DateTime('2011-03-15 11:15:00'); + $f = new MonthField(); + $f->increment($d); + $this->assertSame('2011-04-01 00:00:00', $d->format('Y-m-d H:i:s')); + + $d = new DateTime('2011-03-15 11:15:00'); + $f->increment($d, true); + $this->assertSame('2011-02-28 23:59:00', $d->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\MonthField::increment + */ + public function testIncrementsDateWithThirtyMinuteTimezone() + { + $tz = date_default_timezone_get(); + date_default_timezone_set('America/St_Johns'); + $d = new DateTime('2011-03-31 11:59:59'); + $f = new MonthField(); + $f->increment($d); + $this->assertSame('2011-04-01 00:00:00', $d->format('Y-m-d H:i:s')); + + $d = new DateTime('2011-03-15 11:15:00'); + $f->increment($d, true); + $this->assertSame('2011-02-28 23:59:00', $d->format('Y-m-d H:i:s')); + date_default_timezone_set($tz); + } + + + /** + * @covers \Cron\MonthField::increment + */ + public function testIncrementsYearAsNeeded() + { + $f = new MonthField(); + $d = new DateTime('2011-12-15 00:00:00'); + $f->increment($d); + $this->assertSame('2012-01-01 00:00:00', $d->format('Y-m-d H:i:s')); + } + + /** + * @covers \Cron\MonthField::increment + */ + public function testDecrementsYearAsNeeded() + { + $f = new MonthField(); + $d = new DateTime('2011-01-15 00:00:00'); + $f->increment($d, true); + $this->assertSame('2010-12-31 23:59:00', $d->format('Y-m-d H:i:s')); + } +} diff --git a/vendor/zoujingli/ip2region/.gitignore b/vendor/zoujingli/ip2region/.gitignore new file mode 100644 index 0000000..12a35fa --- /dev/null +++ b/vendor/zoujingli/ip2region/.gitignore @@ -0,0 +1,2 @@ +# Created by .ignore support plugin (hsz.mobi) +/.idea diff --git a/vendor/zoujingli/ip2region/Ip2Region.php b/vendor/zoujingli/ip2region/Ip2Region.php new file mode 100644 index 0000000..196542f --- /dev/null +++ b/vendor/zoujingli/ip2region/Ip2Region.php @@ -0,0 +1,330 @@ + + * @date 2015-10-29 + */ + +defined('INDEX_BLOCK_LENGTH') or define('INDEX_BLOCK_LENGTH', 12); +defined('TOTAL_HEADER_LENGTH') or define('TOTAL_HEADER_LENGTH', 8192); + +class Ip2Region +{ + /** + * db file handler + */ + private $dbFileHandler = null; + + /** + * header block info + */ + private $HeaderSip = null; + private $HeaderPtr = null; + private $headerLen = 0; + + /** + * super block index info + */ + private $firstIndexPtr = 0; + private $lastIndexPtr = 0; + private $totalBlocks = 0; + + /** + * for memory mode only + * the original db binary string + */ + private $dbBinStr = null; + private $dbFile = null; + + /** + * construct method + * + * @param ip2regionFile + */ + public function __construct($ip2regionFile = null) + { + $this->dbFile = is_null($ip2regionFile) ? __DIR__ . '/ip2region.db' : $ip2regionFile; + } + + /** + * all the db binary string will be loaded into memory + * then search the memory only and this will a lot faster than disk base search + * @Note: + * invoke it once before put it to public invoke could make it thread safe + * + * @param string $ip + * @return array|null + * @throws Exception + */ + public function memorySearch($ip) + { + //check and load the binary string for the first time + if ($this->dbBinStr == null) { + $this->dbBinStr = file_get_contents($this->dbFile); + if ($this->dbBinStr == false) { + throw new Exception("Fail to open the db file {$this->dbFile}"); + } + $this->firstIndexPtr = self::getLong($this->dbBinStr, 0); + $this->lastIndexPtr = self::getLong($this->dbBinStr, 4); + $this->totalBlocks = ($this->lastIndexPtr - $this->firstIndexPtr) / INDEX_BLOCK_LENGTH + 1; + } + if (is_string($ip)) $ip = self::safeIp2long($ip); + //binary search to define the data + $l = 0; + $h = $this->totalBlocks; + $dataPtr = 0; + while ($l <= $h) { + $m = (($l + $h) >> 1); + $p = $this->firstIndexPtr + $m * INDEX_BLOCK_LENGTH; + $sip = self::getLong($this->dbBinStr, $p); + if ($ip < $sip) { + $h = $m - 1; + } else { + $eip = self::getLong($this->dbBinStr, $p + 4); + if ($ip > $eip) { + $l = $m + 1; + } else { + $dataPtr = self::getLong($this->dbBinStr, $p + 8); + break; + } + } + } + //not matched just stop it here + if ($dataPtr == 0) return null; + //get the data + $dataLen = (($dataPtr >> 24) & 0xFF); + $dataPtr = ($dataPtr & 0x00FFFFFF); + return array( + 'city_id' => self::getLong($this->dbBinStr, $dataPtr), + 'region' => substr($this->dbBinStr, $dataPtr + 4, $dataLen - 4), + ); + } + + /** + * get the data block throught the specifield ip address or long ip numeric with binary search algorithm + * + * @param string ip + * @return mixed Array or NULL for any error + * @throws Exception + */ + public function binarySearch($ip) + { + //check and conver the ip address + if (is_string($ip)) $ip = self::safeIp2long($ip); + if ($this->totalBlocks == 0) { + //check and open the original db file + if ($this->dbFileHandler == null) { + $this->dbFileHandler = fopen($this->dbFile, 'r'); + if ($this->dbFileHandler == false) { + throw new Exception("Fail to open the db file {$this->dbFile}"); + } + } + fseek($this->dbFileHandler, 0); + $superBlock = fread($this->dbFileHandler, 8); + $this->firstIndexPtr = self::getLong($superBlock, 0); + $this->lastIndexPtr = self::getLong($superBlock, 4); + $this->totalBlocks = ($this->lastIndexPtr - $this->firstIndexPtr) / INDEX_BLOCK_LENGTH + 1; + } + //binary search to define the data + $l = 0; + $h = $this->totalBlocks; + $dataPtr = 0; + while ($l <= $h) { + $m = (($l + $h) >> 1); + $p = $m * INDEX_BLOCK_LENGTH; + fseek($this->dbFileHandler, $this->firstIndexPtr + $p); + $buffer = fread($this->dbFileHandler, INDEX_BLOCK_LENGTH); + $sip = self::getLong($buffer, 0); + if ($ip < $sip) { + $h = $m - 1; + } else { + $eip = self::getLong($buffer, 4); + if ($ip > $eip) { + $l = $m + 1; + } else { + $dataPtr = self::getLong($buffer, 8); + break; + } + } + } + //not matched just stop it here + if ($dataPtr == 0) return null; + //get the data + $dataLen = (($dataPtr >> 24) & 0xFF); + $dataPtr = ($dataPtr & 0x00FFFFFF); + fseek($this->dbFileHandler, $dataPtr); + $data = fread($this->dbFileHandler, $dataLen); + return array('city_id' => self::getLong($data, 0), 'region' => substr($data, 4)); + } + + /** + * get the data block associated with the specifield ip with b-tree search algorithm + * @Note: not thread safe + * + * @param string ip + * @return Mixed Array for NULL for any error + * @throws Exception + */ + public function btreeSearch($ip) + { + if (is_string($ip)) $ip = self::safeIp2long($ip); + //check and load the header + if ($this->HeaderSip == null) { + //check and open the original db file + if ($this->dbFileHandler == null) { + $this->dbFileHandler = fopen($this->dbFile, 'r'); + if ($this->dbFileHandler == false) { + throw new Exception("Fail to open the db file {$this->dbFile}"); + } + } + fseek($this->dbFileHandler, 8); + $buffer = fread($this->dbFileHandler, TOTAL_HEADER_LENGTH); + //fill the header + $idx = 0; + $this->HeaderSip = array(); + $this->HeaderPtr = array(); + for ($i = 0; $i < TOTAL_HEADER_LENGTH; $i += 8) { + $startIp = self::getLong($buffer, $i); + $dataPtr = self::getLong($buffer, $i + 4); + if ($dataPtr == 0) break; + + $this->HeaderSip[] = $startIp; + $this->HeaderPtr[] = $dataPtr; + $idx++; + } + $this->headerLen = $idx; + } + //1. define the index block with the binary search + $l = 0; + $h = $this->headerLen; + $sptr = 0; + $eptr = 0; + while ($l <= $h) { + $m = (($l + $h) >> 1); + //perfetc matched, just return it + if ($ip == $this->HeaderSip[$m]) { + if ($m > 0) { + $sptr = $this->HeaderPtr[$m - 1]; + $eptr = $this->HeaderPtr[$m]; + } else { + $sptr = $this->HeaderPtr[$m]; + $eptr = $this->HeaderPtr[$m + 1]; + } + + break; + } + //less then the middle value + if ($ip < $this->HeaderSip[$m]) { + if ($m == 0) { + $sptr = $this->HeaderPtr[$m]; + $eptr = $this->HeaderPtr[$m + 1]; + break; + } elseif ($ip > $this->HeaderSip[$m - 1]) { + $sptr = $this->HeaderPtr[$m - 1]; + $eptr = $this->HeaderPtr[$m]; + break; + } + $h = $m - 1; + } else { + if ($m == $this->headerLen - 1) { + $sptr = $this->HeaderPtr[$m - 1]; + $eptr = $this->HeaderPtr[$m]; + break; + } elseif ($ip <= $this->HeaderSip[$m + 1]) { + $sptr = $this->HeaderPtr[$m]; + $eptr = $this->HeaderPtr[$m + 1]; + break; + } + $l = $m + 1; + } + } + //match nothing just stop it + if ($sptr == 0) return null; + //2. search the index blocks to define the data + $blockLen = $eptr - $sptr; + fseek($this->dbFileHandler, $sptr); + $index = fread($this->dbFileHandler, $blockLen + INDEX_BLOCK_LENGTH); + $dataptr = 0; + $l = 0; + $h = $blockLen / INDEX_BLOCK_LENGTH; + while ($l <= $h) { + $m = (($l + $h) >> 1); + $p = (int)($m * INDEX_BLOCK_LENGTH); + $sip = self::getLong($index, $p); + if ($ip < $sip) { + $h = $m - 1; + } else { + $eip = self::getLong($index, $p + 4); + if ($ip > $eip) { + $l = $m + 1; + } else { + $dataptr = self::getLong($index, $p + 8); + break; + } + } + } + //not matched + if ($dataptr == 0) return null; + //3. get the data + $dataLen = (($dataptr >> 24) & 0xFF); + $dataPtr = ($dataptr & 0x00FFFFFF); + fseek($this->dbFileHandler, $dataPtr); + $data = fread($this->dbFileHandler, $dataLen); + return array('city_id' => self::getLong($data, 0), 'region' => substr($data, 4)); + } + + + /** + * safe self::safeIp2long function + * + * @param string ip + * @return string + */ + public static function safeIp2long($ip) + { + $ip = ip2long($ip); + // convert signed int to unsigned int if on 32 bit operating system + if ($ip < 0 && PHP_INT_SIZE == 4) { + $ip = sprintf("%u", $ip); + } + + return $ip; + } + + + /** + * read a long from a byte buffer + * + * @param integer b + * @param integer offset + * @return string + */ + public static function getLong($b, $offset) + { + $val = ( + (ord($b[$offset++])) | + (ord($b[$offset++]) << 8) | + (ord($b[$offset++]) << 16) | + (ord($b[$offset]) << 24) + ); + // convert signed int to unsigned int if on 32 bit operating system + if ($val < 0 && PHP_INT_SIZE == 4) { + $val = sprintf("%u", $val); + } + return $val; + } + + /** + * destruct method, resource destroy + */ + public function __destruct() + { + if ($this->dbFileHandler != null) { + fclose($this->dbFileHandler); + } + $this->dbBinStr = null; + $this->HeaderSip = null; + $this->HeaderPtr = null; + } +} diff --git a/vendor/zoujingli/ip2region/LICENSE.md b/vendor/zoujingli/ip2region/LICENSE.md new file mode 100644 index 0000000..739ef00 --- /dev/null +++ b/vendor/zoujingli/ip2region/LICENSE.md @@ -0,0 +1,225 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +========================================================================== +The following license applies to the ip2region library +-------------------------------------------------------------------------- +Copyright (c) 2015 Lionsoul + +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/zoujingli/ip2region/README.md b/vendor/zoujingli/ip2region/README.md new file mode 100644 index 0000000..2539ff6 --- /dev/null +++ b/vendor/zoujingli/ip2region/README.md @@ -0,0 +1,50 @@ +[![Latest Stable Version](https://poser.pugx.org/zoujingli/ip2region/v/stable)](https://packagist.org/packages/zoujingli/ip2region) +[![Total Downloads](https://poser.pugx.org/zoujingli/ip2region/downloads)](https://packagist.org/packages/zoujingli/ip2region) +[![Latest Unstable Version](https://poser.pugx.org/zoujingli/ip2region/v/unstable)](https://packagist.org/packages/zoujingli/ip2region) +[![License](https://poser.pugx.org/zoujingli/ip2region/license)](https://packagist.org/packages/zoujingli/ip2region) + + +本库基于 [ip2region](https://github.com/lionsoul2014/ip2region) ,简单整合方便使用`composer`来管理。 +-- + +[ip2region](https://github.com/lionsoul2014/ip2region) - 最自由的ip地址查询库,ip到地区的映射库,提供Binary,B树和纯内存三种查询算法,妈妈再也不用担心我的ip地址定位。 + +### 1. 99.9%准确率,定时更新: + +数据聚合了一些知名ip到地名查询提供商的数据,这些是他们官方的的准确率,经测试着实比纯真啥的准确多了。
+每次聚合一下数据需要1-2天,会不定时更新。 + +### 2. 标准化的数据格式: + +每条ip数据段都固定了格式:_城市Id|国家|区域|省份|城市|ISP_ + +只有中国的数据精确到了城市,其他国家只能定位到国家,后前的选项全部是0,已经包含了全部你能查到的大大小小的国家。 +(请忽略前面的城市Id,个人项目需求) + +### 3. 体积小: + +数据库文件ip2region.db只有1.5M + +### Composer 安装 + +``` +composer require zoujingli/ip2region +``` + +### ip2region 使用 +```php + +$ip2region = new Ip2Region(); + +$ip = '101.105.35.57'; + +$info = $ip2region->btreeSearch($ip); + +var_export($info, true); + +// array ( +// 'city_id' => 2163, +// 'region' => '中国|华南|广东省|深圳市|鹏博士', +// ) + +``` diff --git a/vendor/zoujingli/ip2region/composer.json b/vendor/zoujingli/ip2region/composer.json new file mode 100644 index 0000000..a64046e --- /dev/null +++ b/vendor/zoujingli/ip2region/composer.json @@ -0,0 +1,25 @@ +{ + "type": "library", + "name": "zoujingli/ip2region", + "homepage": "https://github.com/zoujingli/Ip2Region", + "description": "Ip2Region for PHP", + "license": "Apache-2.0", + "authors": [ + { + "name": "Anyon", + "email": "zoujingli@qq.com", + "homepage": "http://ctolog.com" + } + ], + "keywords": [ + "Ip2Region" + ], + "require": { + "php": ">=5.3" + }, + "autoload": { + "classmap": [ + "Ip2Region.php" + ] + } +} diff --git a/vendor/zoujingli/ip2region/ip2region.db b/vendor/zoujingli/ip2region/ip2region.db new file mode 100644 index 0000000..5200feb Binary files /dev/null and b/vendor/zoujingli/ip2region/ip2region.db differ diff --git a/vendor/zoujingli/ip2region/test.php b/vendor/zoujingli/ip2region/test.php new file mode 100644 index 0000000..cd6bdb9 --- /dev/null +++ b/vendor/zoujingli/ip2region/test.php @@ -0,0 +1,15 @@ +btreeSearch($ip); + +var_export($info); + +// array ( +// 'city_id' => 2163, +// 'region' => '中国|华南|广东省|深圳市|鹏博士', +// ) \ No newline at end of file