v2.1.7
This commit is contained in:
parent
c2ead8e434
commit
77d0cea926
995 changed files with 28142 additions and 8603 deletions
102
vendor/mtdowling/jmespath.php/CHANGELOG.md
vendored
Normal file
102
vendor/mtdowling/jmespath.php/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# CHANGELOG
|
||||
|
||||
## 2.9.2 - 2026-07-06
|
||||
|
||||
* Pass explicit trim characters ahead of the PHP 8.6 trim default change.
|
||||
|
||||
## 2.9.1 - 2026-06-11
|
||||
|
||||
* Fixed the compiled runtime to emit function names as string literals, preventing arbitrary code execution.
|
||||
* Fixed the parser to reject non-identifier function callees, such as literal and raw string callees.
|
||||
|
||||
## 2.9.0 - 2026-06-10
|
||||
|
||||
* Added PHP 8.5 support.
|
||||
* Fixed to_number() to parse number strings using the JSON number grammar.
|
||||
* Fixed reverse() and string slicing to operate on UTF-8 characters rather than bytes.
|
||||
* Fixed slicing of array-like (ArrayAccess + Countable) values.
|
||||
* Fixed equality and contains() to use JSON semantics, e.g. 1 == 1.0 is now true.
|
||||
* Fixed multi-select hashes to end projections, so following tokens apply to the projected list.
|
||||
* Fixed sort() and sort_by() to compare numbers numerically.
|
||||
* Changed sort(), sort_by(), max(), min(), max_by() and min_by() to order strings by code point.
|
||||
* Fixed max_by() and min_by() to error on mixed-type keys instead of returning arbitrary elements.
|
||||
* Fixed max() returning null or erroring when the first array element is falsy, e.g. max([0, 1]).
|
||||
* Fixed sum() and join() to return 0 and an empty string respectively for empty arrays.
|
||||
* Fixed 0.0 to be truthy in filters and logical operators, like every other number.
|
||||
* Fixed the compiled runtime to apply JMESPath truthiness to || and &&.
|
||||
* Fixed @(foo), foo[-] and oversized index literals to throw syntax errors.
|
||||
* Fixed PHP warnings emitted while parsing certain invalid expressions.
|
||||
* Fixed the caret position in syntax error messages for errors at the end of an expression.
|
||||
* Fixed map() to error on non-array second arguments instead of returning [].
|
||||
* Fixed Env::cleanCompileDir() when JP_PHP_COMPILE=on.
|
||||
|
||||
## 2.8.0 - 2024-09-04
|
||||
|
||||
* Add support for PHP 8.4.
|
||||
|
||||
## 2.7.0 - 2023-08-15
|
||||
|
||||
* Fixed flattening in arrays starting with null.
|
||||
* Drop support for HHVM and PHP earlier than 7.2.5.
|
||||
* Add support for PHP 8.1, 8.2, and 8.3.
|
||||
|
||||
## 2.6.0 - 2020-07-31
|
||||
|
||||
* Support for PHP 8.0.
|
||||
|
||||
## 2.5.0 - 2019-12-30
|
||||
|
||||
* Full support for PHP 7.0-7.4.
|
||||
* Fixed autoloading when run from within vendor folder.
|
||||
* Full multibyte (UTF-8) string support.
|
||||
|
||||
## 2.4.0 - 2016-12-03
|
||||
|
||||
* Added support for floats when interpreting data.
|
||||
* Added a function_exists check to work around redeclaration issues.
|
||||
|
||||
## 2.3.0 - 2016-01-05
|
||||
|
||||
* Added support for [JEP-9](https://github.com/jmespath/jmespath.site/blob/master/docs/proposals/improved-filters.rst),
|
||||
including unary filter expressions, and `&&` filter expressions.
|
||||
* Fixed various parsing issues, including not removing escaped single quotes
|
||||
from raw string literals.
|
||||
* Added support for the `map` function.
|
||||
* Fixed several issues with code generation.
|
||||
|
||||
## 2.2.0 - 2015-05-27
|
||||
|
||||
* Added support for [JEP-12](https://github.com/jmespath/jmespath.site/blob/master/docs/proposals/raw-string-literals.rst)
|
||||
and raw string literals (e.g., `'foo'`).
|
||||
|
||||
## 2.1.0 - 2014-01-13
|
||||
|
||||
* Added `JmesPath\Env::cleanCompileDir()` to delete any previously compiled
|
||||
JMESPath expressions.
|
||||
|
||||
## 2.0.0 - 2014-01-11
|
||||
|
||||
* Moving to a flattened namespace structure.
|
||||
* Runtimes are now only PHP callables.
|
||||
* Fixed an error in the way empty JSON literals are parsed so that they now
|
||||
return an empty string to match the Python and JavaScript implementations.
|
||||
* Removed functions from runtimes. Instead there is now a function dispatcher
|
||||
class, FnDispatcher, that provides function implementations behind a single
|
||||
dispatch function.
|
||||
* Removed ExprNode in lieu of just using a PHP callable with bound variables.
|
||||
* Removed debug methods from runtimes and instead into a new Debugger class.
|
||||
* Heavily cleaned up function argument validation.
|
||||
* Slice syntax is now properly validated (i.e., colons are followed by the
|
||||
appropriate value).
|
||||
* Lots of code cleanup and performance improvements.
|
||||
* Added a convenient `JmesPath\search()` function.
|
||||
* **IMPORTANT**: Relocating the project to https://github.com/jmespath/jmespath.php
|
||||
|
||||
## 1.1.1 - 2014-10-08
|
||||
|
||||
* Added support for using ArrayAccess and Countable as arrays and objects.
|
||||
|
||||
## 1.1.0 - 2014-08-06
|
||||
|
||||
* Added the ability to search data returned from json_decode() where JSON
|
||||
objects are returned as stdClass objects.
|
||||
125
vendor/mtdowling/jmespath.php/README.md
vendored
Normal file
125
vendor/mtdowling/jmespath.php/README.md
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# jmespath.php
|
||||
|
||||
JMESPath (pronounced "jaymz path") allows you to declaratively specify how to
|
||||
extract elements from a JSON document. *jmespath.php* allows you to use JMESPath
|
||||
in PHP applications with PHP data structures. It requires PHP 7.2.5 or greater
|
||||
and can be installed through [Composer](https://getcomposer.org/doc/00-intro.md)
|
||||
using the `mtdowling/jmespath.php` package.
|
||||
|
||||
```php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$expression = 'foo.*.baz';
|
||||
|
||||
$data = [
|
||||
'foo' => [
|
||||
'bar' => ['baz' => 1],
|
||||
'bam' => ['baz' => 2],
|
||||
'boo' => ['baz' => 3]
|
||||
]
|
||||
];
|
||||
|
||||
JmesPath\search($expression, $data);
|
||||
// Returns: [1, 2, 3]
|
||||
```
|
||||
|
||||
- [JMESPath Tutorial](https://jmespath.org/tutorial.html)
|
||||
- [JMESPath Grammar](https://jmespath.org/specification.html#grammar)
|
||||
- [JMESPath Python library](https://github.com/jmespath/jmespath.py)
|
||||
|
||||
## PHP Usage
|
||||
|
||||
The `JmesPath\search` function can be used in most cases when using the library.
|
||||
This function utilizes a JMESPath runtime based on your environment. The runtime
|
||||
utilized can be configured using environment variables.
|
||||
|
||||
```php
|
||||
$result = JmesPath\search($expression, $data);
|
||||
|
||||
// or, if you require PSR-4 compliance.
|
||||
$result = JmesPath\Env::search($expression, $data);
|
||||
```
|
||||
|
||||
### Runtimes
|
||||
|
||||
jmespath.php utilizes *runtimes*. There are currently two runtimes: AstRuntime
|
||||
and CompilerRuntime.
|
||||
|
||||
AstRuntime is utilized by `JmesPath\search()` and `JmesPath\Env::search()` by
|
||||
default.
|
||||
|
||||
#### AstRuntime
|
||||
|
||||
The AstRuntime will parse an expression, cache the resulting AST in memory, and
|
||||
interpret the AST using an external tree visitor. AstRuntime provides a good
|
||||
general approach for interpreting JMESPath expressions that have a low to
|
||||
moderate level of reuse.
|
||||
|
||||
```php
|
||||
$runtime = new JmesPath\AstRuntime();
|
||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
||||
// > 'baz'
|
||||
```
|
||||
|
||||
#### CompilerRuntime
|
||||
|
||||
`JmesPath\CompilerRuntime` provides the most performance for applications that
|
||||
have a moderate to high level of reuse of JMESPath expressions. The
|
||||
CompilerRuntime will walk a JMESPath AST and emit PHP source code, resulting in
|
||||
anywhere from 7x to 60x speed improvements.
|
||||
|
||||
Compiling JMESPath expressions to source code is a slower process than just
|
||||
walking and interpreting a JMESPath AST (via the AstRuntime). However, running
|
||||
the compiled JMESPath code results in much better performance than walking an
|
||||
AST. This essentially means that there is a warm-up period when using the
|
||||
`CompilerRuntime`, but after the warm-up period, it will provide much better
|
||||
performance.
|
||||
|
||||
Use the CompilerRuntime if you know that you will be executing JMESPath
|
||||
expressions more than once or if you can pre-compile JMESPath expressions before
|
||||
executing them (for example, server-side applications).
|
||||
|
||||
```php
|
||||
// Note: The cache directory argument is optional.
|
||||
$runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder');
|
||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
||||
// > 'baz'
|
||||
```
|
||||
|
||||
##### Environment Variables
|
||||
|
||||
You can utilize the CompilerRuntime in `JmesPath\search()` by setting the
|
||||
`JP_PHP_COMPILE` environment variable to "on" or to a directory on disk used to
|
||||
store cached expressions.
|
||||
|
||||
## Testing
|
||||
|
||||
A comprehensive list of test cases can be found at
|
||||
https://github.com/jmespath/jmespath.php/tree/master/tests/compliance. These
|
||||
compliance tests are utilized by jmespath.php to ensure consistency with other
|
||||
implementations, and can serve as examples of the language.
|
||||
|
||||
jmespath.php is tested using PHPUnit. In order to run the tests, you need to
|
||||
first install the dependencies using Composer, then you just need to run the
|
||||
tests via make:
|
||||
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
You can run a suite of performance tests as well:
|
||||
|
||||
```bash
|
||||
make perf
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
If you discover a security vulnerability within this package, follow the
|
||||
reporting process from our
|
||||
[Security Policy](https://github.com/jmespath/jmespath.php/security/policy).
|
||||
|
||||
## License
|
||||
|
||||
jmespath.php is made available under the MIT License (MIT). Please see
|
||||
[License File](LICENSE) for more information.
|
||||
123
vendor/mtdowling/jmespath.php/README.rst
vendored
123
vendor/mtdowling/jmespath.php/README.rst
vendored
|
|
@ -1,123 +0,0 @@
|
|||
============
|
||||
jmespath.php
|
||||
============
|
||||
|
||||
JMESPath (pronounced "jaymz path") allows you to declaratively specify how to
|
||||
extract elements from a JSON document. *jmespath.php* allows you to use
|
||||
JMESPath in PHP applications with PHP data structures. It requires PHP 7.2.5 or
|
||||
greater and can be installed through `Composer <http://getcomposer.org/doc/00-intro.md>`_
|
||||
using the ``mtdowling/jmespath.php`` package.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$expression = 'foo.*.baz';
|
||||
|
||||
$data = [
|
||||
'foo' => [
|
||||
'bar' => ['baz' => 1],
|
||||
'bam' => ['baz' => 2],
|
||||
'boo' => ['baz' => 3]
|
||||
]
|
||||
];
|
||||
|
||||
JmesPath\search($expression, $data);
|
||||
// Returns: [1, 2, 3]
|
||||
|
||||
- `JMESPath Tutorial <http://jmespath.org/tutorial.html>`_
|
||||
- `JMESPath Grammar <http://jmespath.org/specification.html#grammar>`_
|
||||
- `JMESPath Python library <https://github.com/jmespath/jmespath.py>`_
|
||||
|
||||
PHP Usage
|
||||
=========
|
||||
|
||||
The ``JmesPath\search`` function can be used in most cases when using the
|
||||
library. This function utilizes a JMESPath runtime based on your environment.
|
||||
The runtime utilized can be configured using environment variables and may at
|
||||
some point in the future automatically utilize a C extension if available.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$result = JmesPath\search($expression, $data);
|
||||
|
||||
// or, if you require PSR-4 compliance.
|
||||
$result = JmesPath\Env::search($expression, $data);
|
||||
|
||||
Runtimes
|
||||
--------
|
||||
|
||||
jmespath.php utilizes *runtimes*. There are currently two runtimes:
|
||||
AstRuntime and CompilerRuntime.
|
||||
|
||||
AstRuntime is utilized by ``JmesPath\search()`` and ``JmesPath\Env::search()``
|
||||
by default.
|
||||
|
||||
AstRuntime
|
||||
~~~~~~~~~~
|
||||
|
||||
The AstRuntime will parse an expression, cache the resulting AST in memory,
|
||||
and interpret the AST using an external tree visitor. AstRuntime provides a
|
||||
good general approach for interpreting JMESPath expressions that have a low to
|
||||
moderate level of reuse.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$runtime = new JmesPath\AstRuntime();
|
||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
||||
// > 'baz'
|
||||
|
||||
CompilerRuntime
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
``JmesPath\CompilerRuntime`` provides the most performance for
|
||||
applications that have a moderate to high level of reuse of JMESPath
|
||||
expressions. The CompilerRuntime will walk a JMESPath AST and emit PHP source
|
||||
code, resulting in anywhere from 7x to 60x speed improvements.
|
||||
|
||||
Compiling JMESPath expressions to source code is a slower process than just
|
||||
walking and interpreting a JMESPath AST (via the AstRuntime). However,
|
||||
running the compiled JMESPath code results in much better performance than
|
||||
walking an AST. This essentially means that there is a warm-up period when
|
||||
using the ``CompilerRuntime``, but after the warm-up period, it will provide
|
||||
much better performance.
|
||||
|
||||
Use the CompilerRuntime if you know that you will be executing JMESPath
|
||||
expressions more than once or if you can pre-compile JMESPath expressions
|
||||
before executing them (for example, server-side applications).
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
// Note: The cache directory argument is optional.
|
||||
$runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder');
|
||||
$runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
|
||||
// > 'baz'
|
||||
|
||||
Environment Variables
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can utilize the CompilerRuntime in ``JmesPath\search()`` by setting
|
||||
the ``JP_PHP_COMPILE`` environment variable to "on" or to a directory
|
||||
on disk used to store cached expressions.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
A comprehensive list of test cases can be found at
|
||||
https://github.com/jmespath/jmespath.php/tree/master/tests/compliance.
|
||||
These compliance tests are utilized by jmespath.php to ensure consistency with
|
||||
other implementations, and can serve as examples of the language.
|
||||
|
||||
jmespath.php is tested using PHPUnit. In order to run the tests, you need to
|
||||
first install the dependencies using Composer as described in the *Installation*
|
||||
section. Next you just need to run the tests via make:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make test
|
||||
|
||||
You can run a suite of performance tests as well:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make perf
|
||||
14
vendor/mtdowling/jmespath.php/SECURITY.md
vendored
Normal file
14
vendor/mtdowling/jmespath.php/SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# SECURITY POLICY
|
||||
|
||||
## Supported Versions
|
||||
|
||||
After each new major release, the previous release will be supported for no
|
||||
less than 24 months, unless explicitly stated otherwise. This may mean that
|
||||
there are multiple supported versions at any given time.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability within this package, please send an
|
||||
email to security@gjcampbell.co.uk. All security vulnerabilities will be
|
||||
promptly addressed. Please do not disclose security-related issues publicly
|
||||
until a fix has been announced.
|
||||
|
|
@ -9,7 +9,6 @@ class AstRuntime
|
|||
private $parser;
|
||||
private $interpreter;
|
||||
private $cache = [];
|
||||
private $cachedCount = 0;
|
||||
|
||||
public function __construct(
|
||||
?Parser $parser = null,
|
||||
|
|
@ -34,10 +33,9 @@ class AstRuntime
|
|||
public function __invoke($expression, $data)
|
||||
{
|
||||
if (!isset($this->cache[$expression])) {
|
||||
// Clear the AST cache when it hits 1024 entries
|
||||
if (++$this->cachedCount > 1024) {
|
||||
// Clear the AST cache when it already holds 1024 entries.
|
||||
if (count($this->cache) >= 1024) {
|
||||
$this->cache = [];
|
||||
$this->cachedCount = 0;
|
||||
}
|
||||
$this->cache[$expression] = $this->parser->parse($expression);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@ namespace JmesPath;
|
|||
* logic to determine the filename:
|
||||
*
|
||||
* 1. Start with the string "jmespath_"
|
||||
* 2. Append the MD5 checksum of the expression.
|
||||
* 2. Append the MD5 checksum of the expression salted with the PHP version
|
||||
* and compiled-cache epoch.
|
||||
* 3. Append ".php"
|
||||
*/
|
||||
class CompilerRuntime
|
||||
{
|
||||
const CACHE_VERSION = 5;
|
||||
|
||||
private $parser;
|
||||
private $compiler;
|
||||
private $cacheDir;
|
||||
|
|
@ -51,7 +54,7 @@ class CompilerRuntime
|
|||
*/
|
||||
public function __invoke($expression, $data)
|
||||
{
|
||||
$functionName = 'jmespath_' . md5($expression);
|
||||
$functionName = self::functionName($expression);
|
||||
|
||||
if (!function_exists($functionName)) {
|
||||
$filename = "{$this->cacheDir}/{$functionName}.php";
|
||||
|
|
@ -64,6 +67,23 @@ class CompilerRuntime
|
|||
return $functionName($this->interpreter, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Shared with DebugRuntime so cache naming cannot drift.
|
||||
*/
|
||||
public static function functionName($expression)
|
||||
{
|
||||
return 'jmespath_' . md5(
|
||||
'jmespath:' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION
|
||||
. ':' . self::CACHE_VERSION . ':' . $expression
|
||||
);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public function getCacheDir()
|
||||
{
|
||||
return $this->cacheDir;
|
||||
}
|
||||
|
||||
private function compile($filename, $expression, $functionName)
|
||||
{
|
||||
$code = $this->compiler->visit(
|
||||
|
|
@ -72,12 +92,26 @@ class CompilerRuntime
|
|||
$expression
|
||||
);
|
||||
|
||||
if (!file_put_contents($filename, $code)) {
|
||||
$tempFile = $filename . '.' . bin2hex(random_bytes(12)) . '.tmp';
|
||||
|
||||
if (!file_put_contents($tempFile, $code)) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Unable to write the compiled PHP code to: %s (%s)',
|
||||
$filename,
|
||||
$tempFile,
|
||||
var_export(error_get_last(), true)
|
||||
));
|
||||
}
|
||||
|
||||
if (!rename($tempFile, $filename)) {
|
||||
@unlink($tempFile);
|
||||
if (!file_exists($filename)) {
|
||||
throw new \RuntimeException(
|
||||
"Unable to move the compiled PHP code to: {$filename}"
|
||||
);
|
||||
}
|
||||
// Another process won the race; its file is equivalent.
|
||||
} elseif (function_exists('opcache_invalidate')) {
|
||||
opcache_invalidate($filename, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ class DebugRuntime
|
|||
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";
|
||||
$functionName = CompilerRuntime::functionName($expression);
|
||||
$filename = $this->runtime->getCacheDir() . '/' . $functionName . '.php';
|
||||
fwrite($this->out, "File: {$filename}\n\n");
|
||||
fprintf($this->out, file_get_contents($filename));
|
||||
fwrite($this->out, is_file($filename)
|
||||
? file_get_contents($filename)
|
||||
: "(not present: {$functionName} was already loaded in this process, likely from another cache directory)\n");
|
||||
}
|
||||
|
||||
private function debugCallback(callable $debugFn, $expression, $data)
|
||||
|
|
|
|||
5
vendor/mtdowling/jmespath.php/src/Env.php
vendored
5
vendor/mtdowling/jmespath.php/src/Env.php
vendored
|
|
@ -57,7 +57,10 @@ final class Env
|
|||
public static function cleanCompileDir()
|
||||
{
|
||||
$total = 0;
|
||||
$compileDir = self::getEnvVariable(self::COMPILE_DIR) ?: sys_get_temp_dir();
|
||||
$compileDir = self::getEnvVariable(self::COMPILE_DIR);
|
||||
if ($compileDir === 'on' || !$compileDir) {
|
||||
$compileDir = sys_get_temp_dir();
|
||||
}
|
||||
|
||||
foreach (glob("{$compileDir}/jmespath_*.php") as $file) {
|
||||
$total++;
|
||||
|
|
|
|||
135
vendor/mtdowling/jmespath.php/src/FnDispatcher.php
vendored
135
vendor/mtdowling/jmespath.php/src/FnDispatcher.php
vendored
|
|
@ -60,12 +60,18 @@ class FnDispatcher
|
|||
{
|
||||
$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 mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false;
|
||||
} else {
|
||||
return null;
|
||||
foreach ($args[0] as $value) {
|
||||
if (Utils::isEqual($value, $args[1])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_string($args[1])
|
||||
? mb_strpos($args[0], $args[1], 0, 'UTF-8') !== false
|
||||
: false;
|
||||
}
|
||||
|
||||
private function fn_ends_with(array $args)
|
||||
|
|
@ -100,7 +106,7 @@ class FnDispatcher
|
|||
$fn = function ($a, $b, $i) use ($args) {
|
||||
return $i ? ($a . $args[0] . $b) : $b;
|
||||
};
|
||||
return $this->reduce('join:0', $args[1], ['string'], $fn);
|
||||
return $args[1] ? $this->reduce('join:0', $args[1], ['string'], $fn) : '';
|
||||
}
|
||||
|
||||
private function fn_keys(array $args)
|
||||
|
|
@ -118,8 +124,8 @@ class FnDispatcher
|
|||
private function fn_max(array $args)
|
||||
{
|
||||
$this->validate('max', $args, [['array']]);
|
||||
$fn = function ($a, $b) {
|
||||
return $a >= $b ? $a : $b;
|
||||
$fn = function ($a, $b, $i) {
|
||||
return $i && self::compareValues($a, $b) >= 0 ? $a : $b;
|
||||
};
|
||||
return $this->reduce('max:0', $args[0], ['number', 'string'], $fn);
|
||||
}
|
||||
|
|
@ -128,10 +134,21 @@ class FnDispatcher
|
|||
{
|
||||
$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;
|
||||
$carryKey = null;
|
||||
$fn = function ($carry, $item, $index) use ($expr, &$carryKey) {
|
||||
if (!$index) {
|
||||
return $item;
|
||||
}
|
||||
if ($index === 1) {
|
||||
$carryKey = $expr($carry);
|
||||
}
|
||||
$itemKey = $expr($item);
|
||||
$this->validateSeq('max_by:0', ['number', 'string'], $carryKey, $itemKey);
|
||||
if (self::compareValues($carryKey, $itemKey) >= 0) {
|
||||
return $carry;
|
||||
}
|
||||
$carryKey = $itemKey;
|
||||
return $item;
|
||||
};
|
||||
return $this->reduce('max_by:1', $args[0], ['any'], $fn);
|
||||
}
|
||||
|
|
@ -140,7 +157,7 @@ class FnDispatcher
|
|||
{
|
||||
$this->validate('min', $args, [['array']]);
|
||||
$fn = function ($a, $b, $i) {
|
||||
return $i && $a <= $b ? $a : $b;
|
||||
return $i && self::compareValues($a, $b) <= 0 ? $a : $b;
|
||||
};
|
||||
return $this->reduce('min:0', $args[0], ['number', 'string'], $fn);
|
||||
}
|
||||
|
|
@ -149,9 +166,21 @@ class FnDispatcher
|
|||
{
|
||||
$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;
|
||||
$carryKey = null;
|
||||
$fn = function ($carry, $item, $index) use ($expr, &$carryKey) {
|
||||
if (!$index) {
|
||||
return $item;
|
||||
}
|
||||
if ($index === 1) {
|
||||
$carryKey = $expr($carry);
|
||||
}
|
||||
$itemKey = $expr($item);
|
||||
$this->validateSeq('min_by:0', ['number', 'string'], $carryKey, $itemKey);
|
||||
if (self::compareValues($carryKey, $itemKey) <= 0) {
|
||||
return $carry;
|
||||
}
|
||||
$carryKey = $itemKey;
|
||||
return $item;
|
||||
};
|
||||
return $this->reduce('min_by:1', $args[0], ['any'], $fn);
|
||||
}
|
||||
|
|
@ -162,7 +191,7 @@ class FnDispatcher
|
|||
if (is_array($args[0])) {
|
||||
return array_reverse($args[0]);
|
||||
} elseif (is_string($args[0])) {
|
||||
return strrev($args[0]);
|
||||
return implode('', array_reverse(mb_str_split($args[0], 1, 'UTF-8')));
|
||||
} else {
|
||||
throw new \RuntimeException('Cannot reverse provided argument');
|
||||
}
|
||||
|
|
@ -174,7 +203,7 @@ class FnDispatcher
|
|||
$fn = function ($a, $b) {
|
||||
return Utils::add($a, $b);
|
||||
};
|
||||
return $this->reduce('sum:0', $args[0], ['number'], $fn);
|
||||
return $args[0] ? $this->reduce('sum:0', $args[0], ['number'], $fn) : 0;
|
||||
}
|
||||
|
||||
private function fn_sort(array $args)
|
||||
|
|
@ -183,7 +212,7 @@ class FnDispatcher
|
|||
$valid = ['string', 'number'];
|
||||
return Utils::stableSort($args[0], function ($a, $b) use ($valid) {
|
||||
$this->validateSeq('sort:0', $valid, $a, $b);
|
||||
return strnatcmp($a, $b);
|
||||
return self::compareValues($a, $b);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +227,7 @@ class FnDispatcher
|
|||
$va = $expr($a);
|
||||
$vb = $expr($b);
|
||||
$this->validateSeq('sort_by:0', $valid, $va, $vb);
|
||||
return strnatcmp($va, $vb);
|
||||
return self::compareValues($va, $vb);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -236,14 +265,53 @@ class FnDispatcher
|
|||
{
|
||||
$this->validateArity('to_number', count($args), 1);
|
||||
$value = $args[0];
|
||||
$type = Utils::type($value);
|
||||
if ($type == 'number') {
|
||||
|
||||
if (Utils::type($value) == 'number') {
|
||||
return $value;
|
||||
} elseif ($type == 'string' && is_numeric($value)) {
|
||||
return mb_strpos($value, '.', 0, 'UTF-8') ? (float) $value : (int) $value;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->parseJsonNumber($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string conforming to the JSON number grammar (RFC 8259) into
|
||||
* an int when exactly representable, otherwise a float. Returns null for
|
||||
* non-conforming or non-finite input.
|
||||
*/
|
||||
private function parseJsonNumber($value)
|
||||
{
|
||||
if (!preg_match('/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/D', $value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('/^-?(?:0|[1-9][0-9]*)$/D', $value)) {
|
||||
return $this->parseJsonInteger($value);
|
||||
}
|
||||
|
||||
$number = (float) $value;
|
||||
|
||||
return is_finite($number) ? $number : null;
|
||||
}
|
||||
|
||||
private function parseJsonInteger($value)
|
||||
{
|
||||
$negative = $value[0] === '-';
|
||||
$digits = $negative ? substr($value, 1) : $value;
|
||||
$limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
|
||||
|
||||
if (strlen($digits) < strlen($limit)
|
||||
|| (strlen($digits) === strlen($limit) && strcmp($digits, $limit) <= 0)
|
||||
) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
$number = (float) $value;
|
||||
|
||||
return is_finite($number) ? $number : null;
|
||||
}
|
||||
|
||||
private function fn_values(array $args)
|
||||
|
|
@ -272,7 +340,7 @@ class FnDispatcher
|
|||
|
||||
private function fn_map(array $args)
|
||||
{
|
||||
$this->validate('map', $args, [['expression'], ['any']]);
|
||||
$this->validate('map', $args, [['expression'], ['array']]);
|
||||
$result = [];
|
||||
foreach ($args[1] as $a) {
|
||||
$result[] = $args[0]($a);
|
||||
|
|
@ -354,6 +422,21 @@ class FnDispatcher
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two values of the same JMESPath type.
|
||||
*
|
||||
* @param mixed $a Value A
|
||||
* @param mixed $b Value B
|
||||
*
|
||||
* @return int Negative if $a < $b, zero if equal, positive if $a > $b.
|
||||
*/
|
||||
private static function compareValues($a, $b)
|
||||
{
|
||||
return Utils::type($a) === 'string'
|
||||
? strcmp((string) $a, (string) $b)
|
||||
: ($a <=> $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces and validates an array of values to a single value using a fn.
|
||||
*
|
||||
|
|
|
|||
29
vendor/mtdowling/jmespath.php/src/Lexer.php
vendored
29
vendor/mtdowling/jmespath.php/src/Lexer.php
vendored
|
|
@ -298,9 +298,10 @@ class Lexer
|
|||
$buffer .= $current;
|
||||
$current = next($chars);
|
||||
} while ($current !== false && isset($this->numbers[$current]));
|
||||
$value = $this->parseIndexNumber($buffer);
|
||||
$tokens[] = [
|
||||
'type' => self::T_NUMBER,
|
||||
'value' => (int)$buffer,
|
||||
'type' => $value === null ? self::T_UNKNOWN : self::T_NUMBER,
|
||||
'value' => $value === null ? $buffer : $value,
|
||||
'pos' => $start
|
||||
];
|
||||
|
||||
|
|
@ -417,6 +418,30 @@ class Lexer
|
|||
return ['type' => $type, 'value' => $buffer, 'pos' => $position];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a bare index/slice integer token ("-"? digit+). Returns null
|
||||
* when the buffer is a lone "-" or the value cannot be represented as a
|
||||
* PHP integer.
|
||||
*/
|
||||
private function parseIndexNumber($buffer)
|
||||
{
|
||||
if ($buffer === '-') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$negative = $buffer[0] === '-';
|
||||
$digits = ltrim($negative ? substr($buffer, 1) : $buffer, '0') ?: '0';
|
||||
$limit = $negative ? substr((string) PHP_INT_MIN, 1) : (string) PHP_INT_MAX;
|
||||
|
||||
if (strlen($digits) > strlen($limit)
|
||||
|| (strlen($digits) === strlen($limit) && strcmp($digits, $limit) > 0)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON token or sets the token type to "unknown" on error.
|
||||
*
|
||||
|
|
|
|||
12
vendor/mtdowling/jmespath.php/src/Parser.php
vendored
12
vendor/mtdowling/jmespath.php/src/Parser.php
vendored
|
|
@ -5,7 +5,7 @@ use JmesPath\Lexer as T;
|
|||
|
||||
/**
|
||||
* JMESPath Pratt parser
|
||||
* @link http://hall.org.ua/halls/wizzard/pdf/Vaughan.Pratt.TDOP.pdf
|
||||
* @link https://dl.acm.org/doi/10.1145/512927.512931
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
|
|
@ -22,6 +22,8 @@ class Parser
|
|||
T::T_EOF => 0,
|
||||
T::T_QUOTED_IDENTIFIER => 0,
|
||||
T::T_IDENTIFIER => 0,
|
||||
T::T_UNKNOWN => 0,
|
||||
T::T_LITERAL => 0,
|
||||
T::T_RBRACKET => 0,
|
||||
T::T_RPAREN => 0,
|
||||
T::T_COMMA => 0,
|
||||
|
|
@ -272,6 +274,10 @@ class Parser
|
|||
|
||||
private function led_lparen(array $left)
|
||||
{
|
||||
if (!isset($left['type'], $left['value']) || $left['type'] !== 'field') {
|
||||
throw $this->syntax('Invalid function name');
|
||||
}
|
||||
|
||||
$args = [];
|
||||
$this->next();
|
||||
|
||||
|
|
@ -347,6 +353,10 @@ class Parser
|
|||
if ($this->token['type'] == T::T_LBRACKET) {
|
||||
$this->next();
|
||||
return $this->parseMultiSelectList();
|
||||
} elseif ($this->token['type'] == T::T_LBRACE) {
|
||||
// Like the multi-select list above, a multi-select hash ends any
|
||||
// projection: tokens that follow apply to the projected list.
|
||||
return $this->nud_lbrace();
|
||||
}
|
||||
|
||||
return $this->expr($bp);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class SyntaxErrorException extends \InvalidArgumentException
|
|||
array $token,
|
||||
$expression
|
||||
) {
|
||||
$token += ['pos' => mb_strlen($expression, 'UTF-8'), 'value' => null];
|
||||
$message = sprintf("Syntax error at character %d\n", max($token['pos'], 0))
|
||||
. $expression . "\n" . str_repeat(' ', max($token['pos'], 0)) . "^\n";
|
||||
$message .= !is_array($expectedTypesOrMessage)
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ class TreeCompiler
|
|||
return $this
|
||||
->write('%s = $value;', $a)
|
||||
->dispatch($node['children'][0])
|
||||
->write('if (!$value && $value !== "0" && $value !== 0) {')
|
||||
->write('if (!Utils::isTruthy($value)) {')
|
||||
->indent()
|
||||
->write('$value = %s;', $a)
|
||||
->dispatch($node['children'][1])
|
||||
|
|
@ -121,7 +121,7 @@ class TreeCompiler
|
|||
return $this
|
||||
->write('%s = $value;', $a)
|
||||
->dispatch($node['children'][0])
|
||||
->write('if ($value || $value === "0" || $value === 0) {')
|
||||
->write('if (Utils::isTruthy($value)) {')
|
||||
->indent()
|
||||
->write('$value = %s;', $a)
|
||||
->dispatch($node['children'][1])
|
||||
|
|
@ -257,8 +257,8 @@ class TreeCompiler
|
|||
}
|
||||
|
||||
return $this->write(
|
||||
'$value = Fd::getInstance()->__invoke("%s", %s);',
|
||||
$node['value'], $args
|
||||
'$value = Fd::getInstance()->__invoke(%s, %s);',
|
||||
var_export($node['value'], true), $args
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +332,7 @@ class TreeCompiler
|
|||
->write('');
|
||||
|
||||
if (!isset($node['from'])) {
|
||||
$this->write('if (!is_array($value) || !($value instanceof \stdClass)) { $value = null; }');
|
||||
$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') {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ class TreeInterpreter
|
|||
|
||||
case 'projection':
|
||||
$left = $this->dispatch($node['children'][0], $value);
|
||||
switch ($node['from']) {
|
||||
$from = isset($node['from']) ? $node['from'] : null;
|
||||
switch ($from) {
|
||||
case 'object':
|
||||
if (!Utils::isObject($left)) {
|
||||
return null;
|
||||
|
|
@ -81,7 +82,7 @@ class TreeInterpreter
|
|||
}
|
||||
break;
|
||||
default:
|
||||
if (!is_array($left) || !($left instanceof \stdClass)) {
|
||||
if (!is_array($left) && !($left instanceof \stdClass)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
67
vendor/mtdowling/jmespath.php/src/Utils.php
vendored
67
vendor/mtdowling/jmespath.php/src/Utils.php
vendored
|
|
@ -22,7 +22,7 @@ class Utils
|
|||
public static function isTruthy($value)
|
||||
{
|
||||
if (!$value) {
|
||||
return $value === 0 || $value === '0';
|
||||
return $value === 0 || $value === 0.0 || $value === '0';
|
||||
} elseif ($value instanceof \stdClass) {
|
||||
return (bool) get_object_vars($value);
|
||||
} else {
|
||||
|
|
@ -58,12 +58,18 @@ class Utils
|
|||
return count($arg) == 0 || $arg->offsetExists(0)
|
||||
? 'array'
|
||||
: 'object';
|
||||
} elseif (method_exists($arg, '__toString')) {
|
||||
return 'string';
|
||||
} elseif (is_object($arg)) {
|
||||
if (method_exists($arg, '__toString')) {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
'Unable to determine JMESPath type from ' . get_class($arg)
|
||||
);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
'Unable to determine JMESPath type from ' . get_class($arg)
|
||||
'Unable to determine JMESPath type from ' . gettype($arg)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +112,10 @@ class Utils
|
|||
}
|
||||
|
||||
/**
|
||||
* JSON aware value comparison function.
|
||||
* JSON-semantic equality: one number type, structural comparison for arrays
|
||||
* and objects, and no object key-order sensitivity.
|
||||
* Empty arrays and empty objects compare equal because PHP cannot represent
|
||||
* that distinction after associative JSON decoding.
|
||||
*
|
||||
* @param mixed $a First value to compare
|
||||
* @param mixed $b Second value to compare
|
||||
|
|
@ -115,15 +124,38 @@ class Utils
|
|||
*/
|
||||
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;
|
||||
$typeA = self::type($a);
|
||||
$typeB = self::type($b);
|
||||
|
||||
if ($typeA !== $typeB) {
|
||||
return ($typeA === 'array' || $typeA === 'object')
|
||||
&& ($typeB === 'array' || $typeB === 'object')
|
||||
&& (array) $a === []
|
||||
&& (array) $b === [];
|
||||
}
|
||||
|
||||
if ($typeA === 'number') {
|
||||
return $a == $b;
|
||||
}
|
||||
|
||||
if ($typeA === 'array' || $typeA === 'object') {
|
||||
$a = (array) $a;
|
||||
$b = (array) $b;
|
||||
|
||||
if (count($a) !== count($b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($a as $key => $value) {
|
||||
if (!array_key_exists($key, $b) || !self::isEqual($value, $b[$key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $a === $b;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,7 +192,7 @@ class Utils
|
|||
* @param callable $sortFn Callable used to sort values
|
||||
*
|
||||
* @return array Returns the sorted array
|
||||
* @link http://en.wikipedia.org/wiki/Schwartzian_transform
|
||||
* @link https://en.wikipedia.org/wiki/Schwartzian_transform
|
||||
*/
|
||||
public static function stableSort(array $data, callable $sortFn)
|
||||
{
|
||||
|
|
@ -192,7 +224,7 @@ class Utils
|
|||
*/
|
||||
public static function slice($value, $start = null, $stop = null, $step = 1)
|
||||
{
|
||||
if (!is_array($value) && !is_string($value)) {
|
||||
if (!is_string($value) && !self::isArray($value)) {
|
||||
throw new \InvalidArgumentException('Expects string or array');
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +271,10 @@ class Utils
|
|||
private static function sliceIndices($subject, $start, $stop, $step)
|
||||
{
|
||||
$type = gettype($subject);
|
||||
$len = $type == 'string' ? mb_strlen($subject, 'UTF-8') : count($subject);
|
||||
if ($type == 'string') {
|
||||
$subject = mb_str_split($subject, 1, 'UTF-8');
|
||||
}
|
||||
$len = count($subject);
|
||||
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
|
||||
|
||||
$result = [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue