refactor: Cleanup git state - commit all staged changes
Major refactoring cleanup: - Add new controller architecture (class-controller-*.php) - Add new settings-v2 UI (views/settings-v2/) - Add new CSS architecture (agentic-sidebar.css, tokens) - Add esbuild build pipeline (scripts/build.js, package.json) - Add composer dependencies (vendor/) - Add frontend src directory (assets/js/src/index.jsx) - Add documentation files - Remove old/obsolete files (class-settings.php, old CSS) This commits all pending changes from previous refactoring efforts.
This commit is contained in:
4
vendor/phpdocumentor/json-path/.gitignore
vendored
Normal file
4
vendor/phpdocumentor/json-path/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
vendor/*
|
||||
.phpunit.cache
|
||||
build
|
||||
/.phpunit.result.cache
|
||||
21
vendor/phpdocumentor/json-path/LICENSE
vendored
Normal file
21
vendor/phpdocumentor/json-path/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2010 Mike van Riel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
32
vendor/phpdocumentor/json-path/README.md
vendored
Normal file
32
vendor/phpdocumentor/json-path/README.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# JSON-path
|
||||
|
||||
JSON-path is a simple JSON path parser and evaluator for PHP. It is based on the JSONPath
|
||||
implementation in [Goessner's JSONPath](http://goessner.net/articles/JsonPath/).
|
||||
The code allows you to parse json paths and evaluate them on php objects. Which makes it a query language for
|
||||
php object structures.
|
||||
|
||||
It's propably not the fastest solution to query php objects, but as the paths are stored as plain strings, it's
|
||||
easy to use them in configuration files or databases. This makes is a good solution for tools that need to query
|
||||
a php object structure based on user input.
|
||||
|
||||
## Installation
|
||||
|
||||
The recommended way to install JSON-path is through [Composer](http://getcomposer.org).
|
||||
|
||||
```bash
|
||||
composer require phpdocumentor/json-path
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```php
|
||||
|
||||
$parser = \phpDocumentor\JsonPath\Parser::createInstance();
|
||||
$query = $parser->parse('.store.book[*].author');
|
||||
|
||||
$executor = new \phpDocumentor\JsonPath\Executor();
|
||||
foreach ($executor->execute($query, $json) as $result) {
|
||||
var_dump($result);
|
||||
}
|
||||
|
||||
```
|
||||
30
vendor/phpdocumentor/json-path/composer.json
vendored
Normal file
30
vendor/phpdocumentor/json-path/composer.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "phpdocumentor/json-path",
|
||||
"description": "Access php objects with json path expressions",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\JsonPath\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"phpDocumentor\\JsonPath\\": "tests/unit"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "jaapio",
|
||||
"email": "jaap@phpdoc.org"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "8.1.*|8.2.*|8.3.*|8.4.*",
|
||||
"parsica-php/parsica": "^0.8.3",
|
||||
"symfony/property-access": "^5.4|^6.4|^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5"
|
||||
}
|
||||
}
|
||||
2431
vendor/phpdocumentor/json-path/composer.lock
generated
vendored
Normal file
2431
vendor/phpdocumentor/json-path/composer.lock
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
20
vendor/phpdocumentor/json-path/phpunit.xml.dist
vendored
Normal file
20
vendor/phpdocumentor/json-path/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" colors="true" bootstrap="vendor/autoload.php" beStrictAboutOutputDuringTests="true" beStrictAboutChangesToGlobalState="true" cacheDirectory=".phpunit.cache">
|
||||
<coverage>
|
||||
<report>
|
||||
<clover outputFile="build/logs/clover.xml"/>
|
||||
<html outputDirectory="build/coverage" lowUpperBound="35" highLowerBound="70"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory>./tests/unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging/>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./src/</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
39
vendor/phpdocumentor/json-path/src/AST/Comparison.php
vendored
Normal file
39
vendor/phpdocumentor/json-path/src/AST/Comparison.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
final class Comparison implements Expression
|
||||
{
|
||||
public function __construct(
|
||||
private readonly QueryNode $left,
|
||||
private readonly string $operator,
|
||||
private readonly QueryNode $right,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root): bool
|
||||
{
|
||||
return match ($this->operator) {
|
||||
'==' => $param->evaluateEqualsComparison($root, $currentObject, $this->left, $this->right),
|
||||
'!=' => $param->evaluateNotEqualsComparison($root, $currentObject, $this->left, $this->right),
|
||||
'starts_with' => $param->evaluateStartsWithComparison($root, $currentObject, $this->left, $this->right),
|
||||
'contains' => $param->evaluateContainsComparison($root, $currentObject, $this->left, $this->right),
|
||||
default => throw new InvalidArgumentException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
25
vendor/phpdocumentor/json-path/src/AST/CurrentNode.php
vendored
Normal file
25
vendor/phpdocumentor/json-path/src/AST/CurrentNode.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
final class CurrentNode implements PathNode
|
||||
{
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $currentObject;
|
||||
}
|
||||
}
|
||||
25
vendor/phpdocumentor/json-path/src/AST/Expression.php
vendored
Normal file
25
vendor/phpdocumentor/json-path/src/AST/Expression.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
interface Expression
|
||||
{
|
||||
/**
|
||||
* @param mixed $currentObject
|
||||
* @param mixed $root
|
||||
*/
|
||||
public function visit(Executor $param, $currentObject, $root): bool;
|
||||
}
|
||||
29
vendor/phpdocumentor/json-path/src/AST/FieldAccess.php
vendored
Normal file
29
vendor/phpdocumentor/json-path/src/AST/FieldAccess.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
class FieldAccess implements PathNode
|
||||
{
|
||||
public function __construct(private readonly FieldName|Expression $fieldName)
|
||||
{
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $param->evaluateFieldAccess($currentObject, $this->fieldName);
|
||||
}
|
||||
}
|
||||
26
vendor/phpdocumentor/json-path/src/AST/FieldName.php
vendored
Normal file
26
vendor/phpdocumentor/json-path/src/AST/FieldName.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
class FieldName
|
||||
{
|
||||
public function __construct(private readonly string $name)
|
||||
{
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
42
vendor/phpdocumentor/json-path/src/AST/FilterNode.php
vendored
Normal file
42
vendor/phpdocumentor/json-path/src/AST/FilterNode.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
use function is_iterable;
|
||||
|
||||
final class FilterNode implements PathNode
|
||||
{
|
||||
public function __construct(private readonly Expression $expression)
|
||||
{
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
if (is_iterable($currentObject) === false) {
|
||||
throw new InvalidArgumentException('Can only filter iterable values %s given');
|
||||
}
|
||||
|
||||
foreach ($currentObject as $current) {
|
||||
if (! $this->expression->visit($param, $current, $root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
yield $current;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
vendor/phpdocumentor/json-path/src/AST/FunctionCall.php
vendored
Normal file
33
vendor/phpdocumentor/json-path/src/AST/FunctionCall.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
final class FunctionCall implements QueryNode
|
||||
{
|
||||
/** @var QueryNode[] */
|
||||
private readonly array $arguments;
|
||||
|
||||
public function __construct(private readonly string $name, QueryNode ...$arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $param->evaluateFunctionCall($root, $currentObject, $this->name, ...$this->arguments);
|
||||
}
|
||||
}
|
||||
36
vendor/phpdocumentor/json-path/src/AST/Path.php
vendored
Normal file
36
vendor/phpdocumentor/json-path/src/AST/Path.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
class Path implements QueryNode
|
||||
{
|
||||
/** @param non-empty-list<PathNode> $nodes */
|
||||
public function __construct(private readonly array $nodes)
|
||||
{
|
||||
}
|
||||
|
||||
/** @return non-empty-list<PathNode>*/
|
||||
public function getNodes(): array
|
||||
{
|
||||
return $this->nodes;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $param->evaluatePath($root, $currentObject, ...$this->nodes);
|
||||
}
|
||||
}
|
||||
18
vendor/phpdocumentor/json-path/src/AST/PathNode.php
vendored
Normal file
18
vendor/phpdocumentor/json-path/src/AST/PathNode.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
interface PathNode extends QueryNode
|
||||
{
|
||||
}
|
||||
27
vendor/phpdocumentor/json-path/src/AST/QueryNode.php
vendored
Normal file
27
vendor/phpdocumentor/json-path/src/AST/QueryNode.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
interface QueryNode
|
||||
{
|
||||
/**
|
||||
* @param mixed $currentObject
|
||||
* @param mixed $root
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function visit(Executor $param, $currentObject, $root);
|
||||
}
|
||||
25
vendor/phpdocumentor/json-path/src/AST/RootNode.php
vendored
Normal file
25
vendor/phpdocumentor/json-path/src/AST/RootNode.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
class RootNode implements PathNode
|
||||
{
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $root ?? $currentObject;
|
||||
}
|
||||
}
|
||||
34
vendor/phpdocumentor/json-path/src/AST/Value.php
vendored
Normal file
34
vendor/phpdocumentor/json-path/src/AST/Value.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
class Value implements QueryNode
|
||||
{
|
||||
public function __construct(private readonly mixed $value)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $currentObject
|
||||
* @param mixed $root
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
21
vendor/phpdocumentor/json-path/src/AST/Wildcard.php
vendored
Normal file
21
vendor/phpdocumentor/json-path/src/AST/Wildcard.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpDocumentor\JsonPath\AST;
|
||||
|
||||
use phpDocumentor\JsonPath\Executor;
|
||||
|
||||
final class Wildcard implements Expression
|
||||
{
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '*';
|
||||
}
|
||||
}
|
||||
224
vendor/phpdocumentor/json-path/src/Executor.php
vendored
Normal file
224
vendor/phpdocumentor/json-path/src/Executor.php
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath;
|
||||
|
||||
use ArrayAccess;
|
||||
use Generator;
|
||||
use phpDocumentor\JsonPath\AST\Expression;
|
||||
use phpDocumentor\JsonPath\AST\FieldName;
|
||||
use phpDocumentor\JsonPath\AST\PathNode;
|
||||
use phpDocumentor\JsonPath\AST\QueryNode;
|
||||
use phpDocumentor\JsonPath\AST\Wildcard;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||
|
||||
use function array_merge;
|
||||
use function current;
|
||||
use function is_array;
|
||||
use function is_iterable;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function iterator_to_array;
|
||||
use function str_starts_with;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
|
||||
final class Executor
|
||||
{
|
||||
private readonly PropertyAccessor $propertyAccessor;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function evaluate(QueryNode $query, mixed $currentElement, mixed $rootElement = null)
|
||||
{
|
||||
return $query->visit($this, $currentElement, $rootElement);
|
||||
}
|
||||
|
||||
public function evaluateEqualsComparison(
|
||||
mixed $root,
|
||||
mixed $currentObject,
|
||||
QueryNode $left,
|
||||
QueryNode $right,
|
||||
): bool {
|
||||
$leftValue = $this->toValue($this->evaluate($left, $currentObject, $root));
|
||||
$rightValue = $this->toValue($this->evaluate($right, $currentObject, $root));
|
||||
|
||||
if (is_string($rightValue)) {
|
||||
return ((string) $leftValue) === $rightValue;
|
||||
}
|
||||
|
||||
return $leftValue === $rightValue;
|
||||
}
|
||||
|
||||
public function evaluateNotEqualsComparison(
|
||||
mixed $root,
|
||||
mixed $currentObject,
|
||||
QueryNode $left,
|
||||
QueryNode $right,
|
||||
): bool {
|
||||
return ! $this->evaluateEqualsComparison($root, $currentObject, $left, $right);
|
||||
}
|
||||
|
||||
public function evaluateStartsWithComparison(
|
||||
mixed $root,
|
||||
mixed $currentObject,
|
||||
QueryNode $left,
|
||||
QueryNode $right,
|
||||
): bool {
|
||||
$leftValue = $this->toValue($this->evaluate($left, $currentObject, $root));
|
||||
$rightValue = $this->toValue($this->evaluate($right, $currentObject, $root));
|
||||
|
||||
return str_starts_with((string) $leftValue, (string) $rightValue);
|
||||
}
|
||||
|
||||
public function evaluateContainsComparison(
|
||||
mixed $root,
|
||||
mixed $currentObject,
|
||||
QueryNode $left,
|
||||
QueryNode $right,
|
||||
): bool {
|
||||
$leftValue = $this->toValue($this->evaluate($left, $currentObject, $root));
|
||||
$rightValue = $this->toValue($this->evaluate($right, $currentObject, $root));
|
||||
|
||||
if (is_iterable($leftValue)) {
|
||||
foreach ($leftValue as $value) {
|
||||
if (is_string($rightValue) && ((string) $value) === $rightValue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($value === $rightValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Generator<mixed>|mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function toValue($value)
|
||||
{
|
||||
if ($value instanceof Generator) {
|
||||
$result = iterator_to_array($value, false);
|
||||
|
||||
return current($result);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function evaluatePath(mixed $root, mixed $currentElement, PathNode ...$nodes)
|
||||
{
|
||||
$result = $currentElement;
|
||||
foreach ($nodes as $node) {
|
||||
$result = $this->evaluate($node, $result, $root);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function evaluateFunctionCall(
|
||||
mixed $root,
|
||||
mixed $currentElement,
|
||||
string $functionName,
|
||||
QueryNode ...$arguments,
|
||||
) {
|
||||
switch ($functionName) {
|
||||
case 'type':
|
||||
$class = $this->evaluate($arguments[0], $currentElement, $root)::class;
|
||||
$isNamespacedClass = strrpos($class, '\\');
|
||||
if ($isNamespacedClass !== false) {
|
||||
return substr($class, $isNamespacedClass + 1);
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Generator<mixed> */
|
||||
public function evaluateFieldAccess(mixed $currentElement, FieldName|Expression $fieldName): Generator
|
||||
{
|
||||
if ($fieldName instanceof Wildcard && is_iterable($currentElement)) {
|
||||
foreach ($currentElement as $element) {
|
||||
foreach ($element as $value) {
|
||||
yield $value;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($currentElement instanceof Generator) {
|
||||
foreach ($currentElement as $element) {
|
||||
foreach ($this->evaluateFieldAccess($element, $fieldName) as $result) {
|
||||
yield $result;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(is_array($currentElement) || $currentElement instanceof ArrayAccess) &&
|
||||
isset($currentElement[$fieldName->getName()])
|
||||
) {
|
||||
yield $currentElement[$fieldName->getName()];
|
||||
} elseif (is_iterable($currentElement)) {
|
||||
$result = [];
|
||||
foreach ($currentElement as $element) {
|
||||
foreach ($this->evaluateFieldAccess($element, $fieldName) as $row) {
|
||||
if (is_iterable($row)) {
|
||||
$result = array_merge(
|
||||
$result,
|
||||
is_array($row) ? $row : iterator_to_array($row, false),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
yield from $result;
|
||||
} else {
|
||||
if (is_object($currentElement) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$this->propertyAccessor->isReadable(
|
||||
$currentElement,
|
||||
new PropertyPath($fieldName->getName()),
|
||||
) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield $this->propertyAccessor->getValue($currentElement, new PropertyPath($fieldName->getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
40
vendor/phpdocumentor/json-path/src/Parser.php
vendored
Normal file
40
vendor/phpdocumentor/json-path/src/Parser.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath;
|
||||
|
||||
use Parsica\Parsica\Parser as InnerParser;
|
||||
use phpDocumentor\JsonPath\AST\Path;
|
||||
use phpDocumentor\JsonPath\Parser\ParserBuilder;
|
||||
|
||||
final class Parser
|
||||
{
|
||||
/** @param InnerParser<Path> $innerParser*/
|
||||
private function __construct(private readonly InnerParser $innerParser)
|
||||
{
|
||||
}
|
||||
|
||||
public static function createInstance(): self
|
||||
{
|
||||
return new self(
|
||||
(new ParserBuilder())->build(),
|
||||
);
|
||||
}
|
||||
|
||||
public function parse(string $query): Query
|
||||
{
|
||||
return new Query(
|
||||
$this->innerParser->tryString($query)->output(),
|
||||
);
|
||||
}
|
||||
}
|
||||
194
vendor/phpdocumentor/json-path/src/Parser/ParserBuilder.php
vendored
Normal file
194
vendor/phpdocumentor/json-path/src/Parser/ParserBuilder.php
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\Parser;
|
||||
|
||||
use Parsica\Parsica\Parser;
|
||||
use phpDocumentor\JsonPath\AST\Comparison;
|
||||
use phpDocumentor\JsonPath\AST\CurrentNode;
|
||||
use phpDocumentor\JsonPath\AST\FieldAccess;
|
||||
use phpDocumentor\JsonPath\AST\FieldName;
|
||||
use phpDocumentor\JsonPath\AST\FilterNode;
|
||||
use phpDocumentor\JsonPath\AST\FunctionCall;
|
||||
use phpDocumentor\JsonPath\AST\Path;
|
||||
use phpDocumentor\JsonPath\AST\RootNode;
|
||||
use phpDocumentor\JsonPath\AST\Value;
|
||||
use phpDocumentor\JsonPath\AST\Wildcard;
|
||||
|
||||
use function is_array;
|
||||
use function Parsica\Parsica\alphaNumChar;
|
||||
use function Parsica\Parsica\any;
|
||||
use function Parsica\Parsica\atLeastOne;
|
||||
use function Parsica\Parsica\between;
|
||||
use function Parsica\Parsica\char;
|
||||
use function Parsica\Parsica\choice;
|
||||
use function Parsica\Parsica\collect;
|
||||
use function Parsica\Parsica\keepSecond;
|
||||
use function Parsica\Parsica\noneOfS;
|
||||
use function Parsica\Parsica\optional;
|
||||
use function Parsica\Parsica\recursive;
|
||||
use function Parsica\Parsica\sepBy;
|
||||
use function Parsica\Parsica\skipHSpace;
|
||||
use function Parsica\Parsica\some;
|
||||
use function Parsica\Parsica\string;
|
||||
use function Parsica\Parsica\whitespace;
|
||||
|
||||
final class ParserBuilder
|
||||
{
|
||||
/** @return Parser<RootNode> */
|
||||
private static function rootNode(): Parser
|
||||
{
|
||||
return char('$')->map(static fn () => new RootNode())->label('$');
|
||||
}
|
||||
|
||||
/** @return Parser<CurrentNode> */
|
||||
private static function currentNode(): Parser
|
||||
{
|
||||
return char('@')->map(static fn () => new CurrentNode());
|
||||
}
|
||||
|
||||
/** @return Parser<FieldAccess> */
|
||||
private static function fieldAccess(): Parser
|
||||
{
|
||||
$fieldName = self::fieldName();
|
||||
|
||||
return choice(
|
||||
keepSecond(char('.'), any($fieldName, self::wildcard())),
|
||||
between(string("['"), string("']"), $fieldName),
|
||||
)->map(static fn ($args) => new FieldAccess($args));
|
||||
}
|
||||
|
||||
/** @return Parser<Wildcard> */
|
||||
private static function wildcard(): Parser
|
||||
{
|
||||
return string('*')->label('Wildcard')->map(static fn () => new Wildcard());
|
||||
}
|
||||
|
||||
/** @return Parser<FilterNode> */
|
||||
private static function filter(): Parser
|
||||
{
|
||||
return choice(
|
||||
between(
|
||||
string('['),
|
||||
string(']'),
|
||||
self::wildcard(),
|
||||
)->map(static fn ($wildcard) => new FilterNode($wildcard)),
|
||||
between(
|
||||
string('[?('),
|
||||
string(')]'),
|
||||
self::expression(),
|
||||
)->map((static fn ($expression) => new FilterNode($expression))),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return Parser<Comparison> */
|
||||
private static function expression(): Parser
|
||||
{
|
||||
$operator = choice(
|
||||
string('=='),
|
||||
string('!='),
|
||||
string('starts_with'),
|
||||
string('contains'),
|
||||
);
|
||||
|
||||
$value = choice(
|
||||
between(char('"'), char('"'), atLeastOne(noneOfS('"')))
|
||||
->map(static fn ($value) => new Value($value)),
|
||||
between(char("'"), char("'"), atLeastOne(noneOfS("'")))
|
||||
->map(static fn ($value) => new Value($value)),
|
||||
)->label('VALUE');
|
||||
|
||||
return collect(
|
||||
choice(
|
||||
self::currentNodeFollowUp(),
|
||||
self::functionCall(),
|
||||
),
|
||||
optional(whitespace())->followedBy($operator),
|
||||
optional(whitespace())->followedBy($value),
|
||||
)->map(static fn ($args) => new Comparison($args[0], $args[1], $args[2]));
|
||||
}
|
||||
|
||||
/** @return Parser<Path> */
|
||||
private static function currentNodeFollowUp(): Parser
|
||||
{
|
||||
$inner = choice(
|
||||
self::fieldAccess(),
|
||||
);
|
||||
|
||||
return self::currentNode()->followedBy(
|
||||
some($inner)->map(static fn ($args) => is_array($args) ? $args : []),
|
||||
)->map(static fn ($args) => new Path([new CurrentNode(), ...$args]));
|
||||
}
|
||||
|
||||
/** @return Parser<FunctionCall> */
|
||||
private static function functionCall(): Parser
|
||||
{
|
||||
return collect(
|
||||
atLeastOne(alphaNumChar()),
|
||||
skipHSpace()->followedBy(
|
||||
between(
|
||||
char('('),
|
||||
char(')'),
|
||||
optional(self::arguments()),
|
||||
),
|
||||
),
|
||||
)->map(static fn ($a) => new FunctionCall($a[0], ...$a[1]));
|
||||
}
|
||||
|
||||
/** @return Parser<list<mixed>> */
|
||||
private static function arguments(): Parser
|
||||
{
|
||||
return sepBy(
|
||||
char(','),
|
||||
choice(self::currentNodeFollowUp(), self::currentNode()),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return Parser<FieldName> */
|
||||
private static function fieldName(): Parser
|
||||
{
|
||||
return atLeastOne(
|
||||
alphaNumChar()->or(char('_')),
|
||||
)->label('NODE_NAME')->map(static fn ($name) => new FieldName($name));
|
||||
}
|
||||
|
||||
/** @return Parser<Path> */
|
||||
private static function rootFollowUp(): Parser
|
||||
{
|
||||
$inner = choice(
|
||||
self::fieldAccess(),
|
||||
self::filter(),
|
||||
);
|
||||
|
||||
$path = recursive();
|
||||
$path->recurse(collect($inner, $path));
|
||||
|
||||
return collect(
|
||||
self::rootNode(),
|
||||
some($inner),
|
||||
)->map(
|
||||
static fn ($args) => new Path([$args[0], ...$args[1]]),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return Parser<Path> */
|
||||
public function build(): Parser
|
||||
{
|
||||
return choice(
|
||||
self::rootFollowUp(),
|
||||
self::currentNodeFollowUp(),
|
||||
self::rootNode(),
|
||||
self::currentNode(),
|
||||
)->thenEof()->label('End of Query');
|
||||
}
|
||||
}
|
||||
29
vendor/phpdocumentor/json-path/src/Query.php
vendored
Normal file
29
vendor/phpdocumentor/json-path/src/Query.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath;
|
||||
|
||||
use phpDocumentor\JsonPath\AST\QueryNode;
|
||||
|
||||
final class Query implements QueryNode
|
||||
{
|
||||
public function __construct(private readonly QueryNode $node)
|
||||
{
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function visit(Executor $param, $currentObject, $root)
|
||||
{
|
||||
return $param->evaluate($this->node, $currentObject, $root);
|
||||
}
|
||||
}
|
||||
276
vendor/phpdocumentor/json-path/tests/unit/ExecutorTest.php
vendored
Normal file
276
vendor/phpdocumentor/json-path/tests/unit/ExecutorTest.php
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath;
|
||||
|
||||
use phpDocumentor\JsonPath\AST\Comparison;
|
||||
use phpDocumentor\JsonPath\AST\CurrentNode;
|
||||
use phpDocumentor\JsonPath\AST\FieldAccess;
|
||||
use phpDocumentor\JsonPath\AST\FieldName;
|
||||
use phpDocumentor\JsonPath\AST\FilterNode;
|
||||
use phpDocumentor\JsonPath\AST\FunctionCall;
|
||||
use phpDocumentor\JsonPath\AST\Path;
|
||||
use phpDocumentor\JsonPath\AST\RootNode;
|
||||
use phpDocumentor\JsonPath\AST\Value;
|
||||
use phpDocumentor\JsonPath\AST\Wildcard;
|
||||
use phpDocumentor\JsonPath\Fixtures\Book;
|
||||
use phpDocumentor\JsonPath\Fixtures\Commic;
|
||||
use phpDocumentor\JsonPath\Fixtures\Store;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
use function iterator_to_array;
|
||||
|
||||
final class ExecutorTest extends TestCase
|
||||
{
|
||||
public function testQueryRootSource(): void
|
||||
{
|
||||
$store = new Store();
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(new FieldName('store')),
|
||||
],
|
||||
),
|
||||
['store' => $store],
|
||||
);
|
||||
|
||||
self::assertSame([$store], iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
public function testQueryRootSourceObject(): void
|
||||
{
|
||||
$root = new stdClass();
|
||||
$store = new Store();
|
||||
$root->store = $store;
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(new FieldName('store')),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertSame([$store], iterator_to_array($result));
|
||||
}
|
||||
|
||||
public function testQuerySubProperty(): void
|
||||
{
|
||||
$root = new stdClass();
|
||||
$store = new Store();
|
||||
$store->addBook(new Book('First book'));
|
||||
$store->addBook(new Book('Second book'));
|
||||
$root->store = $store;
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(new FieldName('store')),
|
||||
new FieldAccess(new FieldName('books')),
|
||||
new FieldAccess(new Wildcard()),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertSame($store->getBooks(), iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
public function testQuerySubPropertyByFilter(): void
|
||||
{
|
||||
$book = new Book('phpDoc');
|
||||
$root = new stdClass();
|
||||
$store = new Store();
|
||||
$store->addBook(new Book('First book'));
|
||||
$store->addBook($book);
|
||||
$store->addBook(new Book('Second book'));
|
||||
$root->store = $store;
|
||||
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(new FieldName('store')),
|
||||
new FieldAccess(new FieldName('books')),
|
||||
new FieldAccess(new Wildcard()),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
new FieldAccess(new FieldName('title')),
|
||||
]),
|
||||
'==',
|
||||
new Value(
|
||||
'phpDoc',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertSame([$book], iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
public function testQuerySubPropertyByFilterFunctionCall(): void
|
||||
{
|
||||
$book = new Commic('phpDoc');
|
||||
$root = new stdClass();
|
||||
$store = new Store();
|
||||
$store->addBook(new Book('First book'));
|
||||
$store->addBook($book);
|
||||
$store->addBook(new Book('Second book'));
|
||||
$root->store = $store;
|
||||
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new Wildcard(),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new FunctionCall(
|
||||
'type',
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
]),
|
||||
),
|
||||
'==',
|
||||
new Value(
|
||||
'Commic',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertSame([$book], iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
public function testQueryWithWildcard(): void
|
||||
{
|
||||
$books = [
|
||||
'phpDoc',
|
||||
'First book',
|
||||
'Second book',
|
||||
];
|
||||
|
||||
$root = new stdClass();
|
||||
$root->store = $this->createStore($books);
|
||||
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Wildcard(),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('title'),
|
||||
),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertSame($books, iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
public function testQueryCollectionInCollection(): void
|
||||
{
|
||||
$books = [
|
||||
'phpDoc',
|
||||
'First book',
|
||||
'Second book',
|
||||
];
|
||||
|
||||
$root = new stdClass();
|
||||
$root->stores = [];
|
||||
|
||||
$root->stores[] = $this->createStore($books);
|
||||
$root->stores[] = $this->createStore(['foo', 'bar']);
|
||||
$root->stores[] = $this->createStore($books);
|
||||
|
||||
$executor = new Executor();
|
||||
$result = $executor->evaluate(
|
||||
new Path(
|
||||
[
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('stores'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Wildcard(),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
new FieldAccess(new FieldName('title')),
|
||||
]),
|
||||
'==',
|
||||
new Value(
|
||||
'phpDoc',
|
||||
),
|
||||
),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('title'),
|
||||
),
|
||||
],
|
||||
),
|
||||
$root,
|
||||
);
|
||||
|
||||
self::assertEquals(['phpDoc', 'phpDoc'], iterator_to_array($result, false));
|
||||
}
|
||||
|
||||
private function createStore(array $books): Store
|
||||
{
|
||||
$store = new Store();
|
||||
foreach ($books as $title) {
|
||||
$store->addBook(new Book($title));
|
||||
}
|
||||
|
||||
return $store;
|
||||
}
|
||||
}
|
||||
26
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Book.php
vendored
Normal file
26
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Book.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\Fixtures;
|
||||
|
||||
class Book
|
||||
{
|
||||
public function __construct(private readonly string $title)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
18
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Commic.php
vendored
Normal file
18
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Commic.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\Fixtures;
|
||||
|
||||
class Commic extends Book
|
||||
{
|
||||
}
|
||||
39
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Store.php
vendored
Normal file
39
vendor/phpdocumentor/json-path/tests/unit/Fixtures/Store.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\Fixtures;
|
||||
|
||||
class Store
|
||||
{
|
||||
/** @var Book[] */
|
||||
private array $books = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function getAddress(): string
|
||||
{
|
||||
return 'My Address';
|
||||
}
|
||||
|
||||
public function addBook(Book $book): void
|
||||
{
|
||||
$this->books[] = $book;
|
||||
}
|
||||
|
||||
public function getBooks(): array
|
||||
{
|
||||
return $this->books;
|
||||
}
|
||||
}
|
||||
282
vendor/phpdocumentor/json-path/tests/unit/Parser/ParserBuilderTest.php
vendored
Normal file
282
vendor/phpdocumentor/json-path/tests/unit/Parser/ParserBuilderTest.php
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of phpDocumentor.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*
|
||||
* @link https://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace phpDocumentor\JsonPath\Parser;
|
||||
|
||||
use Generator;
|
||||
use Parsica\Parsica\Parser;
|
||||
use phpDocumentor\JsonPath\AST\Comparison;
|
||||
use phpDocumentor\JsonPath\AST\CurrentNode;
|
||||
use phpDocumentor\JsonPath\AST\FieldAccess;
|
||||
use phpDocumentor\JsonPath\AST\FieldName;
|
||||
use phpDocumentor\JsonPath\AST\FilterNode;
|
||||
use phpDocumentor\JsonPath\AST\FunctionCall;
|
||||
use phpDocumentor\JsonPath\AST\Path;
|
||||
use phpDocumentor\JsonPath\AST\RootNode;
|
||||
use phpDocumentor\JsonPath\AST\Value;
|
||||
use phpDocumentor\JsonPath\AST\Wildcard;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ParserBuilderTest extends TestCase
|
||||
{
|
||||
private Parser $parser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->parser = (new ParserBuilder())->build();
|
||||
}
|
||||
|
||||
public function testRootNodeIsParsed(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$');
|
||||
self::assertEquals(new RootNode(), $result->output());
|
||||
}
|
||||
|
||||
public function testCurrentNodeIsParsed(): void
|
||||
{
|
||||
$result = $this->parser->tryString('@');
|
||||
self::assertEquals(new CurrentNode(), $result->output());
|
||||
}
|
||||
|
||||
public function testCurrentNodeChildren(): void
|
||||
{
|
||||
$result = $this->parser->tryString('@.*');
|
||||
self::assertEquals(new Path([new CurrentNode(), new FieldAccess(new Wildcard())]), $result->output());
|
||||
}
|
||||
|
||||
public function testRootFieldAccess(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store');
|
||||
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRootFieldAccessArrayLike(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$[\'store\']');
|
||||
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testRootFieldChildAccess(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.address');
|
||||
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('address'),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
#[DataProvider('operatorProvider')]
|
||||
public function testFilterExpression(string $operator): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books[?(@.title ' . $operator . ' "phpDoc")]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
new FieldAccess(new FieldName('title')),
|
||||
]),
|
||||
$operator,
|
||||
new Value(
|
||||
'phpDoc',
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return Generator<string, string> */
|
||||
public static function operatorProvider(): Generator
|
||||
{
|
||||
$operators = [
|
||||
'==',
|
||||
'!=',
|
||||
'starts_with',
|
||||
];
|
||||
|
||||
foreach ($operators as $operator) {
|
||||
yield $operator => [$operator];
|
||||
}
|
||||
}
|
||||
|
||||
public function testFilterExpressionCurrentObjectProperyWildCard(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books[?(@.* == "phpDoc")]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
new FieldAccess(new Wildcard()),
|
||||
]),
|
||||
'==',
|
||||
new Value(
|
||||
'phpDoc',
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testFilterExpressionCurrentObjectTypeEquals(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books[?(type(@) == "api")]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new FunctionCall(
|
||||
'type',
|
||||
new CurrentNode(),
|
||||
),
|
||||
'==',
|
||||
new Value(
|
||||
'api',
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testFilterExpressionCurrentObjectChildrenTypeEquals(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books[?(type(@.*) == "api")]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Comparison(
|
||||
new FunctionCall(
|
||||
'type',
|
||||
new Path([
|
||||
new CurrentNode(),
|
||||
new FieldAccess(new Wildcard()),
|
||||
]),
|
||||
),
|
||||
'==',
|
||||
new Value(
|
||||
'api',
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testFilterExpressionWildcard(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books_title[*]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books_title'),
|
||||
),
|
||||
new FilterNode(
|
||||
new Wildcard(),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testFilterExpressionWildcardNested(): void
|
||||
{
|
||||
$result = $this->parser->tryString('$.store.books_title.*[*]');
|
||||
self::assertEquals(
|
||||
new Path([
|
||||
new RootNode(),
|
||||
new FieldAccess(
|
||||
new FieldName('store'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new FieldName('books_title'),
|
||||
),
|
||||
new FieldAccess(
|
||||
new Wildcard(),
|
||||
),
|
||||
new FilterNode(
|
||||
new Wildcard(),
|
||||
),
|
||||
]),
|
||||
$result->output(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user