├── .gitignore
├── .php_cs
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml
├── src
├── AnyComparable.php
├── Comparator
│ ├── Comparator.php
│ ├── DeepComparator.php
│ ├── ObjectIdentityComparator.php
│ ├── ParityComparator.php
│ ├── PhpComparator.php
│ └── StrictPhpComparator.php
├── Exception
│ └── NotComparableException.php
├── ExtendedComparable.php
├── ExtendedComparableTrait.php
├── Parity.php
├── RestrictedComparable.php
├── SelfComparable.php
└── SubClassComparable.php
└── test
├── src
├── ChildObject.php
├── Comparator
│ ├── SelfComparableImpl.php
│ ├── SelfComparableSubClass.php
│ ├── SubClassComparableImpl.php
│ └── SubClassComparableSubClass.php
├── ExtendedComparableTraitUser.php
└── ParentObject.php
└── suite
├── Comparator
├── DeepComparatorTest.php
├── ObjectIdentityComparatorTest.php
├── ParityComparatorTest.php
├── PhpComparatorTest.php
└── StrictPhpComparatorTest.php
├── Exception
└── NotComparableExceptionTest.php
├── ExtendedComparableTraitTest.php
└── ParityTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /artifacts/
2 | /vendor/
3 | .phpunit.result.cache
4 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 | in(__DIR__)
5 | ->exclude([
6 | 'artifacts',
7 | 'vendor',
8 | ]);
9 |
10 | return PhpCsFixer\Config::create()
11 | ->setFinder($finder)
12 | ->setCacheFile(__DIR__ . '/artifacts/.php_cs.cache')
13 | ->setRules([
14 | '@PSR2' => true,
15 |
16 | 'array_syntax' => ['syntax' => 'short'],
17 | 'binary_operator_spaces' => false,
18 | 'blank_line_after_opening_tag' => true,
19 | 'blank_line_before_return' => true,
20 | 'braces' => false,
21 | 'cast_spaces' => true,
22 | 'class_definition' => ['singleLine' => false],
23 | 'concat_space' => ['spacing' => 'one'],
24 | 'declare_equal_normalize' => true,
25 | 'function_typehint_space' => true,
26 | 'hash_to_slash_comment' => true,
27 | 'heredoc_to_nowdoc' => true,
28 | 'include' => true,
29 | 'linebreak_after_opening_tag' => true,
30 | 'lowercase_cast' => true,
31 | 'method_separation' => true,
32 | 'native_function_casing' => true,
33 | 'new_with_braces' => true,
34 | 'no_blank_lines_after_class_opening' => true,
35 | 'no_blank_lines_after_phpdoc' => true,
36 | 'no_empty_comment' => true,
37 | 'no_empty_phpdoc' => true,
38 | 'no_empty_statement' => true,
39 | 'no_extra_consecutive_blank_lines' => true,
40 | 'no_leading_import_slash' => true,
41 | 'no_leading_namespace_whitespace' => true,
42 | 'no_mixed_echo_print' => true,
43 | 'no_multiline_whitespace_before_semicolons' => true,
44 | 'no_short_bool_cast' => true,
45 | 'no_singleline_whitespace_before_semicolons' => true,
46 | 'no_spaces_around_offset' => true,
47 | 'no_trailing_comma_in_singleline_array' => true,
48 | 'no_unneeded_control_parentheses' => true,
49 | 'no_unused_imports' => true,
50 | 'no_whitespace_before_comma_in_array' => true,
51 | 'no_whitespace_in_blank_line' => true,
52 | 'normalize_index_brace' => true,
53 | 'object_operator_without_whitespace' => true,
54 | 'ordered_imports' => true,
55 | 'phpdoc_align' => true,
56 | 'phpdoc_indent' => true,
57 | 'phpdoc_no_package' => true,
58 | 'phpdoc_scalar' => true,
59 | 'phpdoc_to_comment' => true,
60 | 'phpdoc_trim' => true,
61 | 'phpdoc_types' => true,
62 | 'pre_increment' => true,
63 | 'return_type_declaration' => true,
64 | 'self_accessor' => true,
65 | 'short_scalar_cast' => true,
66 | 'single_blank_line_before_namespace' => true,
67 | 'single_quote' => true,
68 | 'space_after_semicolon' => true,
69 | 'standardize_not_equals' => true,
70 | 'ternary_operator_spaces' => true,
71 | 'trailing_comma_in_multiline_array' => true,
72 | 'trim_array_spaces' => true,
73 | 'unary_operator_spaces' => true,
74 | 'whitespace_after_comma_in_array' => true,
75 | ]);
76 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: php
3 | php:
4 | - '7.3'
5 | - '7.3'
6 | - '7.4'
7 | - '8.0'
8 | matrix:
9 | fast_finish: true
10 | allow_failures:
11 | - php: nightly
12 | before_install:
13 | - phpenv config-rm xdebug.ini || true
14 | - "[[ $GITHUB_TOKEN ]] && composer config --global github-oauth.github.com $GITHUB_TOKEN"
15 | install: composer install --no-interaction
16 | script: phpdbg -qrr vendor/bin/phpunit
17 | after_script: bash <(curl -s https://codecov.io/bash)
18 | env:
19 | global:
20 | secure: H0ZWjPYUbKpxE9nR0wA5S3oWG827F7daF473DGi40Afxej5h7kAWe2gcHHZrFxDwXR65RpcRw/kcL9hXld1aNtehZRES7wiktFLqExQTQn/Z5EYal8Jx9qFpq6Wb2Pvs0sF+9DO0QUNiDeVjt7g/fNsahAZ/MKzObIH0swNJOmg=
21 | cache:
22 | directories:
23 | - "$HOME/.composer/cache/files"
24 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Parity Changelog
2 |
3 | ### 3.0.1 (2021-02-04)
4 |
5 | Please note, as of this release this project intends to support only those
6 | versions of PHP that are listed under the "Currently Supported Versions" section
7 | of https://www.php.net/supported-versions.php. This means that support for older
8 | PHP versions may be dropped in minor or patch releases.
9 |
10 | - Drop support for PHP 7.2
11 | - Add support for PHP 8
12 |
13 | ### 3.0.0 (2020-08-25)
14 |
15 | This release contains several backwards compatibility breaks that will effect
16 | more advanced usage of Parity, however the common usage via `Parity::compare()`
17 | is unchanged.
18 |
19 | - **[BC]** Drop support for PHP 7.1
20 | - **[BC]** Add parameter and return type hints
21 | - **[BC]** Remove `PackageInfo` class
22 | - **[BC]** Rename `ComparatorInterface` to `Comparator`
23 | - **[BC]** Rename `AnyComparableInterface` to `AnyComparable`
24 | - **[BC]** Rename `ExtendedComparableInterface` to `ExtendedComparable`
25 | - **[BC]** Rename `SelfComparableInterface` to `SelfComparable`
26 | - **[BC]** Rename `SubClassComparableInterface` to `SubClassComparable`
27 | - **[BC]** Remove `AbstractExtendedComparable` class, use `ExtendedComparableTrait` instead
28 |
29 | ### 2.0.0 (2018-11-06)
30 |
31 | - **[BC]** Drop support for PHP 5.x and PHP 7.0
32 | - **[NEW]** Added `Parity::min[Sequence]()`, `max[Sequence]()` (thanks [@koden-km](https://github.com/jmalloc))
33 |
34 | ### 1.0.0 (2014-01-17)
35 |
36 | * Stable release (no API changes since 1.0.0-alpha.2).
37 |
38 | ### 1.0.0-alpha.2 (2013-10-22)
39 |
40 | - **[BC]** `SelfComparableInterface` now requires the comparison operands to be the exactly same type.
41 | - **[NEW]** Added `SubClassComparableInterface`, similar to the previous behavior of `SelfComparableInterface`
42 |
43 | ### 1.0.0-alpha.1 (2013-10-20)
44 |
45 | - **[BC]** Removed `deep` parameter from `Parity::compare()` and related convenience methods
46 | - **[BC]** Removed `ComparableInterface` and `Comparator`
47 | - **[BC]** Removed `AbstractComparable` (replaced with `AbstractExtendedComparable` which does not implement any comparable interface)
48 | - **[NEW]** Added `AnyComparableInterface` and `SelfComparableInterface`
49 | - **[NEW]** Added `ComparatorInterface::__invoke()`
50 | - **[NEW]** Added `ObjectIdentityComparator`, `StrictPhpComparator` and `PhpComparator`
51 | - **[NEW]** Added `ParityComparator` which compares numeric types in a more natural manner
52 | - **[IMPROVED]** Comparators are now use other comparators to handle unsupported comparisons (composition rather than extension)
53 | - **[IMPROVED]** Deep comparison no longer compares arrays by size first, allows for more natural ordering based on elements
54 |
55 | ### 0.1.0 (2013-05-29)
56 |
57 | - Initial release
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2013 James Harris
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Parity
2 |
3 | [](https://travis-ci.org/icecave/parity)
4 | [](https://codecov.io/github/icecave/parity)
5 | [](https://semver.org)
6 |
7 | **Parity** is a deep comparison library for PHP.
8 |
9 | composer require icecave/parity
10 |
11 | ## Rationale
12 |
13 | PHP does not provide a way to reliably and strictly compare values of heterogeneous types. Most of the built-in
14 | comparison operators perform often undesired [type-juggling](http://php.net/manual/en/language.types.type-juggling.php).
15 | The one exception is the [strict equality operator](http://php.net/manual/en/language.operators.comparison.php), which
16 | has the further caveat that it can only compare objects by their identity. No type-strict mechanism is provided for
17 | comparing objects by their properties; nor are there any type-strict versions of the relative comparison operators
18 | (less-than, greater-than, etc).
19 |
20 | **Parity** aims to fill the void by providing a comparison engine with the following features:
21 |
22 | * Type-strict comparison of arrays and objects by their elements
23 | * Recursion-safe comparison of objects
24 | * Natural, type-strict comparison semantics for built-in types
25 | * Powerful mechanisms for classes to customize comparison behavior
26 |
27 | ## Example
28 |
29 | The **Parity** comparison engine is accessed via static methods on the `Parity` facade class. These methods accept any
30 | types and are guaranteed to produce a deterministic comparison result[1](#caveat1). Some basic examples are
31 | shown below using integers.
32 |
33 | ```php
34 | use Icecave\Parity\Parity;
35 |
36 | // The compare() method provides a strcmp-style comparison, and hence can be
37 | // used as a sorting function for operations such as usort()
38 | assert(Parity::compare(1, 2) < 0);
39 |
40 | // The following methods are convenience methods, implemented on top of compare().
41 | assert(Parity::isEqualTo(1, 2) === false);
42 | assert(Parity::isNotEqualTo(1, 2) === true);
43 | assert(Parity::isNotEqualTo(1, 2) === true);
44 | assert(Parity::isLessThan(1, 2) === true);
45 | assert(Parity::isLessThanOrEqualTo(1, 2) === true);
46 | assert(Parity::isGreaterThan(1, 2) === false);
47 | assert(Parity::isGreaterThanOrEqualTo(1, 2) === false);
48 | ```
49 |
50 | ## Concepts
51 |
52 | ### Comparable
53 |
54 | The core concept of **Parity** is the *comparable*. A comparable is any object that provides its own behavior for
55 | comparison with other values. The following refinements of the comparable concept are supported by the comparison engine:
56 |
57 | * [Restricted Comparable](src/RestrictedComparable.php): A comparable that can be queried as to which values it may be compared to.
58 | * [Self Comparable](src/SelfComparable.php): A comparable that may only be compared to other objects of exactly the same type.
59 | * [Sub Class Comparable](src/SubClassComparable.php): A comparable that may only be compared to other objects of the same or a derived type.
60 | * [Any Comparable](src/AnyComparable.php): A comparable that may be freely compared to values of any other type.
61 |
62 | ### Comparator
63 |
64 | A *[Comparator](src/Comparator/Comparator.php)* defines comparison behavior for values other
65 | than itself. **Parity** provides the following comparator implementations:
66 |
67 | * [Parity Comparator](src/Comparator/ParityComparator.php): Implements the logic surrounding the comparable concepts described in the section above.
68 | * [Deep Comparator](src/Comparator/DeepComparator.php): Performs deep comparison of arrays and objects. Object comparison is recursion-safe.
69 | * [Object Identity Comparator](src/Comparator/ObjectIdentityComparator.php): Compares objects by identity.
70 | * [Strict PHP Comparator](src/Comparator/StrictPhpComparator.php): Approximates PHP's strict comparison for the full suite of comparison operations.
71 | * [PHP Comparator](src/Comparator/PhpComparator.php): Exposes the standard PHP comparison behavior as a Parity comparator.
72 |
73 | ## Algorithm Resolution
74 |
75 | The following process is used by `Parity::compare($A, $B)` to determine which comparison algorithm to use:
76 |
77 | Use `$A->compare($B)` if:
78 |
79 | 1. `$A` is [Any Comparable](src/AnyComparable.php); or
80 | 2. `$A` is [Restricted Comparable](src/RestrictedComparable.php) and `$A->canCompare($B)` returns `true`; or
81 | 3. `$A` is [Self Comparable](src/SelfComparable.php) and `$A` is exactly the same type as `$B`; or
82 | 4. `$A` is [Sub Class Comparable](src/SubClassComparable.php) and `$B` is an instance of the class where `$A->compare()` is implemented
83 |
84 | If none of the conditions above are true, the comparison is tried in reverse with `$A` on the right hand side and `$B`
85 | on the left; the result is also inverted. If still no comparison is possible, **Parity** falls back to a strictly-typed
86 | deep comparison.
87 |
88 | When comparing scalar types, integers and doubles (PHP's only true numeric types) are treated as though they were the
89 | same type, such that the expression `3 < 3.5 < 4` holds true. Numeric strings are **not** compared in this way.
90 |
91 | ## Caveats
92 |
93 | 1. Comparison of recursive objects is not a truly deterministic operation as objects are compared
94 | by their [object hash](http://php.net/manual/en/function.spl-object-hash.php) where deeper comparison would otherwise
95 | result in infinite recursion.
96 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "icecave/parity",
3 | "description": "A customizable deep comparison library.",
4 | "keywords": [
5 | "compare",
6 | "comparison",
7 | "equal",
8 | "equality",
9 | "less",
10 | "greater",
11 | "sort",
12 | "sorting"
13 | ],
14 | "homepage": "https://github.com/icecave/parity",
15 | "license": "MIT",
16 | "authors": [
17 | {
18 | "name": "James Harris",
19 | "email": "mailjamesharris@gmail.com",
20 | "homepage": "https://github.com/jmalloc"
21 | }
22 | ],
23 | "require": {
24 | "php": ">=7.3",
25 | "icecave/repr": "^4"
26 | },
27 | "require-dev": {
28 | "eloquent/liberator": "^2",
29 | "friendsofphp/php-cs-fixer": "^2",
30 | "eloquent/phony": "^5",
31 | "eloquent/phony-phpunit": "^7",
32 | "phpunit/phpunit": "^9"
33 | },
34 | "autoload": { "psr-4": { "Icecave\\Parity\\": "src" } },
35 | "autoload-dev": { "psr-4": { "Icecave\\Parity\\": "test/src" } },
36 | "config": {
37 | "sort-packages": true,
38 | "platform": {
39 | "php": "7.3"
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | test/suite
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | src
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/AnyComparable.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
20 | * +--------------------+---------------+
21 | *
22 | * @param mixed $value The value to compare.
23 | *
24 | * @return int The result of the comparison.
25 | */
26 | public function compare($value): int;
27 | }
28 |
--------------------------------------------------------------------------------
/src/Comparator/Comparator.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
19 | * +--------------------+---------------+
20 | *
21 | * @param mixed $lhs The first value to compare.
22 | * @param mixed $rhs The second value to compare.
23 | *
24 | * @return int The result of the comparison.
25 | */
26 | public function compare($lhs, $rhs): int;
27 |
28 | /**
29 | * An alias for compare().
30 | *
31 | * @param mixed $lhs The first value to compare.
32 | * @param mixed $rhs The second value to compare.
33 | *
34 | * @return int The result of the comparison.
35 | */
36 | public function __invoke($lhs, $rhs): int;
37 | }
38 |
--------------------------------------------------------------------------------
/src/Comparator/DeepComparator.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = $fallbackComparator;
26 | $this->relaxClassComparisons = $relaxClassComparisons;
27 | }
28 |
29 | /**
30 | * Fetch the fallback comparator.
31 | *
32 | * @return Comparator The comparator to use when the operands are not arrays or objects.
33 | */
34 | public function fallbackComparator(): Comparator
35 | {
36 | return $this->fallbackComparator;
37 | }
38 |
39 | /**
40 | * Compare two values, yielding a result according to the following table:
41 | *
42 | * +--------------------+---------------+
43 | * | Condition | Result |
44 | * +--------------------+---------------+
45 | * | $this == $value | $result === 0 |
46 | * | $this < $value | $result < 0 |
47 | * | $this > $value | $result > 0 |
48 | * +--------------------+---------------+
49 | *
50 | * A deep comparison is performed if both operands are arrays, or both are
51 | * objects; otherwise, the fallback comparator is used.
52 | *
53 | * @param mixed $lhs The first value to compare.
54 | * @param mixed $rhs The second value to compare.
55 | *
56 | * @return int The result of the comparison.
57 | */
58 | public function compare($lhs, $rhs): int
59 | {
60 | $visitationContext = [];
61 |
62 | return $this->compareValue($lhs, $rhs, $visitationContext);
63 | }
64 |
65 | /**
66 | * An alias for compare().
67 | *
68 | * @param mixed $lhs The first value to compare.
69 | * @param mixed $rhs The second value to compare.
70 | *
71 | * @return int The result of the comparison.
72 | */
73 | public function __invoke($lhs, $rhs): int
74 | {
75 | return $this->compare($lhs, $rhs);
76 | }
77 |
78 | /**
79 | * @param mixed $lhs
80 | * @param mixed $rhs
81 | * @param mixed &$visitationContext
82 | *
83 | * @return int The result of the comparison.
84 | */
85 | protected function compareValue($lhs, $rhs, &$visitationContext): int
86 | {
87 | if (is_array($lhs) && is_array($rhs)) {
88 | return $this->compareArray($lhs, $rhs, $visitationContext);
89 | } elseif (is_object($lhs) && is_object($rhs)) {
90 | return $this->compareObject($lhs, $rhs, $visitationContext);
91 | }
92 |
93 | return $this->fallbackComparator()->compare($lhs, $rhs);
94 | }
95 |
96 | /**
97 | * @param array $lhs
98 | * @param array $rhs
99 | * @param mixed &$visitationContext
100 | *
101 | * @return int The result of the comparison.
102 | */
103 | protected function compareArray(array $lhs, array $rhs, &$visitationContext): int
104 | {
105 | reset($lhs);
106 | reset($rhs);
107 |
108 | while (true) {
109 | $lk = key($lhs);
110 | $rk = key($rhs);
111 |
112 | if ($lk === null && $rk === null) {
113 | break;
114 | } elseif ($lk === null) {
115 | return -1;
116 | } elseif ($rk === null) {
117 | return +1;
118 | }
119 |
120 | $cmp = $this->compareValue($lk, $rk, $visitationContext);
121 | if ($cmp !== 0) {
122 | return $cmp;
123 | }
124 |
125 | $lv = current($lhs);
126 | $rv = current($rhs);
127 |
128 | $cmp = $this->compareValue($lv, $rv, $visitationContext);
129 | if ($cmp !== 0) {
130 | return $cmp;
131 | }
132 |
133 | next($lhs);
134 | next($rhs);
135 | }
136 |
137 | return 0;
138 | }
139 |
140 | /**
141 | * @param object $lhs
142 | * @param object $rhs
143 | * @param mixed &$visitationContext
144 | *
145 | * @return int The result of the comparison.
146 | */
147 | protected function compareObject($lhs, $rhs, &$visitationContext): int
148 | {
149 | if ($lhs === $rhs) {
150 | return 0;
151 | } elseif ($this->isNestedComparison($lhs, $rhs, $visitationContext)) {
152 | return strcmp(
153 | spl_object_hash($lhs),
154 | spl_object_hash($rhs)
155 | );
156 | } elseif (!$this->relaxClassComparisons) {
157 | $diff = strcmp(get_class($lhs), get_class($rhs));
158 | if ($diff !== 0) {
159 | return $diff;
160 | }
161 | }
162 |
163 | return $this->compareArray(
164 | $this->objectProperties($lhs, $visitationContext),
165 | $this->objectProperties($rhs, $visitationContext),
166 | $visitationContext
167 | );
168 | }
169 |
170 | /**
171 | * @param object $object
172 | * @param mixed &$visitationContext
173 | *
174 | * @return array
175 | */
176 | protected function objectProperties($object, &$visitationContext): array
177 | {
178 | $properties = [];
179 | $reflector = new ReflectionObject($object);
180 |
181 | while ($reflector) {
182 | foreach ($reflector->getProperties() as $property) {
183 | if ($property->isStatic()) {
184 | continue;
185 | }
186 |
187 | $key = sprintf(
188 | '%s::%s',
189 | $property->getDeclaringClass()->getName(),
190 | $property->getName()
191 | );
192 |
193 | $property->setAccessible(true);
194 | $properties[$key] = $property->getValue($object);
195 | }
196 |
197 | $reflector = $reflector->getParentClass();
198 | }
199 |
200 | return $properties;
201 | }
202 |
203 | /**
204 | * @param mixed $lhs
205 | * @param mixed $rhs
206 | * @param mixed &$visitationContext
207 | *
208 | * @return bool
209 | */
210 | protected function isNestedComparison($lhs, $rhs, &$visitationContext): bool
211 | {
212 | $key = spl_object_hash($lhs) . ':' . spl_object_hash($rhs);
213 |
214 | if (array_key_exists($key, $visitationContext)) {
215 | return true;
216 | }
217 |
218 | $visitationContext[$key] = true;
219 |
220 | return false;
221 | }
222 |
223 | private $fallbackComparator;
224 | private $relaxClassComparisons;
225 | }
226 |
--------------------------------------------------------------------------------
/src/Comparator/ObjectIdentityComparator.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = $fallbackComparator;
16 | }
17 |
18 | /**
19 | * Fetch the fallback comparator.
20 | *
21 | * @return Comparator The comparator to use for non-objects.
22 | */
23 | public function fallbackComparator(): Comparator
24 | {
25 | return $this->fallbackComparator;
26 | }
27 |
28 | /**
29 | * Compare two values, yielding a result according to the following table:
30 | *
31 | * +--------------------+---------------+
32 | * | Condition | Result |
33 | * +--------------------+---------------+
34 | * | $this == $value | $result === 0 |
35 | * | $this < $value | $result < 0 |
36 | * | $this > $value | $result > 0 |
37 | * +--------------------+---------------+
38 | *
39 | * If either of the operands is not an object the fallback comparator is
40 | * used.
41 | *
42 | * @param mixed $lhs The first value to compare.
43 | * @param mixed $rhs The second value to compare.
44 | *
45 | * @return int The result of the comparison.
46 | */
47 | public function compare($lhs, $rhs): int
48 | {
49 | if (!is_object($lhs) || !is_object($rhs)) {
50 | return $this->fallbackComparator()->compare($lhs, $rhs);
51 | }
52 |
53 | return strcmp(
54 | spl_object_hash($lhs),
55 | spl_object_hash($rhs)
56 | );
57 |
58 | }
59 |
60 | /**
61 | * An alias for compare().
62 | *
63 | * @param mixed $lhs The first value to compare.
64 | * @param mixed $rhs The second value to compare.
65 | *
66 | * @return int The result of the comparison.
67 | */
68 | public function __invoke($lhs, $rhs): int
69 | {
70 | return $this->compare($lhs, $rhs);
71 | }
72 |
73 | private $fallbackComparator;
74 | }
75 |
--------------------------------------------------------------------------------
/src/Comparator/ParityComparator.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = $fallbackComparator;
23 | $this->compareImplementationClasses = [];
24 | }
25 |
26 | /**
27 | * Fetch the fallback comparator.
28 | *
29 | * @return Comparator The comparator to use when the operands do not provide their own comparison algorithm.
30 | */
31 | public function fallbackComparator(): Comparator
32 | {
33 | return $this->fallbackComparator;
34 | }
35 |
36 | /**
37 | * Compare two values, yielding a result according to the following table:
38 | *
39 | * +--------------------+---------------+
40 | * | Condition | Result |
41 | * +--------------------+---------------+
42 | * | $this == $value | $result === 0 |
43 | * | $this < $value | $result < 0 |
44 | * | $this > $value | $result > 0 |
45 | * +--------------------+---------------+
46 | *
47 | * If either of the operands implements one of the Parity comparator
48 | * interfaces and is able to perform the comparison to the other operand
49 | * its compare() method is used to perform the comparison. If neither
50 | * operand provides a suitable implementation, the fallback comparator is
51 | * used.
52 | *
53 | * @param mixed $lhs The first value to compare.
54 | * @param mixed $rhs The second value to compare.
55 | *
56 | * @return int The result of the comparison.
57 | */
58 | public function compare($lhs, $rhs): int
59 | {
60 | if ($this->canCompare($lhs, $rhs)) {
61 | return $lhs->compare($rhs);
62 | } elseif ($this->canCompare($rhs, $lhs)) {
63 | return -$rhs->compare($lhs);
64 | }
65 |
66 | return $this->fallbackComparator()->compare($lhs, $rhs);
67 | }
68 |
69 | /**
70 | * An alias for compare().
71 | *
72 | * @param mixed $lhs The first value to compare.
73 | * @param mixed $rhs The second value to compare.
74 | *
75 | * @return int The result of the comparison.
76 | */
77 | public function __invoke($lhs, $rhs): int
78 | {
79 | return $this->compare($lhs, $rhs);
80 | }
81 |
82 | /**
83 | * Check if one value can be compared to another.
84 | *
85 | * @param mixed $lhs The first value to compare.
86 | * @param mixed $rhs The second value to compare.
87 | *
88 | * @return bool
89 | */
90 | protected function canCompare($lhs, $rhs): bool
91 | {
92 | if ($lhs instanceof AnyComparable) {
93 | return true;
94 | } elseif ($lhs instanceof RestrictedComparable && $lhs->canCompare($rhs)) {
95 | return true;
96 | } elseif ($lhs instanceof SelfComparable) {
97 | return is_object($rhs)
98 | && get_class($lhs) === get_class($rhs);
99 | } elseif ($lhs instanceof SubClassComparable) {
100 | $className = $this->compareImplementationClass($lhs);
101 |
102 | return $rhs instanceof $className;
103 | }
104 |
105 | return false;
106 | }
107 |
108 | /**
109 | * @param mixed $value
110 | *
111 | * @return string
112 | */
113 | protected function compareImplementationClass($value): string
114 | {
115 | $className = get_class($value);
116 |
117 | if (array_key_exists($className, $this->compareImplementationClasses)) {
118 | return $this->compareImplementationClasses[$className];
119 | }
120 |
121 | $reflector = new ReflectionMethod($value, 'compare');
122 | $declaringClassName = $reflector->getDeclaringClass()->getName();
123 | $this->compareImplementationClasses[$className] = $declaringClassName;
124 |
125 | return $declaringClassName;
126 | }
127 |
128 | private $fallbackComparator;
129 | private $compareImplementationClasses;
130 | }
131 |
--------------------------------------------------------------------------------
/src/Comparator/PhpComparator.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
19 | * +--------------------+---------------+
20 | *
21 | * @param mixed $lhs The first value to compare.
22 | * @param mixed $rhs The second value to compare.
23 | *
24 | * @return int The result of the comparison.
25 | */
26 | public function compare($lhs, $rhs): int
27 | {
28 | if ($lhs < $rhs) {
29 | return -1;
30 | } elseif ($rhs < $lhs) {
31 | return +1;
32 | }
33 |
34 | return 0;
35 | }
36 |
37 | /**
38 | * An alias for compare().
39 | *
40 | * @param mixed $lhs The first value to compare.
41 | * @param mixed $rhs The second value to compare.
42 | *
43 | * @return int The result of the comparison.
44 | */
45 | public function __invoke($lhs, $rhs): int
46 | {
47 | return $this->compare($lhs, $rhs);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Comparator/StrictPhpComparator.php:
--------------------------------------------------------------------------------
1 | relaxNumericComparisons = $relaxNumericComparisons;
21 | }
22 |
23 | /**
24 | * Compare two values, yielding a result according to the following table:
25 | *
26 | * +--------------------+---------------+
27 | * | Condition | Result |
28 | * +--------------------+---------------+
29 | * | $this == $value | $result === 0 |
30 | * | $this < $value | $result < 0 |
31 | * | $this > $value | $result > 0 |
32 | * +--------------------+---------------+
33 | *
34 | * @param mixed $lhs The first value to compare.
35 | * @param mixed $rhs The second value to compare.
36 | *
37 | * @return int The result of the comparison.
38 | */
39 | public function compare($lhs, $rhs): int
40 | {
41 | $lhsType = $this->transformTypeName($lhs);
42 | $rhsType = $this->transformTypeName($rhs);
43 | $cmp = strcmp($lhsType, $rhsType);
44 |
45 | if ($cmp !== 0) {
46 | return $cmp;
47 | } elseif ($lhs < $rhs) {
48 | return -1;
49 | } elseif ($rhs < $lhs) {
50 | return +1;
51 | }
52 |
53 | return 0;
54 | }
55 |
56 | /**
57 | * An alias for compare().
58 | *
59 | * @param mixed $lhs The first value to compare.
60 | * @param mixed $rhs The second value to compare.
61 | *
62 | * @return int The result of the comparison.
63 | */
64 | public function __invoke($lhs, $rhs): int
65 | {
66 | return $this->compare($lhs, $rhs);
67 | }
68 |
69 | /**
70 | * @param mixed $value
71 | *
72 | * @return string The effective type name to use when comparing types.
73 | */
74 | private function transformTypeName($value): string
75 | {
76 | if (is_object($value)) {
77 | return 'object:' . get_class($value);
78 | } elseif (is_integer($value) && $this->relaxNumericComparisons) {
79 | return 'double';
80 | }
81 |
82 | return gettype($value);
83 | }
84 |
85 | private $relaxNumericComparisons;
86 | }
87 |
--------------------------------------------------------------------------------
/src/Exception/NotComparableException.php:
--------------------------------------------------------------------------------
1 | $value.
35 | */
36 | public function isGreaterThan($value): bool;
37 |
38 | /**
39 | * @param mixed $value The value to compare.
40 | *
41 | * @return bool True if $this <= $value.
42 | */
43 | public function isLessThanOrEqualTo($value): bool;
44 |
45 | /**
46 | * @param mixed $value The value to compare.
47 | *
48 | * @return bool True if $this >= $value.
49 | */
50 | public function isGreaterThanOrEqualTo($value): bool;
51 | }
52 |
--------------------------------------------------------------------------------
/src/ExtendedComparableTrait.php:
--------------------------------------------------------------------------------
1 | compare($value) === 0;
18 | }
19 |
20 | /**
21 | * @param mixed $value The value to compare.
22 | *
23 | * @return bool True if $this != $value.
24 | */
25 | public function isNotEqualTo($value): bool
26 | {
27 | return $this->compare($value) !== 0;
28 | }
29 |
30 | /**
31 | * @param mixed $value The value to compare.
32 | *
33 | * @return bool True if $this < $value.
34 | */
35 | public function isLessThan($value): bool
36 | {
37 | return $this->compare($value) < 0;
38 | }
39 |
40 | /**
41 | * @param mixed $value The value to compare.
42 | *
43 | * @return bool True if $this > $value.
44 | */
45 | public function isGreaterThan($value): bool
46 | {
47 | return $this->compare($value) > 0;
48 | }
49 |
50 | /**
51 | * @param mixed $value The value to compare.
52 | *
53 | * @return bool True if $this <= $value.
54 | */
55 | public function isLessThanOrEqualTo($value): bool
56 | {
57 | return $this->compare($value) <= 0;
58 | }
59 |
60 | /**
61 | * @param mixed $value The value to compare.
62 | *
63 | * @return bool True if $this >= $value.
64 | */
65 | public function isGreaterThanOrEqualTo($value): bool
66 | {
67 | return $this->compare($value) >= 0;
68 | }
69 |
70 | /**
71 | * @param mixed $value The value to compare.
72 | *
73 | * @return int
74 | */
75 | abstract protected function compare($value): int;
76 | }
77 |
--------------------------------------------------------------------------------
/src/Parity.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
21 | * +--------------------+---------------+
22 | *
23 | * @param mixed $lhs The first value to compare.
24 | * @param mixed $rhs The second value to compare.
25 | *
26 | * @return int The result of the comparison.
27 | */
28 | public static function compare($lhs, $rhs): int
29 | {
30 | return self::comparator()->compare($lhs, $rhs);
31 | }
32 |
33 | /**
34 | * @param mixed $lhs The first value to compare.
35 | * @param mixed $rhs The second value to compare.
36 | *
37 | * @return bool True if $lhs == $rhs.
38 | */
39 | public static function isEqualTo($lhs, $rhs): bool
40 | {
41 | return static::compare($lhs, $rhs) === 0;
42 | }
43 |
44 | /**
45 | * @param mixed $lhs The first value to compare.
46 | * @param mixed $rhs The second value to compare.
47 | *
48 | * @return bool True if $lhs != $rhs.
49 | */
50 | public static function isNotEqualTo($lhs, $rhs): bool
51 | {
52 | return static::compare($lhs, $rhs) !== 0;
53 | }
54 |
55 | /**
56 | * @param mixed $lhs The first value to compare.
57 | * @param mixed $rhs The second value to compare.
58 | *
59 | * @return bool True if $lhs < $rhs.
60 | */
61 | public static function isLessThan($lhs, $rhs): bool
62 | {
63 | return static::compare($lhs, $rhs) < 0;
64 | }
65 |
66 | /**
67 | * @param mixed $lhs The first value to compare.
68 | * @param mixed $rhs The second value to compare.
69 | *
70 | * @return bool True if $lhs > $rhs.
71 | */
72 | public static function isGreaterThan($lhs, $rhs): bool
73 | {
74 | return static::compare($lhs, $rhs) > 0;
75 | }
76 |
77 | /**
78 | * @param mixed $lhs The first value to compare.
79 | * @param mixed $rhs The second value to compare.
80 | *
81 | * @return bool True if $lhs <= $rhs.
82 | */
83 | public static function isLessThanOrEqualTo($lhs, $rhs): bool
84 | {
85 | return static::compare($lhs, $rhs) <= 0;
86 | }
87 |
88 | /**
89 | * @param mixed $lhs The first value to compare.
90 | * @param mixed $rhs The second value to compare.
91 | *
92 | * @return bool True if $lhs >= $rhs.
93 | */
94 | public static function isGreaterThanOrEqualTo($lhs, $rhs): bool
95 | {
96 | return static::compare($lhs, $rhs) >= 0;
97 | }
98 |
99 | /**
100 | * @param mixed $lhs The first value to compare.
101 | * @param mixed $rhs,... The second (and more) value(s) to compare.
102 | *
103 | * @return mixed
104 | */
105 | public static function min($lhs, $rhs)
106 | {
107 | return self::minSequence(func_get_args());
108 | }
109 |
110 | /**
111 | * @param mixed $lhs The first value to compare.
112 | * @param mixed $rhs,... The second (and more) value(s) to compare.
113 | *
114 | * @return mixed
115 | */
116 | public static function max($lhs, $rhs)
117 | {
118 | return self::maxSequence(func_get_args());
119 | }
120 |
121 | /**
122 | * @param iterable $sequence The sequence to find the minimum value in.
123 | * @param mixed $default The default miniumum value.
124 | *
125 | * @return mixed The minimum value in the sequence.
126 | */
127 | public static function minSequence(iterable $sequence, $default = null)
128 | {
129 | $minAssigned = false;
130 | $min = null;
131 |
132 | foreach ($sequence as $value) {
133 | if (!$minAssigned) {
134 | $minAssigned = true;
135 | $min = $value;
136 | } elseif (static::isLessThan($value, $min)) {
137 | $min = $value;
138 | }
139 | }
140 |
141 | if (!$minAssigned) {
142 | return $default;
143 | }
144 |
145 | return $min;
146 | }
147 |
148 | /**
149 | * @param iterable $sequence The sequence to find the maximum value in.
150 | * @param mixed $default The default maxiumum value.
151 | *
152 | * @return mixed The maximum value in the sequence.
153 | */
154 | public static function maxSequence(iterable $sequence, $default = null)
155 | {
156 | $maxAssigned = false;
157 | $max = null;
158 |
159 | foreach ($sequence as $value) {
160 | if (!$maxAssigned) {
161 | $maxAssigned = true;
162 | $max = $value;
163 | } elseif (static::isGreaterThan($value, $max)) {
164 | $max = $value;
165 | }
166 | }
167 |
168 | if (!$maxAssigned) {
169 | return $default;
170 | }
171 |
172 | return $max;
173 | }
174 |
175 | /**
176 | * Get the internal Parity comparator.
177 | *
178 | * The comparator returned by this method in such as way as to enforce the
179 | * documented rules of Parity's comparison engine.
180 | *
181 | * @return Comparator
182 | */
183 | public static function comparator(): Comparator
184 | {
185 | if (null === self::$comparator) {
186 | self::$comparator = new ParityComparator(
187 | new DeepComparator(
188 | new StrictPhpComparator()
189 | )
190 | );
191 | }
192 |
193 | return self::$comparator;
194 | }
195 |
196 | private static $comparator;
197 | }
198 |
--------------------------------------------------------------------------------
/src/RestrictedComparable.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
20 | * +--------------------+---------------+
21 | *
22 | * @param mixed $value The value to compare.
23 | *
24 | * @return int The result of the comparison.
25 | * @throws Exception\NotComparableException Indicates that the implementation does not know how to compare $this to $value.
26 | */
27 | public function compare($value): int;
28 |
29 | /**
30 | * Check if $this is able to be compared to another value.
31 | *
32 | * A return value of false indicates that calling $this->compare($value)
33 | * will throw an exception.
34 | *
35 | * @param mixed $value The value to compare.
36 | *
37 | * @return bool True if $this can be compared to $value.
38 | */
39 | public function canCompare($value): bool;
40 | }
41 |
--------------------------------------------------------------------------------
/src/SelfComparable.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
20 | * +--------------------+---------------+
21 | *
22 | * @param object $value The object to compare.
23 | *
24 | * @return int The result of the comparison.
25 | * @throws Exception\NotComparableException if $value is not the same type as $this.
26 | */
27 | public function compare($value): int;
28 | }
29 |
--------------------------------------------------------------------------------
/src/SubClassComparable.php:
--------------------------------------------------------------------------------
1 | $value | $result > 0 |
21 | * +--------------------+---------------+
22 | *
23 | * @param object $value The object to compare.
24 | *
25 | * @return int The result of the comparison.
26 | * @throws Exception\NotComparableException if $value is not the same type as $this.
27 | */
28 | public function compare($value): int;
29 | }
30 |
--------------------------------------------------------------------------------
/test/src/ChildObject.php:
--------------------------------------------------------------------------------
1 | foo = $foo;
10 | $this->bar = $bar;
11 | }
12 |
13 | public static $staticProperty = 'staticPropertyValue';
14 | private $foo;
15 | private $bar;
16 | }
17 |
--------------------------------------------------------------------------------
/test/suite/Comparator/DeepComparatorTest.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = Phony::partialMock(PhpComparator::class);
17 | $this->comparator = new DeepComparator($this->fallbackComparator->get());
18 | }
19 |
20 | public function testInvoke()
21 | {
22 | $this->assertSame(
23 | 0,
24 | call_user_func(
25 | $this->comparator,
26 | [1, 2, 3],
27 | [1, 2, 3]
28 | )
29 | );
30 | }
31 |
32 | public function testCompareWithObjectReferences()
33 | {
34 | $value = (object) ['foo' => 'bar'];
35 |
36 | $result = $this->comparator->compare($value, $value);
37 |
38 | // Even though there are strings in the object, the fallback comparator
39 | // should never be used because we are comparing references to the same
40 | // object.
41 | $this->fallbackComparator->noInteraction();
42 |
43 | $this->assertSame(0, $result);
44 | }
45 |
46 | public function testCompareWithEmptyArrays()
47 | {
48 | $result = $this->comparator->compare([], []);
49 |
50 | $this->fallbackComparator->noInteraction();
51 |
52 | $this->assertSame(0, $result);
53 | }
54 |
55 | public function testCompareWithArrays()
56 | {
57 | $this->assertSame(
58 | 0,
59 | $this->comparator->compare(
60 | [1, 2, 3],
61 | [1, 2, 3]
62 | )
63 | );
64 | }
65 |
66 | public function testCompareWithAssociativeArrays()
67 | {
68 | $this->assertSame(
69 | 0,
70 | $this->comparator->compare(
71 | ['a' => 1, 'b' => 2, 'c' => 3],
72 | ['a' => 1, 'b' => 2, 'c' => 3]
73 | )
74 | );
75 | }
76 |
77 | public function testCompareWithArraysThatDifferBySize()
78 | {
79 | $this->assertLessThan(
80 | 0,
81 | $this->comparator->compare(
82 | [1, 2],
83 | [1, 2, 3]
84 | )
85 | );
86 |
87 | $this->assertGreaterThan(
88 | 0,
89 | $this->comparator->compare(
90 | [1, 2, 3],
91 | [1, 2]
92 | )
93 | );
94 | }
95 |
96 | public function testCompareWithArraysThatDifferBySizeAndContent()
97 | {
98 | $this->assertLessThan(
99 | 0,
100 | $this->comparator->compare(
101 | [1, 2, 3],
102 | [1, 3]
103 | )
104 | );
105 |
106 | $this->assertGreaterThan(
107 | 0,
108 | $this->comparator->compare(
109 | [1, 3],
110 | [1, 2, 3]
111 | )
112 | );
113 | }
114 |
115 | public function testCompareWithArraysThatDifferByKeys()
116 | {
117 | $this->assertLessThan(
118 | 0,
119 | $this->comparator->compare(
120 | ['a' => 1],
121 | ['b' => 1]
122 | )
123 | );
124 |
125 | $this->assertGreaterThan(
126 | 0,
127 | $this->comparator->compare(
128 | ['b' => 1],
129 | ['a' => 1]
130 | )
131 | );
132 | }
133 |
134 | public function testCompareWithObjects()
135 | {
136 | $this->assertSame(
137 | 0,
138 | $this->comparator->compare(
139 | (object) ['a' => 1, 'b' => 2, 'c' => 3],
140 | (object) ['a' => 1, 'b' => 2, 'c' => 3]
141 | )
142 | );
143 | }
144 |
145 | public function testCompareWithObjectsThatDifferBySize()
146 | {
147 | $this->assertLessThan(
148 | 0,
149 | $this->comparator->compare(
150 | (object) ['a' => 1, 'b' => 2],
151 | (object) ['a' => 1, 'b' => 2, 'c' => 3]
152 | )
153 | );
154 |
155 | $this->assertGreaterThan(
156 | 0,
157 | $this->comparator->compare(
158 | (object) ['a' => 1, 'b' => 2, 'c' => 3],
159 | (object) ['a' => 1, 'b' => 2]
160 | )
161 | );
162 | }
163 |
164 | public function testCompareWithObjectsThatDifferBySizeAndContent()
165 | {
166 | $this->assertLessThan(
167 | 0,
168 | $this->comparator->compare(
169 | (object) ['a' => 1, 'b' => 2, 'c' => 3],
170 | (object) ['a' => 1, 'b' => 3]
171 | )
172 | );
173 |
174 | $this->assertGreaterThan(
175 | 0,
176 | $this->comparator->compare(
177 | (object) ['a' => 1, 'b' => 3],
178 | (object) ['a' => 1, 'b' => 2, 'c' => 3]
179 | )
180 | );
181 | }
182 |
183 | public function testCompareWithObjectsThatDifferClassName()
184 | {
185 | $this->assertLessThan(
186 | 0,
187 | $this->comparator->compare(
188 | new DateTime(),
189 | new stdClass()
190 | )
191 | );
192 |
193 | $this->assertGreaterThan(
194 | 0,
195 | $this->comparator->compare(
196 | new stdClass(),
197 | new DateTime()
198 | )
199 | );
200 | }
201 |
202 | public function testCompareWithObjectsWithRelaxedClassComparisons()
203 | {
204 | $this->comparator = new DeepComparator($this->fallbackComparator->get(), true);
205 |
206 | $this->assertSame(
207 | 0,
208 | $this->comparator->compare(
209 | new ParentObject(1, 2),
210 | new ChildObject(1, 2)
211 | )
212 | );
213 | }
214 |
215 | public function testCompareWithObjectsDifferentInnerClassTypes()
216 | {
217 | $obj1 = new stdClass();
218 | $obj1->foo = new stdClass();
219 |
220 | $obj2 = new stdClass();
221 | $obj2->foo = new ParentObject(0, 0);
222 |
223 | $this->assertSame(0, $this->comparator->compare($obj1, $obj1));
224 | $this->assertSame(0, $this->comparator->compare($obj2, $obj2));
225 |
226 | $this->assertLessThan(0, $this->comparator->compare($obj2, $obj1));
227 | $this->assertGreaterThan(0, $this->comparator->compare($obj1, $obj2));
228 | }
229 |
230 | public function testCompareWithObjectsParentAndDerived()
231 | {
232 | $obj1 = new ParentObject(0, 0);
233 | $obj2 = new ChildObject(0, 0);
234 |
235 | $this->assertSame(0, $this->comparator->compare($obj1, $obj1));
236 | $this->assertSame(0, $this->comparator->compare($obj2, $obj2));
237 |
238 | $this->assertLessThan(0, $this->comparator->compare($obj2, $obj1));
239 | $this->assertGreaterThan(0, $this->comparator->compare($obj1, $obj2));
240 | }
241 |
242 | public function testCompareWithObjectsHavingSharedInnerObject()
243 | {
244 | $shared = new ParentObject('foo', 'bar');
245 |
246 | $obj1 = new ParentObject(111, $shared);
247 | $obj2 = new ParentObject(222, $shared);
248 |
249 | $obj3 = new ChildObject(333, $shared);
250 | $obj4 = new ChildObject(444, $shared);
251 |
252 | $this->assertSame(0, $this->comparator->compare($obj1, $obj1));
253 | $this->assertSame(0, $this->comparator->compare($obj3, $obj3));
254 |
255 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
256 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
257 |
258 | $this->assertLessThan(0, $this->comparator->compare($obj3, $obj4));
259 | $this->assertGreaterThan(0, $this->comparator->compare($obj4, $obj3));
260 | }
261 |
262 | public function testCompareWithSimpleRecursion()
263 | {
264 | $obj1 = new stdClass();
265 | $obj1->foo = $obj1;
266 | $obj1->bar = 1;
267 |
268 | $obj2 = new stdClass();
269 | $obj2->foo = $obj2;
270 | $obj2->bar = 2;
271 |
272 | // The first property compared is infinitely recusive, so just the hash will be used.
273 | // Since the hash's wont match the 'bar' property will not be compared.
274 | if (spl_object_hash($obj1) < spl_object_hash($obj2)) {
275 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
276 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
277 | } else {
278 | $this->assertLessThan(0, $this->comparator->compare($obj2, $obj1));
279 | $this->assertGreaterThan(0, $this->comparator->compare($obj1, $obj2));
280 | }
281 | }
282 |
283 | public function testCompareWithSimpleObjectsDoubleRecursion()
284 | {
285 | $obj1 = new stdClass();
286 | $obj1->recurse = new stdClass();
287 | $obj1->recurse->recurse = $obj1;
288 | $obj1->value = 1;
289 |
290 | $obj2 = new stdClass();
291 | $obj2->recurse = new stdClass();
292 | $obj2->recurse->recurse = $obj2;
293 | $obj2->value = 2;
294 |
295 | // The first property compared is infinitely recusive, so just the hash will be used.
296 | // Since the hash's wont match the 'value' property will not be compared.
297 | if (spl_object_hash($obj1) < spl_object_hash($obj2)) {
298 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
299 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
300 | } else {
301 | $this->assertLessThan(0, $this->comparator->compare($obj2, $obj1));
302 | $this->assertGreaterThan(0, $this->comparator->compare($obj1, $obj2));
303 | }
304 | }
305 |
306 | public function testCompareWithSimpleObjectsBothHavingObject1AsFirstProperty()
307 | {
308 | $obj1 = new stdClass();
309 | $obj1->foo = $obj1;
310 | $obj1->bar = 1;
311 |
312 | $obj2 = new stdClass();
313 | $obj2->foo = $obj1;
314 | $obj2->bar = 2;
315 |
316 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
317 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
318 | }
319 |
320 | public function testCompareWithObjectCycle()
321 | {
322 | $obj1 = new stdClass();
323 | $obj1->foo = new ParentObject('foo1', $obj1);
324 |
325 | $obj2 = new stdClass();
326 | $obj2->foo = new ParentObject('foo2', $obj2);
327 |
328 | $obj3 = new stdClass();
329 | $obj3->foo = new ChildObject('bar3', $obj1);
330 |
331 | $obj4 = new stdClass();
332 | $obj4->foo = new ChildObject('bar4', $obj2);
333 |
334 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
335 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
336 |
337 | $this->assertLessThan(0, $this->comparator->compare($obj3, $obj4));
338 | $this->assertGreaterThan(0, $this->comparator->compare($obj4, $obj3));
339 | }
340 |
341 | public function testCompareWithObjectsHavingInternalArraysAndObjects()
342 | {
343 | $shared = new ChildObject('foo', 'bar');
344 |
345 | $obj1 = new ParentObject(['a', 'b'], [$shared, 'foo']);
346 | $obj2 = new ParentObject(['a', 'b'], [$shared, 'foo']);
347 | $obj3 = new ParentObject(['x', 'y'], [$shared, 'foo']);
348 |
349 | $this->assertSame(0, $this->comparator->compare($obj1, $obj1));
350 | $this->assertSame(0, $this->comparator->compare($obj1, $obj2));
351 | $this->assertSame(0, $this->comparator->compare($obj3, $obj3));
352 |
353 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj3));
354 | $this->assertGreaterThan(0, $this->comparator->compare($obj3, $obj1));
355 | }
356 | }
357 |
--------------------------------------------------------------------------------
/test/suite/Comparator/ObjectIdentityComparatorTest.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = Phony::mock(Comparator::class);
14 | $this->fallbackComparator->compare->returns(-1);
15 |
16 | $this->comparator = new ObjectIdentityComparator($this->fallbackComparator->get());
17 | }
18 |
19 | public function testInvoke()
20 | {
21 | $this->assertSame(
22 | -1,
23 | call_user_func(
24 | $this->comparator,
25 | [1, 2, 3],
26 | [1, 2, 3]
27 | )
28 | );
29 | }
30 |
31 | public function testCompare()
32 | {
33 | $obj1 = new stdClass();
34 | $obj2 = new stdClass();
35 |
36 | $this->assertSame(0, $this->comparator->compare($obj1, $obj1));
37 | $this->assertSame(0, $this->comparator->compare($obj2, $obj2));
38 |
39 | // The first property compared is infinitely recusive, so just the hash will be used.
40 | // Since the hash's wont match the 'bar' property will not be compared.
41 | if (spl_object_hash($obj1) < spl_object_hash($obj2)) {
42 | $this->assertLessThan(0, $this->comparator->compare($obj1, $obj2));
43 | $this->assertGreaterThan(0, $this->comparator->compare($obj2, $obj1));
44 | } else {
45 | $this->assertLessThan(0, $this->comparator->compare($obj2, $obj1));
46 | $this->assertGreaterThan(0, $this->comparator->compare($obj1, $obj2));
47 | }
48 | }
49 |
50 | public function testCompareWithFallback()
51 | {
52 | $lhs = new stdClass();
53 |
54 | $result = $this->comparator->compare($lhs, 20);
55 |
56 | $this->fallbackComparator->compare->calledWith($lhs, 20);
57 |
58 | $this->assertSame($result, -1);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/test/suite/Comparator/ParityComparatorTest.php:
--------------------------------------------------------------------------------
1 | fallbackComparator = Phony::mock(Comparator::class);
19 | $this->fallbackComparator->compare->returns(-1);
20 |
21 | $this->comparator = new ParityComparator($this->fallbackComparator->get());
22 | }
23 |
24 | public function testInvoke()
25 | {
26 | $this->assertSame(
27 | -1,
28 | call_user_func(
29 | $this->comparator,
30 | [1, 2, 3],
31 | [1, 2, 3]
32 | )
33 | );
34 | }
35 |
36 | public function testCompareInversion()
37 | {
38 | $lhs = Phony::mock(AnyComparable::class);
39 | $lhs->compare->returns(-1);
40 |
41 | $this->assertSame(-1, $this->comparator->compare($lhs->get(), 10));
42 | $this->assertSame(+1, $this->comparator->compare(10, $lhs->get()));
43 | }
44 |
45 | public function testCompareWithFallback()
46 | {
47 | $result = $this->comparator->compare(10, 20);
48 |
49 | $this->fallbackComparator->compare->calledWith(10, 20);
50 |
51 | $this->assertSame($result, -1);
52 | }
53 |
54 | public function testCompareWithAnyComparable()
55 | {
56 | $comparable = Phony::mock(AnyComparable::class);
57 | $comparable->compare->returns(-10);
58 |
59 | $result = $this->comparator->compare($comparable->get(), 10);
60 |
61 | $comparable->compare->calledWith(10);
62 | $this->fallbackComparator->noInteraction();
63 |
64 | $this->assertSame($result, -10);
65 | }
66 |
67 | public function testCompareWithRestrictedComparable()
68 | {
69 | $comparable = Phony::mock(RestrictedComparable::class);
70 | $comparable->compare->returns(-10);
71 | $comparable->canCompare->returns(true);
72 |
73 | $result = $this->comparator->compare($comparable->get(), 10);
74 |
75 | Phony::inOrder(
76 | $comparable->canCompare->calledWith(10),
77 | $comparable->compare->calledWith(10)
78 | );
79 |
80 | $this->fallbackComparator->noInteraction();
81 |
82 | $this->assertSame($result, -10);
83 | }
84 |
85 | public function testCompareWithRestrictedComparableAndUnsupportedOperand()
86 | {
87 | $comparable = Phony::mock(RestrictedComparable::class);
88 | $comparable->compare->returns(-10);
89 | $comparable->canCompare->returns(false);
90 |
91 | $result = $this->comparator->compare($comparable->get(), 10);
92 |
93 | $comparable->canCompare->calledWith(10);
94 | $comparable->compare->never()->called();
95 | $this->fallbackComparator->compare->calledWith($comparable, 10);
96 |
97 | $this->assertSame($result, -1);
98 | }
99 |
100 | public function testCompareWithSelfComparable()
101 | {
102 | $lhsComparable = Phony::mock(SelfComparable::class);
103 | $lhsComparable->compare->returns(-10);
104 |
105 | $rhsComparable = Phony::mock(SelfComparable::class);
106 |
107 | $result = $this->comparator->compare($lhsComparable->get(), $rhsComparable->get());
108 |
109 | $lhsComparable->compare->calledWith($rhsComparable);
110 | $this->fallbackComparator->noInteraction();
111 |
112 | $this->assertSame($result, -10);
113 | }
114 |
115 | public function testCompareWithSelfComparableAndSubClass()
116 | {
117 | $lhsComparable = new SelfComparableImpl();
118 | $rhsComparable = new SelfComparableSubClass();
119 |
120 | $result = $this->comparator->compare($lhsComparable, $rhsComparable);
121 |
122 | $this->fallbackComparator->compare->calledWith($lhsComparable, $rhsComparable);
123 |
124 | $this->assertSame($result, -1);
125 | }
126 |
127 | public function testCompareWithSelfComparableAndNonObject()
128 | {
129 | $comparable = Phony::mock(SelfComparable::class);
130 |
131 | $result = $this->comparator->compare($comparable->get(), 10);
132 |
133 | $comparable->compare->never()->called();
134 | $this->fallbackComparator->compare->calledWith($comparable, 10);
135 |
136 | $this->assertSame($result, -1);
137 | }
138 |
139 | public function testCompareWithSelfComparableAndUnrelatedType()
140 | {
141 | $comparable = Phony::mock(SelfComparable::class);
142 |
143 | $result = $this->comparator->compare($comparable->get(), new stdClass());
144 |
145 | $comparable->compare->never()->called();
146 | $this->fallbackComparator->compare->calledWith($comparable, new stdClass());
147 |
148 | $this->assertSame($result, -1);
149 | }
150 |
151 | public function testCompareWithSubClassComparable()
152 | {
153 | $lhsComparable = Phony::mock(SubClassComparable::class);
154 | $lhsComparable->compare->returns(-10);
155 |
156 | $rhsComparable = Phony::mock(SubClassComparable::class);
157 |
158 | $result = $this->comparator->compare($lhsComparable->get(), $rhsComparable->get());
159 |
160 | $lhsComparable->compare->calledWith($rhsComparable);
161 | $this->fallbackComparator->noInteraction();
162 |
163 | $this->assertSame($result, -10);
164 | }
165 |
166 | public function testCompareWithSubClassComparableAndSubClass()
167 | {
168 | $lhsComparable = new SubClassComparableImpl();
169 | $rhsComparable = new SubClassComparableSubClass();
170 |
171 | $result = $this->comparator->compare($lhsComparable, $rhsComparable);
172 |
173 | $this->fallbackComparator->noInteraction();
174 |
175 | $this->assertSame($result, -10);
176 | }
177 |
178 | public function testCompareWithSubClassComparableUsesCache()
179 | {
180 | $lhsComparable = Phony::mock(SubClassComparable::class);
181 | $lhsComparable->compare->returns(-10);
182 |
183 | $rhsComparable = Phony::mock(SubClassComparable::class);
184 |
185 | $this->assertSame(-10, $this->comparator->compare($lhsComparable->get(), $rhsComparable->get()));
186 | $this->assertSame(-10, $this->comparator->compare($lhsComparable->get(), $rhsComparable->get()));
187 |
188 | $this->assertSame(
189 | Liberator::liberate($this->comparator)->compareImplementationClasses[get_class($lhsComparable->get())],
190 | get_class($lhsComparable->get())
191 | );
192 | }
193 |
194 | public function testCompareWithSubClassComparableAndNonObject()
195 | {
196 | $comparable = Phony::mock(SubClassComparable::class);
197 |
198 | $result = $this->comparator->compare($comparable->get(), 10);
199 |
200 | $comparable->compare->never()->called();
201 | $this->fallbackComparator->compare->calledWith($comparable, 10);
202 |
203 | $this->assertSame($result, -1);
204 | }
205 |
206 | public function testCompareWithSubClassComparableAndUnrelatedType()
207 | {
208 | $comparable = Phony::mock(SubClassComparable::class);
209 |
210 | $result = $this->comparator->compare($comparable->get(), new stdClass());
211 |
212 | $comparable->compare->never()->called();
213 | $this->fallbackComparator->compare->calledWith($comparable, new stdClass());
214 |
215 | $this->assertSame($result, -1);
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/test/suite/Comparator/PhpComparatorTest.php:
--------------------------------------------------------------------------------
1 | comparator = new PhpComparator();
12 | }
13 |
14 | public function testCompare()
15 | {
16 | $this->assertLessThan(0, $this->comparator->compare(10, 20));
17 | $this->assertGreaterThan(0, $this->comparator->compare(20, 10));
18 | $this->assertSame(0, $this->comparator->compare(10, 10));
19 | }
20 |
21 | public function testInvoke()
22 | {
23 | $this->assertLessThan(0, call_user_func($this->comparator, 10, 20));
24 | $this->assertGreaterThan(0, call_user_func($this->comparator, 20, 10));
25 | $this->assertSame(0, call_user_func($this->comparator, 10, 10));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/suite/Comparator/StrictPhpComparatorTest.php:
--------------------------------------------------------------------------------
1 | comparator = new StrictPhpComparator();
14 | }
15 |
16 | public function testCompare()
17 | {
18 | $this->assertLessThan(0, $this->comparator->compare(10, 20));
19 | $this->assertGreaterThan(0, $this->comparator->compare(20, 10));
20 | $this->assertSame(0, $this->comparator->compare(10, 10));
21 | }
22 |
23 | public function testCompareWithoutRelaxedNumericComparisons()
24 | {
25 | $this->comparator = new StrictPhpComparator(false);
26 |
27 | $this->assertGreaterThan(0, $this->comparator->compare(1, 2.5));
28 | $this->assertLessThan(0, $this->comparator->compare(2.5, 3));
29 | $this->assertGreaterThan(0, $this->comparator->compare(1, 1.0));
30 | }
31 |
32 | public function testCompareRelaxedNumericComparisons()
33 | {
34 | $this->assertLessThan(0, $this->comparator->compare(1, 2.5));
35 | $this->assertLessThan(0, $this->comparator->compare(2.5, 3));
36 | $this->assertSame(0, $this->comparator->compare(1, 1.0));
37 | }
38 |
39 | public function testCompareWithObjects()
40 | {
41 | $this->assertSame(0, $this->comparator->compare(new stdClass(), new stdClass()));
42 | $this->assertGreaterThan(0, $this->comparator->compare(new stdClass(), new DateTime()));
43 | }
44 |
45 | public function testInvoke()
46 | {
47 | $this->assertLessThan(0, call_user_func($this->comparator, 10, 20));
48 | $this->assertGreaterThan(0, call_user_func($this->comparator, 20, 10));
49 | $this->assertSame(0, call_user_func($this->comparator, 10, 10));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/test/suite/Exception/NotComparableExceptionTest.php:
--------------------------------------------------------------------------------
1 | assertSame('Can not compare [1, 2, 3] to [4, 5, 6].', $exception->getMessage());
19 | $this->assertSame($previous, $exception->getPrevious());
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/suite/ExtendedComparableTraitTest.php:
--------------------------------------------------------------------------------
1 | trait = Phony::partialMock(ExtendedComparableTraitUser::class)->get();
13 |
14 | $this->less = -1;
15 | $this->same = 0;
16 | $this->more = 1;
17 | }
18 |
19 | public function testIsEqualTo()
20 | {
21 | $this->assertFalse($this->trait->isEqualTo($this->less));
22 | $this->assertTrue($this->trait->isEqualTo($this->same));
23 | $this->assertFalse($this->trait->isEqualTo($this->more));
24 | }
25 |
26 | public function testIsNotEqualTo()
27 | {
28 | $this->assertTrue($this->trait->isNotEqualTo($this->less));
29 | $this->assertFalse($this->trait->isNotEqualTo($this->same));
30 | $this->assertTrue($this->trait->isNotEqualTo($this->more));
31 | }
32 |
33 | public function testIsLessThan()
34 | {
35 | $this->assertFalse($this->trait->isLessThan($this->less));
36 | $this->assertFalse($this->trait->isLessThan($this->same));
37 | $this->assertTrue($this->trait->isLessThan($this->more));
38 | }
39 |
40 | public function testIsGreaterThan()
41 | {
42 | $this->assertTrue($this->trait->isGreaterThan($this->less));
43 | $this->assertFalse($this->trait->isGreaterThan($this->same));
44 | $this->assertFalse($this->trait->isGreaterThan($this->more));
45 | }
46 |
47 | public function testIsLessThanOrEqualTo()
48 | {
49 | $this->assertFalse($this->trait->isLessThanOrEqualTo($this->less));
50 | $this->assertTrue($this->trait->isLessThanOrEqualTo($this->same));
51 | $this->assertTrue($this->trait->isLessThanOrEqualTo($this->more));
52 | }
53 |
54 | public function testIsGreaterThanOrEqualTo()
55 | {
56 | $this->assertTrue($this->trait->isGreaterThanOrEqualTo($this->less));
57 | $this->assertTrue($this->trait->isGreaterThanOrEqualTo($this->same));
58 | $this->assertFalse($this->trait->isGreaterThanOrEqualTo($this->more));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/test/suite/ParityTest.php:
--------------------------------------------------------------------------------
1 | value = 0;
13 | $this->less = -1;
14 | $this->same = 0;
15 | $this->more = 1;
16 | $this->object = new stdClass();
17 | }
18 |
19 | public function testCompare()
20 | {
21 | $this->assertGreaterThan(0, Parity::compare($this->value, $this->less));
22 | $this->assertSame(0, Parity::compare($this->value, $this->same));
23 | $this->assertLessThan(0, Parity::compare($this->value, $this->more));
24 | }
25 |
26 | public function testIsEqualTo()
27 | {
28 | $this->assertFalse(Parity::isEqualTo($this->value, $this->less));
29 | $this->assertTrue(Parity::isEqualTo($this->value, $this->same));
30 | $this->assertFalse(Parity::isEqualTo($this->value, $this->more));
31 | }
32 |
33 | public function testIsNotEqualTo()
34 | {
35 | $this->assertTrue(Parity::isNotEqualTo($this->value, $this->less));
36 | $this->assertFalse(Parity::isNotEqualTo($this->value, $this->same));
37 | $this->assertTrue(Parity::isNotEqualTo($this->value, $this->more));
38 | }
39 |
40 | public function testIsLessThan()
41 | {
42 | $this->assertFalse(Parity::isLessThan($this->value, $this->less));
43 | $this->assertFalse(Parity::isLessThan($this->value, $this->same));
44 | $this->assertTrue(Parity::isLessThan($this->value, $this->more));
45 | }
46 |
47 | public function testIsGreaterThan()
48 | {
49 | $this->assertTrue(Parity::isGreaterThan($this->value, $this->less));
50 | $this->assertFalse(Parity::isGreaterThan($this->value, $this->same));
51 | $this->assertFalse(Parity::isGreaterThan($this->value, $this->more));
52 | }
53 |
54 | public function testIsLessThanOrEqualTo()
55 | {
56 | $this->assertFalse(Parity::isLessThanOrEqualTo($this->value, $this->less));
57 | $this->assertTrue(Parity::isLessThanOrEqualTo($this->value, $this->same));
58 | $this->assertTrue(Parity::isLessThanOrEqualTo($this->value, $this->more));
59 | }
60 |
61 | public function testIsGreaterThanOrEqualTo()
62 | {
63 | $this->assertTrue(Parity::isGreaterThanOrEqualTo($this->value, $this->less));
64 | $this->assertTrue(Parity::isGreaterThanOrEqualTo($this->value, $this->same));
65 | $this->assertFalse(Parity::isGreaterThanOrEqualTo($this->value, $this->more));
66 | }
67 |
68 | public function testMin()
69 | {
70 | $this->assertSame($this->less, Parity::min($this->value, $this->less));
71 | $this->assertSame($this->less, Parity::min($this->less, $this->value));
72 | $this->assertSame($this->less, Parity::min($this->value, $this->same, $this->less, $this->more));
73 | }
74 |
75 | public function testMax()
76 | {
77 | $this->assertSame($this->more, Parity::max($this->value, $this->more));
78 | $this->assertSame($this->more, Parity::max($this->more, $this->value));
79 | $this->assertSame($this->more, Parity::max($this->value, $this->same, $this->less, $this->more));
80 | }
81 |
82 | /**
83 | * @dataProvider minSequenceData
84 | */
85 | public function testMinSequence($sequence, $default, $expected)
86 | {
87 | $this->assertSame(
88 | $expected,
89 | Parity::minSequence($sequence, $default)
90 | );
91 | }
92 |
93 | public function minSequenceData()
94 | {
95 | $value = 0;
96 | $less = -1;
97 | $same = 0;
98 | $more = 1;
99 | $object = new stdClass();
100 |
101 | $sequenceEmpty = [];
102 |
103 | $sequenceObjectAndNull = [
104 | $object,
105 | null,
106 | ];
107 |
108 | $sequenceNumber = [
109 | $value,
110 | $less,
111 | $same,
112 | $more,
113 | ];
114 |
115 | $sequenceMixed = [
116 | $value,
117 | $less,
118 | $same,
119 | $more,
120 | $object,
121 | true,
122 | null,
123 | ];
124 |
125 | return [
126 | 'Empty sequence, default null' => [$sequenceEmpty, null, null],
127 | 'Empty sequence, default object' => [$sequenceEmpty, $object, $object],
128 | 'Empty sequence, default value' => [$sequenceEmpty, $value, $value],
129 |
130 | 'Object and null sequence, default null' => [$sequenceObjectAndNull, null, null],
131 | 'Object and null sequence, default object' => [$sequenceObjectAndNull, $object, null],
132 | 'Object and null sequence, default value' => [$sequenceObjectAndNull, $value, null],
133 |
134 | 'Number sequence, default null' => [$sequenceNumber, null, $less],
135 | 'Number sequence, default object' => [$sequenceNumber, $object, $less],
136 | 'Number sequence, default value' => [$sequenceNumber, $value, $less],
137 |
138 | 'Mixed sequence, default null' => [$sequenceMixed, null, null],
139 | 'Mixed sequence, default object' => [$sequenceMixed, $object, null],
140 | 'Mixed sequence, default value' => [$sequenceMixed, $value, null],
141 | ];
142 | }
143 |
144 | /**
145 | * @dataProvider maxSequenceData
146 | */
147 | public function testMaxSequence($sequence, $default, $expected)
148 | {
149 | $this->assertSame(
150 | $expected,
151 | Parity::maxSequence($sequence, $default)
152 | );
153 | }
154 |
155 | public function maxSequenceData()
156 | {
157 | $value = 0;
158 | $less = -1;
159 | $same = 0;
160 | $more = 1;
161 | $object = new stdClass();
162 |
163 | $sequenceEmpty = [];
164 |
165 | $sequenceObjectAndNull = [
166 | $object,
167 | null,
168 | ];
169 |
170 | $sequenceNumber = [
171 | $value,
172 | $less,
173 | $same,
174 | $more,
175 | ];
176 |
177 | $sequenceMixed = [
178 | $value,
179 | $less,
180 | $same,
181 | $more,
182 | $object,
183 | true,
184 | null,
185 | ];
186 |
187 | return [
188 | 'Empty sequence, default null' => [$sequenceEmpty, null, null],
189 | 'Empty sequence, default object' => [$sequenceEmpty, $object, $object],
190 | 'Empty sequence, default value' => [$sequenceEmpty, $value, $value],
191 |
192 | 'Object and null sequence, default null' => [$sequenceObjectAndNull, null, $object],
193 | 'Object and null sequence, default object' => [$sequenceObjectAndNull, $object, $object],
194 | 'Object and null sequence, default value' => [$sequenceObjectAndNull, $value, $object],
195 |
196 | 'Number sequence, default null' => [$sequenceNumber, null, $more],
197 | 'Number sequence, default object' => [$sequenceNumber, $object, $more],
198 | 'Number sequence, default value' => [$sequenceNumber, $value, $more],
199 |
200 | 'Mixed sequence, default null' => [$sequenceMixed, null, $object],
201 | 'Mixed sequence, default object' => [$sequenceMixed, $object, $object],
202 | 'Mixed sequence, default value' => [$sequenceMixed, $value, $object],
203 | ];
204 | }
205 |
206 | public function testComparitor()
207 | {
208 | $comparator = Parity::comparator();
209 | $this->assertInstanceOf(__NAMESPACE__ . '\Comparator\ParityComparator', $comparator);
210 |
211 | $comparator = $comparator->fallbackComparator();
212 | $this->assertInstanceOf(__NAMESPACE__ . '\Comparator\DeepComparator', $comparator);
213 |
214 | $comparator = $comparator->fallbackComparator();
215 | $this->assertInstanceOf(__NAMESPACE__ . '\Comparator\StrictPhpComparator', $comparator);
216 | }
217 | }
218 |
--------------------------------------------------------------------------------