Skip to content

Commit e2ad48b

Browse files
committed
Applied small optimizations, and fixes
Signed-off-by: Sascha Greuel <[email protected]>
1 parent 78581c2 commit e2ad48b

File tree

5 files changed

+47
-24
lines changed

5 files changed

+47
-24
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
### 0.10.0
4+
- TBA
5+
36
### 0.9.0
47
🔻 Breaking changes ahead:
58

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "softcreatr/jsonpath",
33
"description": "JSONPath implementation for parsing, searching and flattening arrays",
44
"license": "MIT",
5-
"version": "0.9.1",
5+
"version": "0.10.0",
66
"authors": [
77
{
88
"name": "Stephen Frank",

src/Filters/QueryMatchFilter.php

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@
1313
use Flow\JSONPath\AccessHelper;
1414
use Flow\JSONPath\JSONPath;
1515
use Flow\JSONPath\JSONPathException;
16+
use JsonException;
1617
use RuntimeException;
1718

19+
use const JSON_THROW_ON_ERROR;
20+
use const PREG_OFFSET_CAPTURE;
21+
use const PREG_UNMATCHED_AS_NULL;
22+
1823
class QueryMatchFilter extends AbstractFilter
1924
{
2025
protected const MATCH_QUERY_NEGATION_WRAPPED = '^(?<negate>!)\((?<logicalexpr>.+)\)$';
26+
2127
protected const MATCH_QUERY_NEGATION_UNWRAPPED = '^(?<negate>!)(?<logicalexpr>.+)$';
28+
2229
protected const MATCH_QUERY_OPERATORS = '
2330
(@\.(?<key>[^\s<>!=]+)|@\[["\']?(?<keySquare>.*?)["\']?\]|(?<node>@)|(%group(?<group>\d+)%))
2431
(\s*(?<operator>==|=~|=|<>|!==|!=|>=|<=|>|<|in|!in|nin)\s*(?<comparisonValue>.+?(?=(&&|$|\|\||%))))?
@@ -56,7 +63,7 @@ public function filter($collection): array
5663
//sanity check that our group is a group and not something within a string or regular expression
5764
if (\preg_match('/' . static::MATCH_QUERY_OPERATORS . '/x', $test)) {
5865
$filterGroups[$i] = $test;
59-
$filterExpression = \str_replace($matchesGroup[0], "%group$i%", $filterExpression);
66+
$filterExpression = \str_replace($matchesGroup[0], "%group{$i}%", $filterExpression);
6067
}
6168
}
6269
}
@@ -71,16 +78,18 @@ public function filter($collection): array
7178
if (
7279
$match === false
7380
|| !isset($matches[1][0])
74-
|| isset($matches['logicalandor'][array_key_last($matches['logicalandor'])])
81+
|| isset($matches['logicalandor'][\array_key_last($matches['logicalandor'])])
7582
) {
7683
throw new RuntimeException('Malformed filter query');
7784
}
7885

7986
$return = [];
87+
$matchCount = \count($matches[0]);
8088

81-
for ($expressionPart = 0; $expressionPart < \count($matches[0]); $expressionPart++) {
89+
for ($expressionPart = 0; $expressionPart < $matchCount; $expressionPart++) {
8290
$filteredCollection = $collection;
8391
$logicalJoin = $expressionPart > 0 ? $matches['logicalandor'][$expressionPart - 1] : null;
92+
8493
if ($logicalJoin === '&&') {
8594
//Restrict the nodes we need to look at to those already meeting criteria
8695
$filteredCollection = $return;
@@ -92,6 +101,7 @@ public function filter($collection): array
92101
$filter = '$[?(' . $filterGroups[$matches['group'][$expressionPart]] . ')]';
93102
$resolve = (new JSONPath($filteredCollection))->find($filter)->getData();
94103
$return = $resolve;
104+
95105
continue;
96106
}
97107

@@ -102,12 +112,13 @@ public function filter($collection): array
102112
$comparisonValue = $matches['comparisonValue'][$expressionPart] ?? null;
103113

104114
if (\is_string($comparisonValue)) {
105-
$comparisonValue = \preg_replace('/^[\']/', '"', $comparisonValue);
106-
$comparisonValue = \preg_replace('/[\']$/', '"', $comparisonValue);
115+
$comparisonValue = \preg_replace('/^\'/', '"', $comparisonValue);
116+
$comparisonValue = \preg_replace('/\'$/', '"', $comparisonValue);
117+
107118
try {
108-
$comparisonValue = \json_decode($comparisonValue, true, flags: JSON_THROW_ON_ERROR);
109-
} catch (\JsonException $e) {
110-
//Leave $comparisonValue as raw (eg. regular express or non quote wrapped string)
119+
$comparisonValue = \json_decode($comparisonValue, true, 512, JSON_THROW_ON_ERROR);
120+
} catch (JsonException) {
121+
//Leave $comparisonValue as raw (e.g. regular express or non quote wrapped string)
111122
}
112123
}
113124

@@ -116,14 +127,16 @@ public function filter($collection): array
116127
//Short-circuit, node already exists in output due to previous test
117128
continue;
118129
}
119-
$selectedNode = null;
120130

131+
$selectedNode = null;
121132
$notNothing = AccessHelper::keyExists($node, $key, $this->magicIsAllowed);
133+
122134
if ($key) {
123135
if ($notNothing) {
124136
$selectedNode = AccessHelper::getValue($node, $key, $this->magicIsAllowed);
125137
} elseif (\str_contains($key, '.')) {
126138
$foundValue = (new JSONPath($node))->find($key)->getData();
139+
127140
if ($foundValue) {
128141
$selectedNode = $foundValue[0];
129142
$notNothing = true;
@@ -136,6 +149,7 @@ public function filter($collection): array
136149
}
137150

138151
$comparisonResult = null;
152+
139153
if ($notNothing) {
140154
$comparisonResult = match ($operator) {
141155
null => AccessHelper::keyExists($node, $key, $this->magicIsAllowed) || (!$key),
@@ -165,6 +179,7 @@ public function filter($collection): array
165179

166180
//Keep out returned nodes in the same order they were defined in the original collection
167181
\ksort($return);
182+
168183
return $return;
169184
}
170185

@@ -177,14 +192,17 @@ protected function compareEquals($a, $b): bool
177192
{
178193
$type_a = \gettype($a);
179194
$type_b = \gettype($b);
195+
180196
if ($type_a === $type_b || ($this->isNumber($a) && $this->isNumber($b))) {
181197
//Primitives or Numbers
182198
if ($a === null || \is_scalar($a)) {
199+
/** @noinspection TypeUnsafeComparisonInspection */
183200
return $a == $b;
184201
}
185202
//Object/Array
186203
//@TODO array and object comparison
187204
}
205+
188206
return false;
189207
}
190208

@@ -194,6 +212,7 @@ protected function compareLessThan($a, $b): bool
194212
//numerical and string comparison supported only
195213
return $a < $b;
196214
}
215+
197216
return false;
198217
}
199218
}

tests/QueryTest.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ public function testQueries(
7575
self::assertEquals($consensus, $results);
7676

7777
if (\in_array($id, self::$baselineFailedQueries, true)) {
78-
throw new \Exception("XFAIL test $id unexpectedly passed, update baselineFailedQueries.txt");
78+
throw new ExpectationFailedException(
79+
"XFAIL test {$id} unexpectedly passed, update baselineFailedQueries.txt"
80+
);
7981
}
8082
} catch (ExpectationFailedException $e) {
8183
try {
8284
// In some cases, the consensus is just disordered, while
83-
// the actual result is correct. Let's perform a canonicalized
85+
// the actual result is correct. Let's perform a canonical
8486
// assert in these cases. There might be still some false positives
8587
// (e.g. multidimensional comparisons), but that's okay, I guess. Maybe,
8688
// we can also find a way around that in the future.
@@ -100,7 +102,7 @@ public function testQueries(
100102
);
101103
}
102104
}
103-
} catch (JSONPathException|RuntimeException $e) {
105+
} catch (JSONPathException | RuntimeException $e) {
104106
if (!\in_array($id, self::$baselineFailedQueries, true)) {
105107
throw new RuntimeException(
106108
$e->getMessage() . "\nQuery: {$query}\n\nMore information: {$url}",
@@ -1559,55 +1561,55 @@ public static function queryDataProvider(): array
15591561
'rfc_semantics_of_null_object_value',
15601562
'$.a',
15611563
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1562-
'[null]'
1564+
'[null]',
15631565
],
15641566
[
15651567
'rfc_semantics_of_null_null_used_as_array',
15661568
'$.a[0]',
15671569
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1568-
'XFAIL'
1570+
'XFAIL',
15691571
],
15701572
[
15711573
'rfc_semantics_of_null_null_used_as_object',
15721574
'$.a.d',
15731575
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1574-
'XFAIL'
1576+
'XFAIL',
15751577
],
15761578
[
15771579
'rfc_semantics_of_null_array_value',
15781580
'$.b[0]',
15791581
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1580-
'[null]'
1582+
'[null]',
15811583
],
15821584
[
15831585
'rfc_semantics_of_null_array_value_2',
15841586
'$.b[*]',
15851587
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1586-
'[null]'
1588+
'[null]',
15871589
],
15881590
[
15891591
'rfc_semantics_of_null_existence',
15901592
'$.b[?@]',
15911593
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1592-
'[null]'
1594+
'[null]',
15931595
],
15941596
[
15951597
'rfc_semantics_of_null_comparison',
15961598
'$.b[?@==null]',
15971599
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1598-
'[null]'
1600+
'[null]',
15991601
],
16001602
[
16011603
'rfc_semantics_of_null_comparison_with_missing_value',
16021604
'$.c[[email protected]==null]',
16031605
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1604-
'XFAIL'
1606+
'XFAIL',
16051607
],
16061608
[
16071609
'rfc_semantics_of_null_null_string',
16081610
'$.null',
16091611
'{"a": null, "b": [null], "c": [{}], "null": 1}',
1610-
'[1]'
1612+
'[1]',
16111613
],
16121614
];
16131615
}

tests/Traits/TestDataTrait.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
use JsonException;
1212
use RuntimeException;
1313

14-
use const JSON_ERROR_NONE;
1514
use const JSON_THROW_ON_ERROR;
1615

1716
trait TestDataTrait
1817
{
1918
/**
2019
* Returns decoded JSON from a given file either as array or object.
2120
*
22-
* @throws JsonException
21+
* @throws RuntimeException
2322
*/
2423
protected function getData(string $type, bool|int $asArray = true): mixed
2524
{

0 commit comments

Comments
 (0)