update 1.0.8.0
Commits for version update
This commit is contained in:
		
							
								
								
									
										47
									
								
								vendor/mtdowling/jmespath.php/src/AstRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								vendor/mtdowling/jmespath.php/src/AstRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Uses an external tree visitor to interpret an AST. | ||||
|  */ | ||||
| class AstRuntime | ||||
| { | ||||
|     private $parser; | ||||
|     private $interpreter; | ||||
|     private $cache = []; | ||||
|     private $cachedCount = 0; | ||||
|  | ||||
|     public function __construct( | ||||
|         Parser $parser = null, | ||||
|         callable $fnDispatcher = null | ||||
|     ) { | ||||
|         $fnDispatcher = $fnDispatcher ?: FnDispatcher::getInstance(); | ||||
|         $this->interpreter = new TreeInterpreter($fnDispatcher); | ||||
|         $this->parser = $parser ?: new Parser(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns data from the provided input that matches a given JMESPath | ||||
|      * expression. | ||||
|      * | ||||
|      * @param string $expression JMESPath expression to evaluate | ||||
|      * @param mixed  $data       Data to search. This data should be data that | ||||
|      *                           is similar to data returned from json_decode | ||||
|      *                           using associative arrays rather than objects. | ||||
|      * | ||||
|      * @return mixed|null Returns the matching data or null | ||||
|      */ | ||||
|     public function __invoke($expression, $data) | ||||
|     { | ||||
|         if (!isset($this->cache[$expression])) { | ||||
|             // Clear the AST cache when it hits 1024 entries | ||||
|             if (++$this->cachedCount > 1024) { | ||||
|                 $this->cache = []; | ||||
|                 $this->cachedCount = 0; | ||||
|             } | ||||
|             $this->cache[$expression] = $this->parser->parse($expression); | ||||
|         } | ||||
|  | ||||
|         return $this->interpreter->visit($this->cache[$expression], $data); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										83
									
								
								vendor/mtdowling/jmespath.php/src/CompilerRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/mtdowling/jmespath.php/src/CompilerRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Compiles JMESPath expressions to PHP source code and executes it. | ||||
|  * | ||||
|  * JMESPath file names are stored in the cache directory using the following | ||||
|  * logic to determine the filename: | ||||
|  * | ||||
|  * 1. Start with the string "jmespath_" | ||||
|  * 2. Append the MD5 checksum of the expression. | ||||
|  * 3. Append ".php" | ||||
|  */ | ||||
| class CompilerRuntime | ||||
| { | ||||
|     private $parser; | ||||
|     private $compiler; | ||||
|     private $cacheDir; | ||||
|     private $interpreter; | ||||
|  | ||||
|     /** | ||||
|      * @param string $dir Directory used to store compiled PHP files. | ||||
|      * @param Parser $parser JMESPath parser to utilize | ||||
|      * @throws \RuntimeException if the cache directory cannot be created | ||||
|      */ | ||||
|     public function __construct($dir = null, Parser $parser = null) | ||||
|     { | ||||
|         $this->parser = $parser ?: new Parser(); | ||||
|         $this->compiler = new TreeCompiler(); | ||||
|         $dir = $dir ?: sys_get_temp_dir(); | ||||
|  | ||||
|         if (!is_dir($dir) && !mkdir($dir, 0755, true)) { | ||||
|             throw new \RuntimeException("Unable to create cache directory: $dir"); | ||||
|         } | ||||
|  | ||||
|         $this->cacheDir = realpath($dir); | ||||
|         $this->interpreter = new TreeInterpreter(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns data from the provided input that matches a given JMESPath | ||||
|      * expression. | ||||
|      * | ||||
|      * @param string $expression JMESPath expression to evaluate | ||||
|      * @param mixed  $data       Data to search. This data should be data that | ||||
|      *                           is similar to data returned from json_decode | ||||
|      *                           using associative arrays rather than objects. | ||||
|      * | ||||
|      * @return mixed|null Returns the matching data or null | ||||
|      * @throws \RuntimeException | ||||
|      */ | ||||
|     public function __invoke($expression, $data) | ||||
|     { | ||||
|         $functionName = 'jmespath_' . md5($expression); | ||||
|  | ||||
|         if (!function_exists($functionName)) { | ||||
|             $filename = "{$this->cacheDir}/{$functionName}.php"; | ||||
|             if (!file_exists($filename)) { | ||||
|                 $this->compile($filename, $expression, $functionName); | ||||
|             } | ||||
|             require $filename; | ||||
|         } | ||||
|  | ||||
|         return $functionName($this->interpreter, $data); | ||||
|     } | ||||
|  | ||||
|     private function compile($filename, $expression, $functionName) | ||||
|     { | ||||
|         $code = $this->compiler->visit( | ||||
|             $this->parser->parse($expression), | ||||
|             $functionName, | ||||
|             $expression | ||||
|         ); | ||||
|  | ||||
|         if (!file_put_contents($filename, $code)) { | ||||
|             throw new \RuntimeException(sprintf( | ||||
|                 'Unable to write the compiled PHP code to: %s (%s)', | ||||
|                 $filename, | ||||
|                 var_export(error_get_last(), true) | ||||
|             )); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										109
									
								
								vendor/mtdowling/jmespath.php/src/DebugRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/mtdowling/jmespath.php/src/DebugRuntime.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Provides CLI debugging information for the AST and Compiler runtimes. | ||||
|  */ | ||||
| class DebugRuntime | ||||
| { | ||||
|     private $runtime; | ||||
|     private $out; | ||||
|     private $lexer; | ||||
|     private $parser; | ||||
|  | ||||
|     public function __construct(callable $runtime, $output = null) | ||||
|     { | ||||
|         $this->runtime = $runtime; | ||||
|         $this->out = $output ?: STDOUT; | ||||
|         $this->lexer = new Lexer(); | ||||
|         $this->parser = new Parser($this->lexer); | ||||
|     } | ||||
|  | ||||
|     public function __invoke($expression, $data) | ||||
|     { | ||||
|         if ($this->runtime instanceof CompilerRuntime) { | ||||
|             return $this->debugCompiled($expression, $data); | ||||
|         } | ||||
|  | ||||
|         return $this->debugInterpreted($expression, $data); | ||||
|     } | ||||
|  | ||||
|     private function debugInterpreted($expression, $data) | ||||
|     { | ||||
|         return $this->debugCallback( | ||||
|             function () use ($expression, $data) { | ||||
|                 $runtime = $this->runtime; | ||||
|                 return $runtime($expression, $data); | ||||
|             }, | ||||
|             $expression, | ||||
|             $data | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private function debugCompiled($expression, $data) | ||||
|     { | ||||
|         $result = $this->debugCallback( | ||||
|             function () use ($expression, $data) { | ||||
|                 $runtime = $this->runtime; | ||||
|                 return $runtime($expression, $data); | ||||
|             }, | ||||
|             $expression, | ||||
|             $data | ||||
|         ); | ||||
|         $this->dumpCompiledCode($expression); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     private function dumpTokens($expression) | ||||
|     { | ||||
|         $lexer = new Lexer(); | ||||
|         fwrite($this->out, "Tokens\n======\n\n"); | ||||
|         $tokens = $lexer->tokenize($expression); | ||||
|  | ||||
|         foreach ($tokens as $t) { | ||||
|             fprintf( | ||||
|                 $this->out, | ||||
|                 "%3d  %-13s  %s\n", $t['pos'], $t['type'], | ||||
|                 json_encode($t['value']) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         fwrite($this->out, "\n"); | ||||
|     } | ||||
|  | ||||
|     private function dumpAst($expression) | ||||
|     { | ||||
|         $parser = new Parser(); | ||||
|         $ast = $parser->parse($expression); | ||||
|         fwrite($this->out, "AST\n========\n\n"); | ||||
|         fwrite($this->out, json_encode($ast, JSON_PRETTY_PRINT) . "\n"); | ||||
|     } | ||||
|  | ||||
|     private function dumpCompiledCode($expression) | ||||
|     { | ||||
|         fwrite($this->out, "Code\n========\n\n"); | ||||
|         $dir = sys_get_temp_dir(); | ||||
|         $hash = md5($expression); | ||||
|         $functionName = "jmespath_{$hash}"; | ||||
|         $filename = "{$dir}/{$functionName}.php"; | ||||
|         fwrite($this->out, "File: {$filename}\n\n"); | ||||
|         fprintf($this->out, file_get_contents($filename)); | ||||
|     } | ||||
|  | ||||
|     private function debugCallback(callable $debugFn, $expression, $data) | ||||
|     { | ||||
|         fprintf($this->out, "Expression\n==========\n\n%s\n\n", $expression); | ||||
|         $this->dumpTokens($expression); | ||||
|         $this->dumpAst($expression); | ||||
|         fprintf($this->out, "\nData\n====\n\n%s\n\n", json_encode($data, JSON_PRETTY_PRINT)); | ||||
|         $startTime = microtime(true); | ||||
|         $result = $debugFn(); | ||||
|         $total = microtime(true) - $startTime; | ||||
|         fprintf($this->out, "\nResult\n======\n\n%s\n\n", json_encode($result, JSON_PRETTY_PRINT)); | ||||
|         fwrite($this->out, "Time\n====\n\n"); | ||||
|         fprintf($this->out, "Total time:     %f ms\n\n", $total); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/mtdowling/jmespath.php/src/Env.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/mtdowling/jmespath.php/src/Env.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Provides a simple environment based search. | ||||
|  * | ||||
|  * The runtime utilized by the Env class can be customized via environment | ||||
|  * variables. If the JP_PHP_COMPILE environment variable is specified, then the | ||||
|  * CompilerRuntime will be utilized. If set to "on", JMESPath expressions will | ||||
|  * be cached to the system's temp directory. Set the environment variable to | ||||
|  * a string to cache expressions to a specific directory. | ||||
|  */ | ||||
| final class Env | ||||
| { | ||||
|     const COMPILE_DIR = 'JP_PHP_COMPILE'; | ||||
|  | ||||
|     /** | ||||
|      * Returns data from the input array that matches a JMESPath expression. | ||||
|      * | ||||
|      * @param string $expression JMESPath expression to evaluate | ||||
|      * @param mixed  $data       JSON-like data to search | ||||
|      * | ||||
|      * @return mixed|null Returns the matching data or null | ||||
|      */ | ||||
|     public static function search($expression, $data) | ||||
|     { | ||||
|         static $runtime; | ||||
|         if (!$runtime) { | ||||
|             $runtime = Env::createRuntime(); | ||||
|         } | ||||
|         return $runtime($expression, $data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a JMESPath runtime based on environment variables and extensions | ||||
|      * available on a system. | ||||
|      * | ||||
|      * @return callable | ||||
|      */ | ||||
|     public static function createRuntime() | ||||
|     { | ||||
|         switch ($compileDir = getenv(self::COMPILE_DIR)) { | ||||
|             case false: return new AstRuntime(); | ||||
|             case 'on': return new CompilerRuntime(); | ||||
|             default: return new CompilerRuntime($compileDir); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete all previously compiled JMESPath files from the JP_COMPILE_DIR | ||||
|      * directory or sys_get_temp_dir(). | ||||
|      * | ||||
|      * @return int Returns the number of deleted files. | ||||
|      */ | ||||
|     public static function cleanCompileDir() | ||||
|     { | ||||
|         $total = 0; | ||||
|         $compileDir = getenv(self::COMPILE_DIR) ?: sys_get_temp_dir(); | ||||
|         foreach (glob("{$compileDir}/jmespath_*.php") as $file) { | ||||
|             $total++; | ||||
|             unlink($file); | ||||
|         } | ||||
|  | ||||
|         return $total; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										401
									
								
								vendor/mtdowling/jmespath.php/src/FnDispatcher.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								vendor/mtdowling/jmespath.php/src/FnDispatcher.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,401 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Dispatches to named JMESPath functions using a single function that has the | ||||
|  * following signature: | ||||
|  * | ||||
|  *     mixed $result = fn(string $function_name, array $args) | ||||
|  */ | ||||
| class FnDispatcher | ||||
| { | ||||
|     /** | ||||
|      * Gets a cached instance of the default function implementations. | ||||
|      * | ||||
|      * @return FnDispatcher | ||||
|      */ | ||||
|     public static function getInstance() | ||||
|     { | ||||
|         static $instance = null; | ||||
|         if (!$instance) { | ||||
|             $instance = new self(); | ||||
|         } | ||||
|  | ||||
|         return $instance; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $fn   Function name. | ||||
|      * @param array  $args Function arguments. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function __invoke($fn, array $args) | ||||
|     { | ||||
|         return $this->{'fn_' . $fn}($args); | ||||
|     } | ||||
|  | ||||
|     private function fn_abs(array $args) | ||||
|     { | ||||
|         $this->validate('abs', $args, [['number']]); | ||||
|         return abs($args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_avg(array $args) | ||||
|     { | ||||
|         $this->validate('avg', $args, [['array']]); | ||||
|         $sum = $this->reduce('avg:0', $args[0], ['number'], function ($a, $b) { | ||||
|             return $a + $b; | ||||
|         }); | ||||
|         return $args[0] ? ($sum / count($args[0])) : null; | ||||
|     } | ||||
|  | ||||
|     private function fn_ceil(array $args) | ||||
|     { | ||||
|         $this->validate('ceil', $args, [['number']]); | ||||
|         return ceil($args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_contains(array $args) | ||||
|     { | ||||
|         $this->validate('contains', $args, [['string', 'array'], ['any']]); | ||||
|         if (is_array($args[0])) { | ||||
|             return in_array($args[1], $args[0]); | ||||
|         } elseif (is_string($args[1])) { | ||||
|             return strpos($args[0], $args[1]) !== false; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function fn_ends_with(array $args) | ||||
|     { | ||||
|         $this->validate('ends_with', $args, [['string'], ['string']]); | ||||
|         list($search, $suffix) = $args; | ||||
|         return $suffix === '' || substr($search, -strlen($suffix)) === $suffix; | ||||
|     } | ||||
|  | ||||
|     private function fn_floor(array $args) | ||||
|     { | ||||
|         $this->validate('floor', $args, [['number']]); | ||||
|         return floor($args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_not_null(array $args) | ||||
|     { | ||||
|         if (!$args) { | ||||
|             throw new \RuntimeException( | ||||
|                 "not_null() expects 1 or more arguments, 0 were provided" | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return array_reduce($args, function ($carry, $item) { | ||||
|             return $carry !== null ? $carry : $item; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private function fn_join(array $args) | ||||
|     { | ||||
|         $this->validate('join', $args, [['string'], ['array']]); | ||||
|         $fn = function ($a, $b, $i) use ($args) { | ||||
|             return $i ? ($a . $args[0] . $b) : $b; | ||||
|         }; | ||||
|         return $this->reduce('join:0', $args[1], ['string'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_keys(array $args) | ||||
|     { | ||||
|         $this->validate('keys', $args, [['object']]); | ||||
|         return array_keys((array) $args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_length(array $args) | ||||
|     { | ||||
|         $this->validate('length', $args, [['string', 'array', 'object']]); | ||||
|         return is_string($args[0]) ? strlen($args[0]) : count((array) $args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_max(array $args) | ||||
|     { | ||||
|         $this->validate('max', $args, [['array']]); | ||||
|         $fn = function ($a, $b) { return $a >= $b ? $a : $b; }; | ||||
|         return $this->reduce('max:0', $args[0], ['number', 'string'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_max_by(array $args) | ||||
|     { | ||||
|         $this->validate('max_by', $args, [['array'], ['expression']]); | ||||
|         $expr = $this->wrapExpression('max_by:1', $args[1], ['number', 'string']); | ||||
|         $fn = function ($carry, $item, $index) use ($expr) { | ||||
|             return $index | ||||
|                 ? ($expr($carry) >= $expr($item) ? $carry : $item) | ||||
|                 : $item; | ||||
|         }; | ||||
|         return $this->reduce('max_by:1', $args[0], ['any'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_min(array $args) | ||||
|     { | ||||
|         $this->validate('min', $args, [['array']]); | ||||
|         $fn = function ($a, $b, $i) { return $i && $a <= $b ? $a : $b; }; | ||||
|         return $this->reduce('min:0', $args[0], ['number', 'string'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_min_by(array $args) | ||||
|     { | ||||
|         $this->validate('min_by', $args, [['array'], ['expression']]); | ||||
|         $expr = $this->wrapExpression('min_by:1', $args[1], ['number', 'string']); | ||||
|         $i = -1; | ||||
|         $fn = function ($a, $b) use ($expr, &$i) { | ||||
|             return ++$i ? ($expr($a) <= $expr($b) ? $a : $b) : $b; | ||||
|         }; | ||||
|         return $this->reduce('min_by:1', $args[0], ['any'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_reverse(array $args) | ||||
|     { | ||||
|         $this->validate('reverse', $args, [['array', 'string']]); | ||||
|         if (is_array($args[0])) { | ||||
|             return array_reverse($args[0]); | ||||
|         } elseif (is_string($args[0])) { | ||||
|             return strrev($args[0]); | ||||
|         } else { | ||||
|             throw new \RuntimeException('Cannot reverse provided argument'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function fn_sum(array $args) | ||||
|     { | ||||
|         $this->validate('sum', $args, [['array']]); | ||||
|         $fn = function ($a, $b) { return $a + $b; }; | ||||
|         return $this->reduce('sum:0', $args[0], ['number'], $fn); | ||||
|     } | ||||
|  | ||||
|     private function fn_sort(array $args) | ||||
|     { | ||||
|         $this->validate('sort', $args, [['array']]); | ||||
|         $valid = ['string', 'number']; | ||||
|         return Utils::stableSort($args[0], function ($a, $b) use ($valid) { | ||||
|             $this->validateSeq('sort:0', $valid, $a, $b); | ||||
|             return strnatcmp($a, $b); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private function fn_sort_by(array $args) | ||||
|     { | ||||
|         $this->validate('sort_by', $args, [['array'], ['expression']]); | ||||
|         $expr = $args[1]; | ||||
|         $valid = ['string', 'number']; | ||||
|         return Utils::stableSort( | ||||
|             $args[0], | ||||
|             function ($a, $b) use ($expr, $valid) { | ||||
|                 $va = $expr($a); | ||||
|                 $vb = $expr($b); | ||||
|                 $this->validateSeq('sort_by:0', $valid, $va, $vb); | ||||
|                 return strnatcmp($va, $vb); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private function fn_starts_with(array $args) | ||||
|     { | ||||
|         $this->validate('starts_with', $args, [['string'], ['string']]); | ||||
|         list($search, $prefix) = $args; | ||||
|         return $prefix === '' || strpos($search, $prefix) === 0; | ||||
|     } | ||||
|  | ||||
|     private function fn_type(array $args) | ||||
|     { | ||||
|         $this->validateArity('type', count($args), 1); | ||||
|         return Utils::type($args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_to_string(array $args) | ||||
|     { | ||||
|         $this->validateArity('to_string', count($args), 1); | ||||
|         $v = $args[0]; | ||||
|         if (is_string($v)) { | ||||
|             return $v; | ||||
|         } elseif (is_object($v) | ||||
|             && !($v instanceof \JsonSerializable) | ||||
|             && method_exists($v, '__toString') | ||||
|         ) { | ||||
|             return (string) $v; | ||||
|         } | ||||
|  | ||||
|         return json_encode($v); | ||||
|     } | ||||
|  | ||||
|     private function fn_to_number(array $args) | ||||
|     { | ||||
|         $this->validateArity('to_number', count($args), 1); | ||||
|         $value = $args[0]; | ||||
|         $type = Utils::type($value); | ||||
|         if ($type == 'number') { | ||||
|             return $value; | ||||
|         } elseif ($type == 'string' && is_numeric($value)) { | ||||
|             return strpos($value, '.') ? (float) $value : (int) $value; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function fn_values(array $args) | ||||
|     { | ||||
|         $this->validate('values', $args, [['array', 'object']]); | ||||
|         return array_values((array) $args[0]); | ||||
|     } | ||||
|  | ||||
|     private function fn_merge(array $args) | ||||
|     { | ||||
|         if (!$args) { | ||||
|             throw new \RuntimeException( | ||||
|                 "merge() expects 1 or more arguments, 0 were provided" | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return call_user_func_array('array_replace', $args); | ||||
|     } | ||||
|  | ||||
|     private function fn_to_array(array $args) | ||||
|     { | ||||
|         $this->validate('to_array', $args, [['any']]); | ||||
|  | ||||
|         return Utils::isArray($args[0]) ? $args[0] : [$args[0]]; | ||||
|     } | ||||
|  | ||||
|     private function fn_map(array $args) | ||||
|     { | ||||
|         $this->validate('map', $args, [['expression'], ['any']]); | ||||
|         $result = []; | ||||
|         foreach ($args[1] as $a) { | ||||
|             $result[] = $args[0]($a); | ||||
|         } | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     private function typeError($from, $msg) | ||||
|     { | ||||
|         if (strpos($from, ':')) { | ||||
|             list($fn, $pos) = explode(':', $from); | ||||
|             throw new \RuntimeException( | ||||
|                 sprintf('Argument %d of %s %s', $pos, $fn, $msg) | ||||
|             ); | ||||
|         } else { | ||||
|             throw new \RuntimeException( | ||||
|                 sprintf('Type error: %s %s', $from, $msg) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function validateArity($from, $given, $expected) | ||||
|     { | ||||
|         if ($given != $expected) { | ||||
|             $err = "%s() expects {$expected} arguments, {$given} were provided"; | ||||
|             throw new \RuntimeException(sprintf($err, $from)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function validate($from, $args, $types = []) | ||||
|     { | ||||
|         $this->validateArity($from, count($args), count($types)); | ||||
|         foreach ($args as $index => $value) { | ||||
|             if (!isset($types[$index]) || !$types[$index]) { | ||||
|                 continue; | ||||
|             } | ||||
|             $this->validateType("{$from}:{$index}", $value, $types[$index]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function validateType($from, $value, array $types) | ||||
|     { | ||||
|         if ($types[0] == 'any' | ||||
|             || in_array(Utils::type($value), $types) | ||||
|             || ($value === [] && in_array('object', $types)) | ||||
|         ) { | ||||
|             return; | ||||
|         } | ||||
|         $msg = 'must be one of the following types: ' . implode(', ', $types) | ||||
|             . '. ' . Utils::type($value) . ' found'; | ||||
|         $this->typeError($from, $msg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates value A and B, ensures they both are correctly typed, and of | ||||
|      * the same type. | ||||
|      * | ||||
|      * @param string   $from   String of function:argument_position | ||||
|      * @param array    $types  Array of valid value types. | ||||
|      * @param mixed    $a      Value A | ||||
|      * @param mixed    $b      Value B | ||||
|      */ | ||||
|     private function validateSeq($from, array $types, $a, $b) | ||||
|     { | ||||
|         $ta = Utils::type($a); | ||||
|         $tb = Utils::type($b); | ||||
|  | ||||
|         if ($ta !== $tb) { | ||||
|             $msg = "encountered a type mismatch in sequence: {$ta}, {$tb}"; | ||||
|             $this->typeError($from, $msg); | ||||
|         } | ||||
|  | ||||
|         $typeMatch = ($types && $types[0] == 'any') || in_array($ta, $types); | ||||
|         if (!$typeMatch) { | ||||
|             $msg = 'encountered a type error in sequence. The argument must be ' | ||||
|                 . 'an array of ' . implode('|', $types) . ' types. ' | ||||
|                 . "Found {$ta}, {$tb}."; | ||||
|             $this->typeError($from, $msg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reduces and validates an array of values to a single value using a fn. | ||||
|      * | ||||
|      * @param string   $from   String of function:argument_position | ||||
|      * @param array    $values Values to reduce. | ||||
|      * @param array    $types  Array of valid value types. | ||||
|      * @param callable $reduce Reduce function that accepts ($carry, $item). | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     private function reduce($from, array $values, array $types, callable $reduce) | ||||
|     { | ||||
|         $i = -1; | ||||
|         return array_reduce( | ||||
|             $values, | ||||
|             function ($carry, $item) use ($from, $types, $reduce, &$i) { | ||||
|                 if (++$i > 0) { | ||||
|                     $this->validateSeq($from, $types, $carry, $item); | ||||
|                 } | ||||
|                 return $reduce($carry, $item, $i); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates the return values of expressions as they are applied. | ||||
|      * | ||||
|      * @param string   $from  Function name : position | ||||
|      * @param callable $expr  Expression function to validate. | ||||
|      * @param array    $types Array of acceptable return type values. | ||||
|      * | ||||
|      * @return callable Returns a wrapped function | ||||
|      */ | ||||
|     private function wrapExpression($from, callable $expr, array $types) | ||||
|     { | ||||
|         list($fn, $pos) = explode(':', $from); | ||||
|         $from = "The expression return value of argument {$pos} of {$fn}"; | ||||
|         return function ($value) use ($from, $expr, $types) { | ||||
|             $value = $expr($value); | ||||
|             $this->validateType($from, $value, $types); | ||||
|             return $value; | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** @internal Pass function name validation off to runtime */ | ||||
|     public function __call($name, $args) | ||||
|     { | ||||
|         $name = str_replace('fn_', '', $name); | ||||
|         throw new \RuntimeException("Call to undefined function {$name}"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								vendor/mtdowling/jmespath.php/src/JmesPath.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/mtdowling/jmespath.php/src/JmesPath.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Returns data from the input array that matches a JMESPath expression. | ||||
|  * | ||||
|  * @param string $expression Expression to search. | ||||
|  * @param mixed  $data       Data to search. | ||||
|  * | ||||
|  * @return mixed|null | ||||
|  */ | ||||
| function search($expression, $data) | ||||
| { | ||||
|     return Env::search($expression, $data); | ||||
| } | ||||
							
								
								
									
										444
									
								
								vendor/mtdowling/jmespath.php/src/Lexer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								vendor/mtdowling/jmespath.php/src/Lexer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,444 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Tokenizes JMESPath expressions | ||||
|  */ | ||||
| class Lexer | ||||
| { | ||||
|     const T_DOT = 'dot'; | ||||
|     const T_STAR = 'star'; | ||||
|     const T_COMMA = 'comma'; | ||||
|     const T_COLON = 'colon'; | ||||
|     const T_CURRENT = 'current'; | ||||
|     const T_EXPREF = 'expref'; | ||||
|     const T_LPAREN = 'lparen'; | ||||
|     const T_RPAREN = 'rparen'; | ||||
|     const T_LBRACE = 'lbrace'; | ||||
|     const T_RBRACE = 'rbrace'; | ||||
|     const T_LBRACKET = 'lbracket'; | ||||
|     const T_RBRACKET = 'rbracket'; | ||||
|     const T_FLATTEN = 'flatten'; | ||||
|     const T_IDENTIFIER = 'identifier'; | ||||
|     const T_NUMBER = 'number'; | ||||
|     const T_QUOTED_IDENTIFIER = 'quoted_identifier'; | ||||
|     const T_UNKNOWN = 'unknown'; | ||||
|     const T_PIPE = 'pipe'; | ||||
|     const T_OR = 'or'; | ||||
|     const T_AND = 'and'; | ||||
|     const T_NOT = 'not'; | ||||
|     const T_FILTER = 'filter'; | ||||
|     const T_LITERAL = 'literal'; | ||||
|     const T_EOF = 'eof'; | ||||
|     const T_COMPARATOR = 'comparator'; | ||||
|  | ||||
|     const STATE_IDENTIFIER = 0; | ||||
|     const STATE_NUMBER = 1; | ||||
|     const STATE_SINGLE_CHAR = 2; | ||||
|     const STATE_WHITESPACE = 3; | ||||
|     const STATE_STRING_LITERAL = 4; | ||||
|     const STATE_QUOTED_STRING = 5; | ||||
|     const STATE_JSON_LITERAL = 6; | ||||
|     const STATE_LBRACKET = 7; | ||||
|     const STATE_PIPE = 8; | ||||
|     const STATE_LT = 9; | ||||
|     const STATE_GT = 10; | ||||
|     const STATE_EQ = 11; | ||||
|     const STATE_NOT = 12; | ||||
|     const STATE_AND = 13; | ||||
|  | ||||
|     /** @var array We know what token we are consuming based on each char */ | ||||
|     private static $transitionTable = [ | ||||
|         '<'  => self::STATE_LT, | ||||
|         '>'  => self::STATE_GT, | ||||
|         '='  => self::STATE_EQ, | ||||
|         '!'  => self::STATE_NOT, | ||||
|         '['  => self::STATE_LBRACKET, | ||||
|         '|'  => self::STATE_PIPE, | ||||
|         '&'  => self::STATE_AND, | ||||
|         '`'  => self::STATE_JSON_LITERAL, | ||||
|         '"'  => self::STATE_QUOTED_STRING, | ||||
|         "'"  => self::STATE_STRING_LITERAL, | ||||
|         '-'  => self::STATE_NUMBER, | ||||
|         '0'  => self::STATE_NUMBER, | ||||
|         '1'  => self::STATE_NUMBER, | ||||
|         '2'  => self::STATE_NUMBER, | ||||
|         '3'  => self::STATE_NUMBER, | ||||
|         '4'  => self::STATE_NUMBER, | ||||
|         '5'  => self::STATE_NUMBER, | ||||
|         '6'  => self::STATE_NUMBER, | ||||
|         '7'  => self::STATE_NUMBER, | ||||
|         '8'  => self::STATE_NUMBER, | ||||
|         '9'  => self::STATE_NUMBER, | ||||
|         ' '  => self::STATE_WHITESPACE, | ||||
|         "\t" => self::STATE_WHITESPACE, | ||||
|         "\n" => self::STATE_WHITESPACE, | ||||
|         "\r" => self::STATE_WHITESPACE, | ||||
|         '.'  => self::STATE_SINGLE_CHAR, | ||||
|         '*'  => self::STATE_SINGLE_CHAR, | ||||
|         ']'  => self::STATE_SINGLE_CHAR, | ||||
|         ','  => self::STATE_SINGLE_CHAR, | ||||
|         ':'  => self::STATE_SINGLE_CHAR, | ||||
|         '@'  => self::STATE_SINGLE_CHAR, | ||||
|         '('  => self::STATE_SINGLE_CHAR, | ||||
|         ')'  => self::STATE_SINGLE_CHAR, | ||||
|         '{'  => self::STATE_SINGLE_CHAR, | ||||
|         '}'  => self::STATE_SINGLE_CHAR, | ||||
|         '_'  => self::STATE_IDENTIFIER, | ||||
|         'A'  => self::STATE_IDENTIFIER, | ||||
|         'B'  => self::STATE_IDENTIFIER, | ||||
|         'C'  => self::STATE_IDENTIFIER, | ||||
|         'D'  => self::STATE_IDENTIFIER, | ||||
|         'E'  => self::STATE_IDENTIFIER, | ||||
|         'F'  => self::STATE_IDENTIFIER, | ||||
|         'G'  => self::STATE_IDENTIFIER, | ||||
|         'H'  => self::STATE_IDENTIFIER, | ||||
|         'I'  => self::STATE_IDENTIFIER, | ||||
|         'J'  => self::STATE_IDENTIFIER, | ||||
|         'K'  => self::STATE_IDENTIFIER, | ||||
|         'L'  => self::STATE_IDENTIFIER, | ||||
|         'M'  => self::STATE_IDENTIFIER, | ||||
|         'N'  => self::STATE_IDENTIFIER, | ||||
|         'O'  => self::STATE_IDENTIFIER, | ||||
|         'P'  => self::STATE_IDENTIFIER, | ||||
|         'Q'  => self::STATE_IDENTIFIER, | ||||
|         'R'  => self::STATE_IDENTIFIER, | ||||
|         'S'  => self::STATE_IDENTIFIER, | ||||
|         'T'  => self::STATE_IDENTIFIER, | ||||
|         'U'  => self::STATE_IDENTIFIER, | ||||
|         'V'  => self::STATE_IDENTIFIER, | ||||
|         'W'  => self::STATE_IDENTIFIER, | ||||
|         'X'  => self::STATE_IDENTIFIER, | ||||
|         'Y'  => self::STATE_IDENTIFIER, | ||||
|         'Z'  => self::STATE_IDENTIFIER, | ||||
|         'a'  => self::STATE_IDENTIFIER, | ||||
|         'b'  => self::STATE_IDENTIFIER, | ||||
|         'c'  => self::STATE_IDENTIFIER, | ||||
|         'd'  => self::STATE_IDENTIFIER, | ||||
|         'e'  => self::STATE_IDENTIFIER, | ||||
|         'f'  => self::STATE_IDENTIFIER, | ||||
|         'g'  => self::STATE_IDENTIFIER, | ||||
|         'h'  => self::STATE_IDENTIFIER, | ||||
|         'i'  => self::STATE_IDENTIFIER, | ||||
|         'j'  => self::STATE_IDENTIFIER, | ||||
|         'k'  => self::STATE_IDENTIFIER, | ||||
|         'l'  => self::STATE_IDENTIFIER, | ||||
|         'm'  => self::STATE_IDENTIFIER, | ||||
|         'n'  => self::STATE_IDENTIFIER, | ||||
|         'o'  => self::STATE_IDENTIFIER, | ||||
|         'p'  => self::STATE_IDENTIFIER, | ||||
|         'q'  => self::STATE_IDENTIFIER, | ||||
|         'r'  => self::STATE_IDENTIFIER, | ||||
|         's'  => self::STATE_IDENTIFIER, | ||||
|         't'  => self::STATE_IDENTIFIER, | ||||
|         'u'  => self::STATE_IDENTIFIER, | ||||
|         'v'  => self::STATE_IDENTIFIER, | ||||
|         'w'  => self::STATE_IDENTIFIER, | ||||
|         'x'  => self::STATE_IDENTIFIER, | ||||
|         'y'  => self::STATE_IDENTIFIER, | ||||
|         'z'  => self::STATE_IDENTIFIER, | ||||
|     ]; | ||||
|  | ||||
|     /** @var array Valid identifier characters after first character */ | ||||
|     private $validIdentifier = [ | ||||
|         'A' => true, 'B' => true, 'C' => true, 'D' => true, 'E' => true, | ||||
|         'F' => true, 'G' => true, 'H' => true, 'I' => true, 'J' => true, | ||||
|         'K' => true, 'L' => true, 'M' => true, 'N' => true, 'O' => true, | ||||
|         'P' => true, 'Q' => true, 'R' => true, 'S' => true, 'T' => true, | ||||
|         'U' => true, 'V' => true, 'W' => true, 'X' => true, 'Y' => true, | ||||
|         'Z' => true, 'a' => true, 'b' => true, 'c' => true, 'd' => true, | ||||
|         'e' => true, 'f' => true, 'g' => true, 'h' => true, 'i' => true, | ||||
|         'j' => true, 'k' => true, 'l' => true, 'm' => true, 'n' => true, | ||||
|         'o' => true, 'p' => true, 'q' => true, 'r' => true, 's' => true, | ||||
|         't' => true, 'u' => true, 'v' => true, 'w' => true, 'x' => true, | ||||
|         'y' => true, 'z' => true, '_' => true, '0' => true, '1' => true, | ||||
|         '2' => true, '3' => true, '4' => true, '5' => true, '6' => true, | ||||
|         '7' => true, '8' => true, '9' => true, | ||||
|     ]; | ||||
|  | ||||
|     /** @var array Valid number characters after the first character */ | ||||
|     private $numbers = [ | ||||
|         '0' => true, '1' => true, '2' => true, '3' => true, '4' => true, | ||||
|         '5' => true, '6' => true, '7' => true, '8' => true, '9' => true | ||||
|     ]; | ||||
|  | ||||
|     /** @var array Map of simple single character tokens */ | ||||
|     private $simpleTokens = [ | ||||
|         '.' => self::T_DOT, | ||||
|         '*' => self::T_STAR, | ||||
|         ']' => self::T_RBRACKET, | ||||
|         ',' => self::T_COMMA, | ||||
|         ':' => self::T_COLON, | ||||
|         '@' => self::T_CURRENT, | ||||
|         '(' => self::T_LPAREN, | ||||
|         ')' => self::T_RPAREN, | ||||
|         '{' => self::T_LBRACE, | ||||
|         '}' => self::T_RBRACE, | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * Tokenize the JMESPath expression into an array of tokens hashes that | ||||
|      * contain a 'type', 'value', and 'key'. | ||||
|      * | ||||
|      * @param string $input JMESPath input | ||||
|      * | ||||
|      * @return array | ||||
|      * @throws SyntaxErrorException | ||||
|      */ | ||||
|     public function tokenize($input) | ||||
|     { | ||||
|         $tokens = []; | ||||
|  | ||||
|         if ($input === '') { | ||||
|             goto eof; | ||||
|         } | ||||
|  | ||||
|         $chars = str_split($input); | ||||
|  | ||||
|         while (false !== ($current = current($chars))) { | ||||
|  | ||||
|             // Every character must be in the transition character table. | ||||
|             if (!isset(self::$transitionTable[$current])) { | ||||
|                 $tokens[] = [ | ||||
|                     'type'  => self::T_UNKNOWN, | ||||
|                     'pos'   => key($chars), | ||||
|                     'value' => $current | ||||
|                 ]; | ||||
|                 next($chars); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             $state = self::$transitionTable[$current]; | ||||
|  | ||||
|             if ($state === self::STATE_SINGLE_CHAR) { | ||||
|  | ||||
|                 // Consume simple tokens like ".", ",", "@", etc. | ||||
|                 $tokens[] = [ | ||||
|                     'type'  => $this->simpleTokens[$current], | ||||
|                     'pos'   => key($chars), | ||||
|                     'value' => $current | ||||
|                 ]; | ||||
|                 next($chars); | ||||
|  | ||||
|             } elseif ($state === self::STATE_IDENTIFIER) { | ||||
|  | ||||
|                 // Consume identifiers | ||||
|                 $start = key($chars); | ||||
|                 $buffer = ''; | ||||
|                 do { | ||||
|                     $buffer .= $current; | ||||
|                     $current = next($chars); | ||||
|                 } while ($current !== false && isset($this->validIdentifier[$current])); | ||||
|                 $tokens[] = [ | ||||
|                     'type'  => self::T_IDENTIFIER, | ||||
|                     'value' => $buffer, | ||||
|                     'pos'   => $start | ||||
|                 ]; | ||||
|  | ||||
|             } elseif ($state === self::STATE_WHITESPACE) { | ||||
|  | ||||
|                 // Skip whitespace | ||||
|                 next($chars); | ||||
|  | ||||
|             } elseif ($state === self::STATE_LBRACKET) { | ||||
|  | ||||
|                 // Consume "[", "[?", and "[]" | ||||
|                 $position = key($chars); | ||||
|                 $actual = next($chars); | ||||
|                 if ($actual === ']') { | ||||
|                     next($chars); | ||||
|                     $tokens[] = [ | ||||
|                         'type'  => self::T_FLATTEN, | ||||
|                         'pos'   => $position, | ||||
|                         'value' => '[]' | ||||
|                     ]; | ||||
|                 } elseif ($actual === '?') { | ||||
|                     next($chars); | ||||
|                     $tokens[] = [ | ||||
|                         'type'  => self::T_FILTER, | ||||
|                         'pos'   => $position, | ||||
|                         'value' => '[?' | ||||
|                     ]; | ||||
|                 } else { | ||||
|                     $tokens[] = [ | ||||
|                         'type'  => self::T_LBRACKET, | ||||
|                         'pos'   => $position, | ||||
|                         'value' => '[' | ||||
|                     ]; | ||||
|                 } | ||||
|  | ||||
|             } elseif ($state === self::STATE_STRING_LITERAL) { | ||||
|  | ||||
|                 // Consume raw string literals | ||||
|                 $t = $this->inside($chars, "'", self::T_LITERAL); | ||||
|                 $t['value'] = str_replace("\\'", "'", $t['value']); | ||||
|                 $tokens[] = $t; | ||||
|  | ||||
|             } elseif ($state === self::STATE_PIPE) { | ||||
|  | ||||
|                 // Consume pipe and OR | ||||
|                 $tokens[] = $this->matchOr($chars, '|', '|', self::T_OR, self::T_PIPE); | ||||
|  | ||||
|             } elseif ($state == self::STATE_JSON_LITERAL) { | ||||
|  | ||||
|                 // Consume JSON literals | ||||
|                 $token = $this->inside($chars, '`', self::T_LITERAL); | ||||
|                 if ($token['type'] === self::T_LITERAL) { | ||||
|                     $token['value'] = str_replace('\\`', '`', $token['value']); | ||||
|                     $token = $this->parseJson($token); | ||||
|                 } | ||||
|                 $tokens[] = $token; | ||||
|  | ||||
|             } elseif ($state == self::STATE_NUMBER) { | ||||
|  | ||||
|                 // Consume numbers | ||||
|                 $start = key($chars); | ||||
|                 $buffer = ''; | ||||
|                 do { | ||||
|                     $buffer .= $current; | ||||
|                     $current = next($chars); | ||||
|                 } while ($current !== false && isset($this->numbers[$current])); | ||||
|                 $tokens[] = [ | ||||
|                     'type'  => self::T_NUMBER, | ||||
|                     'value' => (int)$buffer, | ||||
|                     'pos'   => $start | ||||
|                 ]; | ||||
|  | ||||
|             } elseif ($state === self::STATE_QUOTED_STRING) { | ||||
|  | ||||
|                 // Consume quoted identifiers | ||||
|                 $token = $this->inside($chars, '"', self::T_QUOTED_IDENTIFIER); | ||||
|                 if ($token['type'] === self::T_QUOTED_IDENTIFIER) { | ||||
|                     $token['value'] = '"' . $token['value'] . '"'; | ||||
|                     $token = $this->parseJson($token); | ||||
|                 } | ||||
|                 $tokens[] = $token; | ||||
|  | ||||
|             } elseif ($state === self::STATE_EQ) { | ||||
|  | ||||
|                 // Consume equals | ||||
|                 $tokens[] = $this->matchOr($chars, '=', '=', self::T_COMPARATOR, self::T_UNKNOWN); | ||||
|  | ||||
|             } elseif ($state == self::STATE_AND) { | ||||
|  | ||||
|                 $tokens[] = $this->matchOr($chars, '&', '&', self::T_AND, self::T_EXPREF); | ||||
|  | ||||
|             } elseif ($state === self::STATE_NOT) { | ||||
|  | ||||
|                 // Consume not equal | ||||
|                 $tokens[] = $this->matchOr($chars, '!', '=', self::T_COMPARATOR, self::T_NOT); | ||||
|  | ||||
|             } else { | ||||
|  | ||||
|                 // either '<' or '>' | ||||
|                 // Consume less than and greater than | ||||
|                 $tokens[] = $this->matchOr($chars, $current, '=', self::T_COMPARATOR, self::T_COMPARATOR); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         eof: | ||||
|         $tokens[] = [ | ||||
|             'type'  => self::T_EOF, | ||||
|             'pos'   => strlen($input), | ||||
|             'value' => null | ||||
|         ]; | ||||
|  | ||||
|         return $tokens; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a token based on whether or not the next token matches the | ||||
|      * expected value. If it does, a token of "$type" is returned. Otherwise, | ||||
|      * a token of "$orElse" type is returned. | ||||
|      * | ||||
|      * @param array  $chars    Array of characters by reference. | ||||
|      * @param string $current  The current character. | ||||
|      * @param string $expected Expected character. | ||||
|      * @param string $type     Expected result type. | ||||
|      * @param string $orElse   Otherwise return a token of this type. | ||||
|      * | ||||
|      * @return array Returns a conditional token. | ||||
|      */ | ||||
|     private function matchOr(array &$chars, $current, $expected, $type, $orElse) | ||||
|     { | ||||
|         if (next($chars) === $expected) { | ||||
|             next($chars); | ||||
|             return [ | ||||
|                 'type'  => $type, | ||||
|                 'pos'   => key($chars) - 1, | ||||
|                 'value' => $current . $expected | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'type'  => $orElse, | ||||
|             'pos'   => key($chars) - 1, | ||||
|             'value' => $current | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a token the is the result of consuming inside of delimiter | ||||
|      * characters. Escaped delimiters will be adjusted before returning a | ||||
|      * value. If the token is not closed, "unknown" is returned. | ||||
|      * | ||||
|      * @param array  $chars Array of characters by reference. | ||||
|      * @param string $delim The delimiter character. | ||||
|      * @param string $type  Token type. | ||||
|      * | ||||
|      * @return array Returns the consumed token. | ||||
|      */ | ||||
|     private function inside(array &$chars, $delim, $type) | ||||
|     { | ||||
|         $position = key($chars); | ||||
|         $current = next($chars); | ||||
|         $buffer = ''; | ||||
|  | ||||
|         while ($current !== $delim) { | ||||
|             if ($current === '\\') { | ||||
|                 $buffer .= '\\'; | ||||
|                 $current = next($chars); | ||||
|             } | ||||
|             if ($current === false) { | ||||
|                 // Unclosed delimiter | ||||
|                 return [ | ||||
|                     'type'  => self::T_UNKNOWN, | ||||
|                     'value' => $buffer, | ||||
|                     'pos'   => $position | ||||
|                 ]; | ||||
|             } | ||||
|             $buffer .= $current; | ||||
|             $current = next($chars); | ||||
|         } | ||||
|  | ||||
|         next($chars); | ||||
|  | ||||
|         return ['type' => $type, 'value' => $buffer, 'pos' => $position]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parses a JSON token or sets the token type to "unknown" on error. | ||||
|      * | ||||
|      * @param array $token Token that needs parsing. | ||||
|      * | ||||
|      * @return array Returns a token with a parsed value. | ||||
|      */ | ||||
|     private function parseJson(array $token) | ||||
|     { | ||||
|         $value = json_decode($token['value'], true); | ||||
|  | ||||
|         if ($error = json_last_error()) { | ||||
|             // Legacy support for elided quotes. Try to parse again by adding | ||||
|             // quotes around the bad input value. | ||||
|             $value = json_decode('"' . $token['value'] . '"', true); | ||||
|             if ($error = json_last_error()) { | ||||
|                 $token['type'] = self::T_UNKNOWN; | ||||
|                 return $token; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $token['value'] = $value; | ||||
|         return $token; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										518
									
								
								vendor/mtdowling/jmespath.php/src/Parser.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								vendor/mtdowling/jmespath.php/src/Parser.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,518 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| use JmesPath\Lexer as T; | ||||
|  | ||||
| /** | ||||
|  * JMESPath Pratt parser | ||||
|  * @link http://hall.org.ua/halls/wizzard/pdf/Vaughan.Pratt.TDOP.pdf | ||||
|  */ | ||||
| class Parser | ||||
| { | ||||
|     /** @var Lexer */ | ||||
|     private $lexer; | ||||
|     private $tokens; | ||||
|     private $token; | ||||
|     private $tpos; | ||||
|     private $expression; | ||||
|     private static $nullToken = ['type' => T::T_EOF]; | ||||
|     private static $currentNode = ['type' => T::T_CURRENT]; | ||||
|  | ||||
|     private static $bp = [ | ||||
|         T::T_EOF               => 0, | ||||
|         T::T_QUOTED_IDENTIFIER => 0, | ||||
|         T::T_IDENTIFIER        => 0, | ||||
|         T::T_RBRACKET          => 0, | ||||
|         T::T_RPAREN            => 0, | ||||
|         T::T_COMMA             => 0, | ||||
|         T::T_RBRACE            => 0, | ||||
|         T::T_NUMBER            => 0, | ||||
|         T::T_CURRENT           => 0, | ||||
|         T::T_EXPREF            => 0, | ||||
|         T::T_COLON             => 0, | ||||
|         T::T_PIPE              => 1, | ||||
|         T::T_OR                => 2, | ||||
|         T::T_AND               => 3, | ||||
|         T::T_COMPARATOR        => 5, | ||||
|         T::T_FLATTEN           => 9, | ||||
|         T::T_STAR              => 20, | ||||
|         T::T_FILTER            => 21, | ||||
|         T::T_DOT               => 40, | ||||
|         T::T_NOT               => 45, | ||||
|         T::T_LBRACE            => 50, | ||||
|         T::T_LBRACKET          => 55, | ||||
|         T::T_LPAREN            => 60, | ||||
|     ]; | ||||
|  | ||||
|     /** @var array Acceptable tokens after a dot token */ | ||||
|     private static $afterDot = [ | ||||
|         T::T_IDENTIFIER        => true, // foo.bar | ||||
|         T::T_QUOTED_IDENTIFIER => true, // foo."bar" | ||||
|         T::T_STAR              => true, // foo.* | ||||
|         T::T_LBRACE            => true, // foo[1] | ||||
|         T::T_LBRACKET          => true, // foo{a: 0} | ||||
|         T::T_FILTER            => true, // foo.[?bar==10] | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * @param Lexer $lexer Lexer used to tokenize expressions | ||||
|      */ | ||||
|     public function __construct(Lexer $lexer = null) | ||||
|     { | ||||
|         $this->lexer = $lexer ?: new Lexer(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parses a JMESPath expression into an AST | ||||
|      * | ||||
|      * @param string $expression JMESPath expression to compile | ||||
|      * | ||||
|      * @return array Returns an array based AST | ||||
|      * @throws SyntaxErrorException | ||||
|      */ | ||||
|     public function parse($expression) | ||||
|     { | ||||
|         $this->expression = $expression; | ||||
|         $this->tokens = $this->lexer->tokenize($expression); | ||||
|         $this->tpos = -1; | ||||
|         $this->next(); | ||||
|         $result = $this->expr(); | ||||
|  | ||||
|         if ($this->token['type'] === T::T_EOF) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         throw $this->syntax('Did not reach the end of the token stream'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parses an expression while rbp < lbp. | ||||
|      * | ||||
|      * @param int   $rbp  Right bound precedence | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function expr($rbp = 0) | ||||
|     { | ||||
|         $left = $this->{"nud_{$this->token['type']}"}(); | ||||
|         while ($rbp < self::$bp[$this->token['type']]) { | ||||
|             $left = $this->{"led_{$this->token['type']}"}($left); | ||||
|         } | ||||
|  | ||||
|         return $left; | ||||
|     } | ||||
|  | ||||
|     private function nud_identifier() | ||||
|     { | ||||
|         $token = $this->token; | ||||
|         $this->next(); | ||||
|         return ['type' => 'field', 'value' => $token['value']]; | ||||
|     } | ||||
|  | ||||
|     private function nud_quoted_identifier() | ||||
|     { | ||||
|         $token = $this->token; | ||||
|         $this->next(); | ||||
|         $this->assertNotToken(T::T_LPAREN); | ||||
|         return ['type' => 'field', 'value' => $token['value']]; | ||||
|     } | ||||
|  | ||||
|     private function nud_current() | ||||
|     { | ||||
|         $this->next(); | ||||
|         return self::$currentNode; | ||||
|     } | ||||
|  | ||||
|     private function nud_literal() | ||||
|     { | ||||
|         $token = $this->token; | ||||
|         $this->next(); | ||||
|         return ['type' => 'literal', 'value' => $token['value']]; | ||||
|     } | ||||
|  | ||||
|     private function nud_expref() | ||||
|     { | ||||
|         $this->next(); | ||||
|         return ['type' => T::T_EXPREF, 'children' => [$this->expr(self::$bp[T::T_EXPREF])]]; | ||||
|     } | ||||
|  | ||||
|     private function nud_not() | ||||
|     { | ||||
|         $this->next(); | ||||
|         return ['type' => T::T_NOT, 'children' => [$this->expr(self::$bp[T::T_NOT])]]; | ||||
|     } | ||||
|  | ||||
|     private function nud_lparen() { | ||||
|         $this->next(); | ||||
|         $result = $this->expr(0); | ||||
|         if ($this->token['type'] !== T::T_RPAREN) { | ||||
|             throw $this->syntax('Unclosed `(`'); | ||||
|         } | ||||
|         $this->next(); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     private function nud_lbrace() | ||||
|     { | ||||
|         static $validKeys = [T::T_QUOTED_IDENTIFIER => true, T::T_IDENTIFIER => true]; | ||||
|         $this->next($validKeys); | ||||
|         $pairs = []; | ||||
|  | ||||
|         do { | ||||
|             $pairs[] = $this->parseKeyValuePair(); | ||||
|             if ($this->token['type'] == T::T_COMMA) { | ||||
|                 $this->next($validKeys); | ||||
|             } | ||||
|         } while ($this->token['type'] !== T::T_RBRACE); | ||||
|  | ||||
|         $this->next(); | ||||
|  | ||||
|         return['type' => 'multi_select_hash', 'children' => $pairs]; | ||||
|     } | ||||
|  | ||||
|     private function nud_flatten() | ||||
|     { | ||||
|         return $this->led_flatten(self::$currentNode); | ||||
|     } | ||||
|  | ||||
|     private function nud_filter() | ||||
|     { | ||||
|         return $this->led_filter(self::$currentNode); | ||||
|     } | ||||
|  | ||||
|     private function nud_star() | ||||
|     { | ||||
|         return $this->parseWildcardObject(self::$currentNode); | ||||
|     } | ||||
|  | ||||
|     private function nud_lbracket() | ||||
|     { | ||||
|         $this->next(); | ||||
|         $type = $this->token['type']; | ||||
|         if ($type == T::T_NUMBER || $type == T::T_COLON) { | ||||
|             return $this->parseArrayIndexExpression(); | ||||
|         } elseif ($type == T::T_STAR && $this->lookahead() == T::T_RBRACKET) { | ||||
|             return $this->parseWildcardArray(); | ||||
|         } else { | ||||
|             return $this->parseMultiSelectList(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function led_lbracket(array $left) | ||||
|     { | ||||
|         static $nextTypes = [T::T_NUMBER => true, T::T_COLON => true, T::T_STAR => true]; | ||||
|         $this->next($nextTypes); | ||||
|         switch ($this->token['type']) { | ||||
|             case T::T_NUMBER: | ||||
|             case T::T_COLON: | ||||
|                 return [ | ||||
|                     'type' => 'subexpression', | ||||
|                     'children' => [$left, $this->parseArrayIndexExpression()] | ||||
|                 ]; | ||||
|             default: | ||||
|                 return $this->parseWildcardArray($left); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function led_flatten(array $left) | ||||
|     { | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'projection', | ||||
|             'from'     => 'array', | ||||
|             'children' => [ | ||||
|                 ['type' => T::T_FLATTEN, 'children' => [$left]], | ||||
|                 $this->parseProjection(self::$bp[T::T_FLATTEN]) | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_dot(array $left) | ||||
|     { | ||||
|         $this->next(self::$afterDot); | ||||
|  | ||||
|         if ($this->token['type'] == T::T_STAR) { | ||||
|             return $this->parseWildcardObject($left); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'subexpression', | ||||
|             'children' => [$left, $this->parseDot(self::$bp[T::T_DOT])] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_or(array $left) | ||||
|     { | ||||
|         $this->next(); | ||||
|         return [ | ||||
|             'type'     => T::T_OR, | ||||
|             'children' => [$left, $this->expr(self::$bp[T::T_OR])] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_and(array $left) | ||||
|     { | ||||
|         $this->next(); | ||||
|         return [ | ||||
|             'type'     => T::T_AND, | ||||
|             'children' => [$left, $this->expr(self::$bp[T::T_AND])] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_pipe(array $left) | ||||
|     { | ||||
|         $this->next(); | ||||
|         return [ | ||||
|             'type'     => T::T_PIPE, | ||||
|             'children' => [$left, $this->expr(self::$bp[T::T_PIPE])] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_lparen(array $left) | ||||
|     { | ||||
|         $args = []; | ||||
|         $this->next(); | ||||
|  | ||||
|         while ($this->token['type'] != T::T_RPAREN) { | ||||
|             $args[] = $this->expr(0); | ||||
|             if ($this->token['type'] == T::T_COMMA) { | ||||
|                 $this->next(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'function', | ||||
|             'value'    => $left['value'], | ||||
|             'children' => $args | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_filter(array $left) | ||||
|     { | ||||
|         $this->next(); | ||||
|         $expression = $this->expr(); | ||||
|         if ($this->token['type'] != T::T_RBRACKET) { | ||||
|             throw $this->syntax('Expected a closing rbracket for the filter'); | ||||
|         } | ||||
|  | ||||
|         $this->next(); | ||||
|         $rhs = $this->parseProjection(self::$bp[T::T_FILTER]); | ||||
|  | ||||
|         return [ | ||||
|             'type'       => 'projection', | ||||
|             'from'       => 'array', | ||||
|             'children'   => [ | ||||
|                 $left ?: self::$currentNode, | ||||
|                 [ | ||||
|                     'type' => 'condition', | ||||
|                     'children' => [$expression, $rhs] | ||||
|                 ] | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function led_comparator(array $left) | ||||
|     { | ||||
|         $token = $this->token; | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => T::T_COMPARATOR, | ||||
|             'value'    => $token['value'], | ||||
|             'children' => [$left, $this->expr(self::$bp[T::T_COMPARATOR])] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function parseProjection($bp) | ||||
|     { | ||||
|         $type = $this->token['type']; | ||||
|         if (self::$bp[$type] < 10) { | ||||
|             return self::$currentNode; | ||||
|         } elseif ($type == T::T_DOT) { | ||||
|             $this->next(self::$afterDot); | ||||
|             return $this->parseDot($bp); | ||||
|         } elseif ($type == T::T_LBRACKET || $type == T::T_FILTER) { | ||||
|             return $this->expr($bp); | ||||
|         } | ||||
|  | ||||
|         throw $this->syntax('Syntax error after projection'); | ||||
|     } | ||||
|  | ||||
|     private function parseDot($bp) | ||||
|     { | ||||
|         if ($this->token['type'] == T::T_LBRACKET) { | ||||
|             $this->next(); | ||||
|             return $this->parseMultiSelectList(); | ||||
|         } | ||||
|  | ||||
|         return $this->expr($bp); | ||||
|     } | ||||
|  | ||||
|     private function parseKeyValuePair() | ||||
|     { | ||||
|         static $validColon = [T::T_COLON => true]; | ||||
|         $key = $this->token['value']; | ||||
|         $this->next($validColon); | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'key_val_pair', | ||||
|             'value'    => $key, | ||||
|             'children' => [$this->expr()] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function parseWildcardObject(array $left = null) | ||||
|     { | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'projection', | ||||
|             'from'     => 'object', | ||||
|             'children' => [ | ||||
|                 $left ?: self::$currentNode, | ||||
|                 $this->parseProjection(self::$bp[T::T_STAR]) | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function parseWildcardArray(array $left = null) | ||||
|     { | ||||
|         static $getRbracket = [T::T_RBRACKET => true]; | ||||
|         $this->next($getRbracket); | ||||
|         $this->next(); | ||||
|  | ||||
|         return [ | ||||
|             'type'     => 'projection', | ||||
|             'from'     => 'array', | ||||
|             'children' => [ | ||||
|                 $left ?: self::$currentNode, | ||||
|                 $this->parseProjection(self::$bp[T::T_STAR]) | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Parses an array index expression (e.g., [0], [1:2:3] | ||||
|      */ | ||||
|     private function parseArrayIndexExpression() | ||||
|     { | ||||
|         static $matchNext = [ | ||||
|             T::T_NUMBER   => true, | ||||
|             T::T_COLON    => true, | ||||
|             T::T_RBRACKET => true | ||||
|         ]; | ||||
|  | ||||
|         $pos = 0; | ||||
|         $parts = [null, null, null]; | ||||
|         $expected = $matchNext; | ||||
|  | ||||
|         do { | ||||
|             if ($this->token['type'] == T::T_COLON) { | ||||
|                 $pos++; | ||||
|                 $expected = $matchNext; | ||||
|             } elseif ($this->token['type'] == T::T_NUMBER) { | ||||
|                 $parts[$pos] = $this->token['value']; | ||||
|                 $expected = [T::T_COLON => true, T::T_RBRACKET => true]; | ||||
|             } | ||||
|             $this->next($expected); | ||||
|         } while ($this->token['type'] != T::T_RBRACKET); | ||||
|  | ||||
|         // Consume the closing bracket | ||||
|         $this->next(); | ||||
|  | ||||
|         if ($pos === 0) { | ||||
|             // No colons were found so this is a simple index extraction | ||||
|             return ['type' => 'index', 'value' => $parts[0]]; | ||||
|         } | ||||
|  | ||||
|         if ($pos > 2) { | ||||
|             throw $this->syntax('Invalid array slice syntax: too many colons'); | ||||
|         } | ||||
|  | ||||
|         // Sliced array from start (e.g., [2:]) | ||||
|         return [ | ||||
|             'type'     => 'projection', | ||||
|             'from'     => 'array', | ||||
|             'children' => [ | ||||
|                 ['type' => 'slice', 'value' => $parts], | ||||
|                 $this->parseProjection(self::$bp[T::T_STAR]) | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     private function parseMultiSelectList() | ||||
|     { | ||||
|         $nodes = []; | ||||
|  | ||||
|         do { | ||||
|             $nodes[] = $this->expr(); | ||||
|             if ($this->token['type'] == T::T_COMMA) { | ||||
|                 $this->next(); | ||||
|                 $this->assertNotToken(T::T_RBRACKET); | ||||
|             } | ||||
|         } while ($this->token['type'] !== T::T_RBRACKET); | ||||
|         $this->next(); | ||||
|  | ||||
|         return ['type' => 'multi_select_list', 'children' => $nodes]; | ||||
|     } | ||||
|  | ||||
|     private function syntax($msg) | ||||
|     { | ||||
|         return new SyntaxErrorException($msg, $this->token, $this->expression); | ||||
|     } | ||||
|  | ||||
|     private function lookahead() | ||||
|     { | ||||
|         return (!isset($this->tokens[$this->tpos + 1])) | ||||
|             ? T::T_EOF | ||||
|             : $this->tokens[$this->tpos + 1]['type']; | ||||
|     } | ||||
|  | ||||
|     private function next(array $match = null) | ||||
|     { | ||||
|         if (!isset($this->tokens[$this->tpos + 1])) { | ||||
|             $this->token = self::$nullToken; | ||||
|         } else { | ||||
|             $this->token = $this->tokens[++$this->tpos]; | ||||
|         } | ||||
|  | ||||
|         if ($match && !isset($match[$this->token['type']])) { | ||||
|             throw $this->syntax($match); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function assertNotToken($type) | ||||
|     { | ||||
|         if ($this->token['type'] == $type) { | ||||
|             throw $this->syntax("Token {$this->tpos} not allowed to be $type"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @internal Handles undefined tokens without paying the cost of validation | ||||
|      */ | ||||
|     public function __call($method, $args) | ||||
|     { | ||||
|         $prefix = substr($method, 0, 4); | ||||
|         if ($prefix == 'nud_' || $prefix == 'led_') { | ||||
|             $token = substr($method, 4); | ||||
|             $message = "Unexpected \"$token\" token ($method). Expected one of" | ||||
|                 . " the following tokens: " | ||||
|                 . implode(', ', array_map(function ($i) { | ||||
|                     return '"' . substr($i, 4) . '"'; | ||||
|                 }, array_filter( | ||||
|                     get_class_methods($this), | ||||
|                     function ($i) use ($prefix) { | ||||
|                         return strpos($i, $prefix) === 0; | ||||
|                     } | ||||
|                 ))); | ||||
|             throw $this->syntax($message); | ||||
|         } | ||||
|  | ||||
|         throw new \BadMethodCallException("Call to undefined method $method"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/mtdowling/jmespath.php/src/SyntaxErrorException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/mtdowling/jmespath.php/src/SyntaxErrorException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Syntax errors raise this exception that gives context | ||||
|  */ | ||||
| class SyntaxErrorException extends \InvalidArgumentException | ||||
| { | ||||
|     /** | ||||
|      * @param string $expectedTypesOrMessage Expected array of tokens or message | ||||
|      * @param array  $token                  Current token | ||||
|      * @param string $expression             Expression input | ||||
|      */ | ||||
|     public function __construct( | ||||
|         $expectedTypesOrMessage, | ||||
|         array $token, | ||||
|         $expression | ||||
|     ) { | ||||
|         $message = "Syntax error at character {$token['pos']}\n" | ||||
|             . $expression . "\n" . str_repeat(' ', $token['pos']) . "^\n"; | ||||
|         $message .= !is_array($expectedTypesOrMessage) | ||||
|             ? $expectedTypesOrMessage | ||||
|             : $this->createTokenMessage($token, $expectedTypesOrMessage); | ||||
|         parent::__construct($message); | ||||
|     } | ||||
|  | ||||
|     private function createTokenMessage(array $token, array $valid) | ||||
|     { | ||||
|         return sprintf( | ||||
|             'Expected one of the following: %s; found %s "%s"', | ||||
|             implode(', ', array_keys($valid)), | ||||
|             $token['type'], | ||||
|             $token['value'] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										419
									
								
								vendor/mtdowling/jmespath.php/src/TreeCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								vendor/mtdowling/jmespath.php/src/TreeCompiler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,419 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Tree visitor used to compile JMESPath expressions into native PHP code. | ||||
|  */ | ||||
| class TreeCompiler | ||||
| { | ||||
|     private $indentation; | ||||
|     private $source; | ||||
|     private $vars; | ||||
|  | ||||
|     /** | ||||
|      * @param array  $ast    AST to compile. | ||||
|      * @param string $fnName The name of the function to generate. | ||||
|      * @param string $expr   Expression being compiled. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function visit(array $ast, $fnName, $expr) | ||||
|     { | ||||
|         $this->vars = []; | ||||
|         $this->source = $this->indentation = ''; | ||||
|         $this->write("<?php\n") | ||||
|             ->write('use JmesPath\\TreeInterpreter as Ti;') | ||||
|             ->write('use JmesPath\\FnDispatcher as Fn;') | ||||
|             ->write('use JmesPath\\Utils;') | ||||
|             ->write('') | ||||
|             ->write('function %s(Ti $interpreter, $value) {', $fnName) | ||||
|             ->indent() | ||||
|                 ->dispatch($ast) | ||||
|                 ->write('') | ||||
|                 ->write('return $value;') | ||||
|             ->outdent() | ||||
|         ->write('}'); | ||||
|  | ||||
|         return $this->source; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param array $node | ||||
|      * @return mixed | ||||
|      */ | ||||
|     private function dispatch(array $node) | ||||
|     { | ||||
|         return $this->{"visit_{$node['type']}"}($node); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a monotonically incrementing unique variable name by prefix. | ||||
|      * | ||||
|      * @param string $prefix Variable name prefix | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function makeVar($prefix) | ||||
|     { | ||||
|         if (!isset($this->vars[$prefix])) { | ||||
|             $this->vars[$prefix] = 0; | ||||
|             return '$' . $prefix; | ||||
|         } | ||||
|  | ||||
|         return '$' . $prefix . ++$this->vars[$prefix]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Writes the given line of source code. Pass positional arguments to write | ||||
|      * that match the format of sprintf. | ||||
|      * | ||||
|      * @param string $str String to write | ||||
|      * @return $this | ||||
|      */ | ||||
|     private function write($str) | ||||
|     { | ||||
|         $this->source .= $this->indentation; | ||||
|         if (func_num_args() == 1) { | ||||
|             $this->source .= $str . "\n"; | ||||
|             return $this; | ||||
|         } | ||||
|         $this->source .= vsprintf($str, array_slice(func_get_args(), 1)) . "\n"; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Decreases the indentation level of code being written | ||||
|      * @return $this | ||||
|      */ | ||||
|     private function outdent() | ||||
|     { | ||||
|         $this->indentation = substr($this->indentation, 0, -4); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Increases the indentation level of code being written | ||||
|      * @return $this | ||||
|      */ | ||||
|     private function indent() | ||||
|     { | ||||
|         $this->indentation .= '    '; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     private function visit_or(array $node) | ||||
|     { | ||||
|         $a = $this->makeVar('beforeOr'); | ||||
|         return $this | ||||
|             ->write('%s = $value;', $a) | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('if (!$value && $value !== "0" && $value !== 0) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = %s;', $a) | ||||
|                 ->dispatch($node['children'][1]) | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_and(array $node) | ||||
|     { | ||||
|         $a = $this->makeVar('beforeAnd'); | ||||
|         return $this | ||||
|             ->write('%s = $value;', $a) | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('if ($value || $value === "0" || $value === 0) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = %s;', $a) | ||||
|                 ->dispatch($node['children'][1]) | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_not(array $node) | ||||
|     { | ||||
|         return $this | ||||
|             ->write('// Visiting not node') | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('// Applying boolean not to result of not node') | ||||
|             ->write('$value = !Utils::isTruthy($value);'); | ||||
|     } | ||||
|  | ||||
|     private function visit_subexpression(array $node) | ||||
|     { | ||||
|         return $this | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('if ($value !== null) {') | ||||
|                 ->indent() | ||||
|                 ->dispatch($node['children'][1]) | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_field(array $node) | ||||
|     { | ||||
|         $arr = '$value[' . var_export($node['value'], true) . ']'; | ||||
|         $obj = '$value->{' . var_export($node['value'], true) . '}'; | ||||
|         $this->write('if (is_array($value) || $value instanceof \\ArrayAccess) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = isset(%s) ? %s : null;', $arr, $arr) | ||||
|                 ->outdent() | ||||
|             ->write('} elseif ($value instanceof \\stdClass) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = isset(%s) ? %s : null;', $obj, $obj) | ||||
|                 ->outdent() | ||||
|             ->write("} else {") | ||||
|                 ->indent() | ||||
|                 ->write('$value = null;') | ||||
|                 ->outdent() | ||||
|             ->write("}"); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     private function visit_index(array $node) | ||||
|     { | ||||
|         if ($node['value'] >= 0) { | ||||
|             $check = '$value[' . $node['value'] . ']'; | ||||
|             return $this->write( | ||||
|                 '$value = (is_array($value) || $value instanceof \\ArrayAccess)' | ||||
|                     . ' && isset(%s) ? %s : null;', | ||||
|                 $check, $check | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $a = $this->makeVar('count'); | ||||
|         return $this | ||||
|             ->write('if (is_array($value) || ($value instanceof \\ArrayAccess && $value instanceof \\Countable)) {') | ||||
|                 ->indent() | ||||
|                 ->write('%s = count($value) + %s;', $a, $node['value']) | ||||
|                 ->write('$value = isset($value[%s]) ? $value[%s] : null;', $a, $a) | ||||
|                 ->outdent() | ||||
|             ->write('} else {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = null;') | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_literal(array $node) | ||||
|     { | ||||
|         return $this->write('$value = %s;', var_export($node['value'], true)); | ||||
|     } | ||||
|  | ||||
|     private function visit_pipe(array $node) | ||||
|     { | ||||
|         return $this | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->dispatch($node['children'][1]); | ||||
|     } | ||||
|  | ||||
|     private function visit_multi_select_list(array $node) | ||||
|     { | ||||
|         return $this->visit_multi_select_hash($node); | ||||
|     } | ||||
|  | ||||
|     private function visit_multi_select_hash(array $node) | ||||
|     { | ||||
|         $listVal = $this->makeVar('list'); | ||||
|         $value = $this->makeVar('prev'); | ||||
|         $this->write('if ($value !== null) {') | ||||
|             ->indent() | ||||
|             ->write('%s = [];', $listVal) | ||||
|             ->write('%s = $value;', $value); | ||||
|  | ||||
|         $first = true; | ||||
|         foreach ($node['children'] as $child) { | ||||
|             if (!$first) { | ||||
|                 $this->write('$value = %s;', $value); | ||||
|             } | ||||
|             $first = false; | ||||
|             if ($node['type'] == 'multi_select_hash') { | ||||
|                 $this->dispatch($child['children'][0]); | ||||
|                 $key = var_export($child['value'], true); | ||||
|                 $this->write('%s[%s] = $value;', $listVal, $key); | ||||
|             } else { | ||||
|                 $this->dispatch($child); | ||||
|                 $this->write('%s[] = $value;', $listVal); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this | ||||
|             ->write('$value = %s;', $listVal) | ||||
|             ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_function(array $node) | ||||
|     { | ||||
|         $value = $this->makeVar('val'); | ||||
|         $args = $this->makeVar('args'); | ||||
|         $this->write('%s = $value;', $value) | ||||
|             ->write('%s = [];', $args); | ||||
|  | ||||
|         foreach ($node['children'] as $arg) { | ||||
|             $this->dispatch($arg); | ||||
|             $this->write('%s[] = $value;', $args) | ||||
|                 ->write('$value = %s;', $value); | ||||
|         } | ||||
|  | ||||
|         return $this->write( | ||||
|             '$value = Fn::getInstance()->__invoke("%s", %s);', | ||||
|             $node['value'], $args | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     private function visit_slice(array $node) | ||||
|     { | ||||
|         return $this | ||||
|             ->write('$value = !is_string($value) && !Utils::isArray($value)') | ||||
|             ->write('    ? null : Utils::slice($value, %s, %s, %s);', | ||||
|                 var_export($node['value'][0], true), | ||||
|                 var_export($node['value'][1], true), | ||||
|                 var_export($node['value'][2], true) | ||||
|             ); | ||||
|     } | ||||
|  | ||||
|     private function visit_current(array $node) | ||||
|     { | ||||
|         return $this->write('// Visiting current node (no-op)'); | ||||
|     } | ||||
|  | ||||
|     private function visit_expref(array $node) | ||||
|     { | ||||
|         $child = var_export($node['children'][0], true); | ||||
|         return $this->write('$value = function ($value) use ($interpreter) {') | ||||
|             ->indent() | ||||
|             ->write('return $interpreter->visit(%s, $value);', $child) | ||||
|             ->outdent() | ||||
|         ->write('};'); | ||||
|     } | ||||
|  | ||||
|     private function visit_flatten(array $node) | ||||
|     { | ||||
|         $this->dispatch($node['children'][0]); | ||||
|         $merged = $this->makeVar('merged'); | ||||
|         $val = $this->makeVar('val'); | ||||
|  | ||||
|         $this | ||||
|             ->write('// Visiting merge node') | ||||
|             ->write('if (!Utils::isArray($value)) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = null;') | ||||
|                 ->outdent() | ||||
|             ->write('} else {') | ||||
|                 ->indent() | ||||
|                 ->write('%s = [];', $merged) | ||||
|                 ->write('foreach ($value as %s) {', $val) | ||||
|                     ->indent() | ||||
|                     ->write('if (is_array(%s) && isset(%s[0])) {', $val, $val) | ||||
|                         ->indent() | ||||
|                         ->write('%s = array_merge(%s, %s);', $merged, $merged, $val) | ||||
|                         ->outdent() | ||||
|                     ->write('} elseif (%s !== []) {', $val) | ||||
|                         ->indent() | ||||
|                         ->write('%s[] = %s;', $merged, $val) | ||||
|                         ->outdent() | ||||
|                     ->write('}') | ||||
|                     ->outdent() | ||||
|                 ->write('}') | ||||
|                 ->write('$value = %s;', $merged) | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     private function visit_projection(array $node) | ||||
|     { | ||||
|         $val = $this->makeVar('val'); | ||||
|         $collected = $this->makeVar('collected'); | ||||
|         $this->write('// Visiting projection node') | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write(''); | ||||
|  | ||||
|         if (!isset($node['from'])) { | ||||
|             $this->write('if (!is_array($value) || !($value instanceof \stdClass)) { $value = null; }'); | ||||
|         } elseif ($node['from'] == 'object') { | ||||
|             $this->write('if (!Utils::isObject($value)) { $value = null; }'); | ||||
|         } elseif ($node['from'] == 'array') { | ||||
|             $this->write('if (!Utils::isArray($value)) { $value = null; }'); | ||||
|         } | ||||
|  | ||||
|         $this->write('if ($value !== null) {') | ||||
|             ->indent() | ||||
|             ->write('%s = [];', $collected) | ||||
|             ->write('foreach ((array) $value as %s) {', $val) | ||||
|                 ->indent() | ||||
|                 ->write('$value = %s;', $val) | ||||
|                 ->dispatch($node['children'][1]) | ||||
|                 ->write('if ($value !== null) {') | ||||
|                     ->indent() | ||||
|                     ->write('%s[] = $value;', $collected) | ||||
|                     ->outdent() | ||||
|                 ->write('}') | ||||
|                 ->outdent() | ||||
|             ->write('}') | ||||
|             ->write('$value = %s;', $collected) | ||||
|             ->outdent() | ||||
|         ->write('}'); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     private function visit_condition(array $node) | ||||
|     { | ||||
|         $value = $this->makeVar('beforeCondition'); | ||||
|         return $this | ||||
|             ->write('%s = $value;', $value) | ||||
|             ->write('// Visiting condition node') | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('// Checking result of condition node') | ||||
|             ->write('if (Utils::isTruthy($value)) {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = %s;', $value) | ||||
|                 ->dispatch($node['children'][1]) | ||||
|                 ->outdent() | ||||
|             ->write('} else {') | ||||
|                 ->indent() | ||||
|                 ->write('$value = null;') | ||||
|                 ->outdent() | ||||
|             ->write('}'); | ||||
|     } | ||||
|  | ||||
|     private function visit_comparator(array $node) | ||||
|     { | ||||
|         $value = $this->makeVar('val'); | ||||
|         $a = $this->makeVar('left'); | ||||
|         $b = $this->makeVar('right'); | ||||
|  | ||||
|         $this | ||||
|             ->write('// Visiting comparator node') | ||||
|             ->write('%s = $value;', $value) | ||||
|             ->dispatch($node['children'][0]) | ||||
|             ->write('%s = $value;', $a) | ||||
|             ->write('$value = %s;', $value) | ||||
|             ->dispatch($node['children'][1]) | ||||
|             ->write('%s = $value;', $b); | ||||
|  | ||||
|         if ($node['value'] == '==') { | ||||
|             $this->write('$value = Utils::isEqual(%s, %s);', $a, $b); | ||||
|         } elseif ($node['value'] == '!=') { | ||||
|             $this->write('$value = !Utils::isEqual(%s, %s);', $a, $b); | ||||
|         } else { | ||||
|             $this->write( | ||||
|                 '$value = is_int(%s) && is_int(%s) && %s %s %s;', | ||||
|                 $a, $b, $a, $node['value'], $b | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** @internal */ | ||||
|     public function __call($method, $args) | ||||
|     { | ||||
|         throw new \RuntimeException( | ||||
|             sprintf('Invalid node encountered: %s', json_encode($args[0])) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										235
									
								
								vendor/mtdowling/jmespath.php/src/TreeInterpreter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								vendor/mtdowling/jmespath.php/src/TreeInterpreter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| /** | ||||
|  * Tree visitor used to evaluates JMESPath AST expressions. | ||||
|  */ | ||||
| class TreeInterpreter | ||||
| { | ||||
|     /** @var callable */ | ||||
|     private $fnDispatcher; | ||||
|  | ||||
|     /** | ||||
|      * @param callable $fnDispatcher Function dispatching function that accepts | ||||
|      *                               a function name argument and an array of | ||||
|      *                               function arguments and returns the result. | ||||
|      */ | ||||
|     public function __construct(callable $fnDispatcher = null) | ||||
|     { | ||||
|         $this->fnDispatcher = $fnDispatcher ?: FnDispatcher::getInstance(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Visits each node in a JMESPath AST and returns the evaluated result. | ||||
|      * | ||||
|      * @param array $node JMESPath AST node | ||||
|      * @param mixed $data Data to evaluate | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function visit(array $node, $data) | ||||
|     { | ||||
|         return $this->dispatch($node, $data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Recursively traverses an AST using depth-first, pre-order traversal. | ||||
|      * The evaluation logic for each node type is embedded into a large switch | ||||
|      * statement to avoid the cost of "double dispatch". | ||||
|      * @return mixed | ||||
|      */ | ||||
|     private function dispatch(array $node, $value) | ||||
|     { | ||||
|         $dispatcher = $this->fnDispatcher; | ||||
|  | ||||
|         switch ($node['type']) { | ||||
|  | ||||
|             case 'field': | ||||
|                 if (is_array($value) || $value instanceof \ArrayAccess) { | ||||
|                     return isset($value[$node['value']]) ? $value[$node['value']] : null; | ||||
|                 } elseif ($value instanceof \stdClass) { | ||||
|                     return isset($value->{$node['value']}) ? $value->{$node['value']} : null; | ||||
|                 } | ||||
|                 return null; | ||||
|  | ||||
|             case 'subexpression': | ||||
|                 return $this->dispatch( | ||||
|                     $node['children'][1], | ||||
|                     $this->dispatch($node['children'][0], $value) | ||||
|                 ); | ||||
|  | ||||
|             case 'index': | ||||
|                 if (!Utils::isArray($value)) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 $idx = $node['value'] >= 0 | ||||
|                     ? $node['value'] | ||||
|                     : $node['value'] + count($value); | ||||
|                 return isset($value[$idx]) ? $value[$idx] : null; | ||||
|  | ||||
|             case 'projection': | ||||
|                 $left = $this->dispatch($node['children'][0], $value); | ||||
|                 switch ($node['from']) { | ||||
|                     case 'object': | ||||
|                         if (!Utils::isObject($left)) { | ||||
|                             return null; | ||||
|                         } | ||||
|                         break; | ||||
|                     case 'array': | ||||
|                         if (!Utils::isArray($left)) { | ||||
|                             return null; | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         if (!is_array($left) || !($left instanceof \stdClass)) { | ||||
|                             return null; | ||||
|                         } | ||||
|                 } | ||||
|  | ||||
|                 $collected = []; | ||||
|                 foreach ((array) $left as $val) { | ||||
|                     $result = $this->dispatch($node['children'][1], $val); | ||||
|                     if ($result !== null) { | ||||
|                         $collected[] = $result; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return $collected; | ||||
|  | ||||
|             case 'flatten': | ||||
|                 static $skipElement = []; | ||||
|                 $value = $this->dispatch($node['children'][0], $value); | ||||
|  | ||||
|                 if (!Utils::isArray($value)) { | ||||
|                     return null; | ||||
|                 } | ||||
|  | ||||
|                 $merged = []; | ||||
|                 foreach ($value as $values) { | ||||
|                     // Only merge up arrays lists and not hashes | ||||
|                     if (is_array($values) && isset($values[0])) { | ||||
|                         $merged = array_merge($merged, $values); | ||||
|                     } elseif ($values !== $skipElement) { | ||||
|                         $merged[] = $values; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 return $merged; | ||||
|  | ||||
|             case 'literal': | ||||
|                 return $node['value']; | ||||
|  | ||||
|             case 'current': | ||||
|                 return $value; | ||||
|  | ||||
|             case 'or': | ||||
|                 $result = $this->dispatch($node['children'][0], $value); | ||||
|                 return Utils::isTruthy($result) | ||||
|                     ? $result | ||||
|                     : $this->dispatch($node['children'][1], $value); | ||||
|  | ||||
|             case 'and': | ||||
|                 $result = $this->dispatch($node['children'][0], $value); | ||||
|                 return Utils::isTruthy($result) | ||||
|                     ? $this->dispatch($node['children'][1], $value) | ||||
|                     : $result; | ||||
|  | ||||
|             case 'not': | ||||
|                 return !Utils::isTruthy( | ||||
|                     $this->dispatch($node['children'][0], $value) | ||||
|                 ); | ||||
|  | ||||
|             case 'pipe': | ||||
|                 return $this->dispatch( | ||||
|                     $node['children'][1], | ||||
|                     $this->dispatch($node['children'][0], $value) | ||||
|                 ); | ||||
|  | ||||
|             case 'multi_select_list': | ||||
|                 if ($value === null) { | ||||
|                     return null; | ||||
|                 } | ||||
|  | ||||
|                 $collected = []; | ||||
|                 foreach ($node['children'] as $node) { | ||||
|                     $collected[] = $this->dispatch($node, $value); | ||||
|                 } | ||||
|  | ||||
|                 return $collected; | ||||
|  | ||||
|             case 'multi_select_hash': | ||||
|                 if ($value === null) { | ||||
|                     return null; | ||||
|                 } | ||||
|  | ||||
|                 $collected = []; | ||||
|                 foreach ($node['children'] as $node) { | ||||
|                     $collected[$node['value']] = $this->dispatch( | ||||
|                         $node['children'][0], | ||||
|                         $value | ||||
|                     ); | ||||
|                 } | ||||
|  | ||||
|                 return $collected; | ||||
|  | ||||
|             case 'comparator': | ||||
|                 $left = $this->dispatch($node['children'][0], $value); | ||||
|                 $right = $this->dispatch($node['children'][1], $value); | ||||
|                 if ($node['value'] == '==') { | ||||
|                     return Utils::isEqual($left, $right); | ||||
|                 } elseif ($node['value'] == '!=') { | ||||
|                     return !Utils::isEqual($left, $right); | ||||
|                 } else { | ||||
|                     return self::relativeCmp($left, $right, $node['value']); | ||||
|                 } | ||||
|  | ||||
|             case 'condition': | ||||
|                 return Utils::isTruthy($this->dispatch($node['children'][0], $value)) | ||||
|                     ? $this->dispatch($node['children'][1], $value) | ||||
|                     : null; | ||||
|  | ||||
|             case 'function': | ||||
|                 $args = []; | ||||
|                 foreach ($node['children'] as $arg) { | ||||
|                     $args[] = $this->dispatch($arg, $value); | ||||
|                 } | ||||
|                 return $dispatcher($node['value'], $args); | ||||
|  | ||||
|             case 'slice': | ||||
|                 return is_string($value) || Utils::isArray($value) | ||||
|                     ? Utils::slice( | ||||
|                         $value, | ||||
|                         $node['value'][0], | ||||
|                         $node['value'][1], | ||||
|                         $node['value'][2] | ||||
|                     ) : null; | ||||
|  | ||||
|             case 'expref': | ||||
|                 $apply = $node['children'][0]; | ||||
|                 return function ($value) use ($apply) { | ||||
|                     return $this->visit($apply, $value); | ||||
|                 }; | ||||
|  | ||||
|             default: | ||||
|                 throw new \RuntimeException("Unknown node type: {$node['type']}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function relativeCmp($left, $right, $cmp) | ||||
|     { | ||||
|         if (!is_int($left) || !is_int($right)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         switch ($cmp) { | ||||
|             case '>': return $left > $right; | ||||
|             case '>=': return $left >= $right; | ||||
|             case '<': return $left < $right; | ||||
|             case '<=': return $left <= $right; | ||||
|             default: throw new \RuntimeException("Invalid comparison: $cmp"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										229
									
								
								vendor/mtdowling/jmespath.php/src/Utils.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								vendor/mtdowling/jmespath.php/src/Utils.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | ||||
| <?php | ||||
| namespace JmesPath; | ||||
|  | ||||
| class Utils | ||||
| { | ||||
|     static $typeMap = [ | ||||
|         'boolean' => 'boolean', | ||||
|         'string'  => 'string', | ||||
|         'NULL'    => 'null', | ||||
|         'double'  => 'number', | ||||
|         'float'   => 'number', | ||||
|         'integer' => 'number' | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * Returns true if the value is truthy | ||||
|      * | ||||
|      * @param mixed $value Value to check | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function isTruthy($value) | ||||
|     { | ||||
|         if (!$value) { | ||||
|             return $value === 0 || $value === '0'; | ||||
|         } elseif ($value instanceof \stdClass) { | ||||
|             return (bool) get_object_vars($value); | ||||
|         } else { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the JMESPath type equivalent of a PHP variable. | ||||
|      * | ||||
|      * @param mixed $arg PHP variable | ||||
|      * @return string Returns the JSON data type | ||||
|      * @throws \InvalidArgumentException when an unknown type is given. | ||||
|      */ | ||||
|     public static function type($arg) | ||||
|     { | ||||
|         $type = gettype($arg); | ||||
|         if (isset(self::$typeMap[$type])) { | ||||
|             return self::$typeMap[$type]; | ||||
|         } elseif ($type === 'array') { | ||||
|             if (empty($arg)) { | ||||
|                 return 'array'; | ||||
|             } | ||||
|             reset($arg); | ||||
|             return key($arg) === 0 ? 'array' : 'object'; | ||||
|         } elseif ($arg instanceof \stdClass) { | ||||
|             return 'object'; | ||||
|         } elseif ($arg instanceof \Closure) { | ||||
|             return 'expression'; | ||||
|         } elseif ($arg instanceof \ArrayAccess | ||||
|             && $arg instanceof \Countable | ||||
|         ) { | ||||
|             return count($arg) == 0 || $arg->offsetExists(0) | ||||
|                 ? 'array' | ||||
|                 : 'object'; | ||||
|         } elseif (method_exists($arg, '__toString')) { | ||||
|             return 'string'; | ||||
|         } | ||||
|  | ||||
|         throw new \InvalidArgumentException( | ||||
|             'Unable to determine JMESPath type from ' . get_class($arg) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the provided value is a JMESPath compatible object. | ||||
|      * | ||||
|      * @param mixed $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function isObject($value) | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return !$value || array_keys($value)[0] !== 0; | ||||
|         } | ||||
|  | ||||
|         // Handle array-like values. Must be empty or offset 0 does not exist | ||||
|         return $value instanceof \Countable && $value instanceof \ArrayAccess | ||||
|             ? count($value) == 0 || !$value->offsetExists(0) | ||||
|             : $value instanceof \stdClass; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the provided value is a JMESPath compatible array. | ||||
|      * | ||||
|      * @param mixed $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function isArray($value) | ||||
|     { | ||||
|         if (is_array($value)) { | ||||
|             return !$value || array_keys($value)[0] === 0; | ||||
|         } | ||||
|  | ||||
|         // Handle array-like values. Must be empty or offset 0 exists. | ||||
|         return $value instanceof \Countable && $value instanceof \ArrayAccess | ||||
|             ? count($value) == 0 || $value->offsetExists(0) | ||||
|             : false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * JSON aware value comparison function. | ||||
|      * | ||||
|      * @param mixed $a First value to compare | ||||
|      * @param mixed $b Second value to compare | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public static function isEqual($a, $b) | ||||
|     { | ||||
|         if ($a === $b) { | ||||
|             return true; | ||||
|         } elseif ($a instanceof \stdClass) { | ||||
|             return self::isEqual((array) $a, $b); | ||||
|         } elseif ($b instanceof \stdClass) { | ||||
|             return self::isEqual($a, (array) $b); | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * JMESPath requires a stable sorting algorithm, so here we'll implement | ||||
|      * a simple Schwartzian transform that uses array index positions as tie | ||||
|      * breakers. | ||||
|      * | ||||
|      * @param array    $data   List or map of data to sort | ||||
|      * @param callable $sortFn Callable used to sort values | ||||
|      * | ||||
|      * @return array Returns the sorted array | ||||
|      * @link http://en.wikipedia.org/wiki/Schwartzian_transform | ||||
|      */ | ||||
|     public static function stableSort(array $data, callable $sortFn) | ||||
|     { | ||||
|         // Decorate each item by creating an array of [value, index] | ||||
|         array_walk($data, function (&$v, $k) { $v = [$v, $k]; }); | ||||
|         // Sort by the sort function and use the index as a tie-breaker | ||||
|         uasort($data, function ($a, $b) use ($sortFn) { | ||||
|             return $sortFn($a[0], $b[0]) ?: ($a[1] < $b[1] ? -1 : 1); | ||||
|         }); | ||||
|  | ||||
|         // Undecorate each item and return the resulting sorted array | ||||
|         return array_map(function ($v) { return $v[0]; }, array_values($data)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a Python-style slice of a string or array. | ||||
|      * | ||||
|      * @param array|string $value Value to slice | ||||
|      * @param int|null     $start Starting position | ||||
|      * @param int|null     $stop  Stop position | ||||
|      * @param int          $step  Step (1, 2, -1, -2, etc.) | ||||
|      * | ||||
|      * @return array|string | ||||
|      * @throws \InvalidArgumentException | ||||
|      */ | ||||
|     public static function slice($value, $start = null, $stop = null, $step = 1) | ||||
|     { | ||||
|         if (!is_array($value) && !is_string($value)) { | ||||
|             throw new \InvalidArgumentException('Expects string or array'); | ||||
|         } | ||||
|  | ||||
|         return self::sliceIndices($value, $start, $stop, $step); | ||||
|     } | ||||
|  | ||||
|     private static function adjustEndpoint($length, $endpoint, $step) | ||||
|     { | ||||
|         if ($endpoint < 0) { | ||||
|             $endpoint += $length; | ||||
|             if ($endpoint < 0) { | ||||
|                 $endpoint = $step < 0 ? -1 : 0; | ||||
|             } | ||||
|         } elseif ($endpoint >= $length) { | ||||
|             $endpoint = $step < 0 ? $length - 1 : $length; | ||||
|         } | ||||
|  | ||||
|         return $endpoint; | ||||
|     } | ||||
|  | ||||
|     private static function adjustSlice($length, $start, $stop, $step) | ||||
|     { | ||||
|         if ($step === null) { | ||||
|             $step = 1; | ||||
|         } elseif ($step === 0) { | ||||
|             throw new \RuntimeException('step cannot be 0'); | ||||
|         } | ||||
|  | ||||
|         if ($start === null) { | ||||
|             $start = $step < 0 ? $length - 1 : 0; | ||||
|         } else { | ||||
|             $start = self::adjustEndpoint($length, $start, $step); | ||||
|         } | ||||
|  | ||||
|         if ($stop === null) { | ||||
|             $stop = $step < 0 ? -1 : $length; | ||||
|         } else { | ||||
|             $stop = self::adjustEndpoint($length, $stop, $step); | ||||
|         } | ||||
|  | ||||
|         return [$start, $stop, $step]; | ||||
|     } | ||||
|  | ||||
|     private static function sliceIndices($subject, $start, $stop, $step) | ||||
|     { | ||||
|         $type = gettype($subject); | ||||
|         $len = $type == 'string' ? strlen($subject) : count($subject); | ||||
|         list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step); | ||||
|  | ||||
|         $result = []; | ||||
|         if ($step > 0) { | ||||
|             for ($i = $start; $i < $stop; $i += $step) { | ||||
|                 $result[] = $subject[$i]; | ||||
|             } | ||||
|         } else { | ||||
|             for ($i = $start; $i > $stop; $i += $step) { | ||||
|                 $result[] = $subject[$i]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $type == 'string' ? implode($result, '') : $result; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Manish Verma
					Manish Verma