Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cd30df3

Browse files
refactor: replace icecave/parity with custom deep comparator (#803)
After considering the options and rethinking the problem we are trying to solve I came top the conclusion we don't need a external library which can handle PHP class equality. Since we are working with JSON we only have scalar types (boolean, float, integer and string) and two collections types array and `stdClass`. This made it simple to write our own deep comparison. Fixes #753
1 parent 848c9ee commit cd30df3

File tree

6 files changed

+165
-10
lines changed

6 files changed

+165
-10
lines changed

‎CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- only check minProperties or maxProperties on objects ([#802](https://github.com/jsonrainbow/json-schema/pull/802))
1111
- replace filter_var for uri and uri-reference to userland code to be RFC 3986 compliant ([#800](https://github.com/jsonrainbow/json-schema/pull/800))
1212

13+
## Changed
14+
- replace icecave/parity with custom deep comparator ([#803](https://github.com/jsonrainbow/json-schema/pull/803))
15+
-
1316
## [6.2.1] - 2025年03月06日
1417
### Fixed
1518
- allow items: true to pass validation ([#801](https://github.com/jsonrainbow/json-schema/pull/801))

‎composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929
"require": {
3030
"php": "^7.2 || ^8.0",
3131
"ext-json": "*",
32-
"marc-mabe/php-enum":"^4.0",
33-
"icecave/parity": "^3.0"
32+
"marc-mabe/php-enum":"^4.0"
3433
},
3534
"require-dev": {
3635
"friendsofphp/php-cs-fixer": "3.3.0",

‎src/JsonSchema/Constraints/ConstConstraint.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
namespace JsonSchema\Constraints;
1313

14-
use Icecave\Parity\Parity;
1514
use JsonSchema\ConstraintError;
1615
use JsonSchema\Entity\JsonPointer;
16+
use JsonSchema\Tool\DeepComparer;
1717

1818
/**
1919
* The ConstConstraint Constraints, validates an element against a constant value
@@ -36,13 +36,13 @@ public function check(&$element, $schema = null, ?JsonPointer $path = null, $i =
3636
$type = gettype($element);
3737
$constType = gettype($const);
3838

39-
if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $constType == 'object') {
40-
if (Parity::isEqualTo((object) $element, $const)) {
39+
if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type === 'array' && $constType === 'object') {
40+
if (DeepComparer::isEqual((object) $element, $const)) {
4141
return;
4242
}
4343
}
4444

45-
if (Parity::isEqualTo($element, $const)) {
45+
if (DeepComparer::isEqual($element, $const)) {
4646
return;
4747
}
4848

‎src/JsonSchema/Constraints/EnumConstraint.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111

1212
namespace JsonSchema\Constraints;
1313

14-
use Icecave\Parity\Parity;
1514
use JsonSchema\ConstraintError;
1615
use JsonSchema\Entity\JsonPointer;
16+
use JsonSchema\Tool\DeepComparer;
1717

1818
/**
1919
* The EnumConstraint Constraints, validates an element against a given set of possibilities
@@ -36,14 +36,15 @@ public function check(&$element, $schema = null, ?JsonPointer $path = null, $i =
3636

3737
foreach ($schema->enum as $enum) {
3838
$enumType = gettype($enum);
39-
if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') {
40-
if (Parity::isEqualTo((object) $element, $enum)) {
39+
40+
if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type === 'array' && $enumType === 'object') {
41+
if (DeepComparer::isEqual((object) $element, $enum)) {
4142
return;
4243
}
4344
}
4445

4546
if ($type === gettype($enum)) {
46-
if (Parity::isEqualTo($element, $enum)) {
47+
if (DeepComparer::isEqual($element, $enum)) {
4748
return;
4849
}
4950
}

‎src/JsonSchema/Tool/DeepComparer.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Tool;
6+
7+
class DeepComparer
8+
{
9+
/**
10+
* @param mixed $left
11+
* @param mixed $right
12+
*/
13+
public static function isEqual($left, $right): bool
14+
{
15+
$isLeftScalar = is_scalar($left);
16+
$isRightScalar = is_scalar($right);
17+
18+
if ($isLeftScalar && $isRightScalar) {
19+
return $left === $right;
20+
}
21+
22+
if ($isLeftScalar !== $isRightScalar) {
23+
return false;
24+
}
25+
26+
if (is_array($left) && is_array($right)) {
27+
return self::isArrayEqual($left, $right);
28+
}
29+
30+
if ($left instanceof \stdClass && $right instanceof \stdClass) {
31+
return self::isArrayEqual((array) $left, (array) $right);
32+
}
33+
34+
return false;
35+
}
36+
37+
/**
38+
* @param array<string|int, mixed> $left
39+
* @param array<string|int, mixed> $right
40+
*/
41+
private static function isArrayEqual(array $left, array $right): bool
42+
{
43+
if (count($left) !== count($right)) {
44+
return false;
45+
}
46+
foreach ($left as $key => $value) {
47+
if (!array_key_exists($key, $right)) {
48+
return false;
49+
}
50+
51+
if (!self::isEqual($value, $right[$key])) {
52+
return false;
53+
}
54+
}
55+
56+
return true;
57+
}
58+
}

‎tests/Tool/DeepComparerTest.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JsonSchema\Tests\Tool;
6+
7+
use JsonSchema\Tool\DeepComparer;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class DeepComparerTest extends TestCase
11+
{
12+
/**
13+
* @dataProvider equalDataProvider
14+
*/
15+
public function testComparesDeepEqualForEqualLeftAndRight($left, $right): void
16+
{
17+
self::assertTrue(DeepComparer::isEqual($left, $right));
18+
}
19+
20+
/**
21+
* @dataProvider notEqualDataProvider
22+
*/
23+
public function testComparesDeepEqualForNotEqualLeftAndRight($left, $right): void
24+
{
25+
self::assertFalse(DeepComparer::isEqual($left, $right));
26+
}
27+
28+
public function equalDataProvider(): \Generator
29+
{
30+
yield 'Boolean true' => [true, true];
31+
yield 'Boolean false' => [false, false];
32+
33+
yield 'Integer one' => [1, 1];
34+
yield 'Integer INT MIN' => [PHP_INT_MIN, PHP_INT_MIN];
35+
yield 'Integer INT MAX' => [PHP_INT_MAX, PHP_INT_MAX];
36+
37+
yield 'Float PI' => [M_PI, M_PI];
38+
39+
yield 'String' => ['hello world!', 'hello world!'];
40+
41+
yield 'array of integer' => [[1, 2, 3], [1, 2, 3]];
42+
yield 'object of integer' => [(object) [1, 2, 3], (object) [1, 2, 3]];
43+
44+
yield 'nested objects of integers' => [
45+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60)],
46+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60)],
47+
];
48+
}
49+
50+
public function notEqualDataProvider(): \Generator
51+
{
52+
yield 'Boolean true/false' => [true, false];
53+
54+
yield 'Integer one/two' => [1, 2];
55+
yield 'Integer INT MIN/MAX' => [PHP_INT_MIN, PHP_INT_MAX];
56+
57+
yield 'Float PI/' => [M_PI, M_E];
58+
59+
yield 'String' => ['hello world!', 'hell0 w0rld!'];
60+
61+
yield 'array of integer with smaller left side' => [[1, 3], [1, 2, 3]];
62+
yield 'array of integer with smaller right side' => [[1, 2, 3], [1, 3]];
63+
yield 'object of integer with smaller left side' => [(object) [1, 3], (object) [1, 2, 3]];
64+
yield 'object of integer with smaller right side' => [(object) [1, 2, 3], (object) [1, 3]];
65+
66+
yield 'nested objects of integers with different left hand side' => [
67+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60, 2)],
68+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60)],
69+
];
70+
yield 'nested objects of integers with different right hand side' => [
71+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60)],
72+
(object) [1 => (object) range(1, 10), 2 => (object) range(50, 60, 2)],
73+
];
74+
75+
$options = [
76+
'boolean' => true,
77+
'integer' => 42,
78+
'float' => M_PI,
79+
'string' => 'hello world!',
80+
'array' => [1, 2, 3],
81+
'object' => (object) [1, 2, 3],
82+
];
83+
84+
foreach ($options as $leftType => $leftValue) {
85+
foreach ($options as $rightType => $rightValue) {
86+
if ($leftType === $rightType) {
87+
continue;
88+
}
89+
90+
yield sprintf('%s vs. %s', $leftType, $rightType) => [$leftValue, $rightValue];
91+
}
92+
}
93+
}
94+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /