Parity is a deep comparison library for PHP.
composer require icecave/parity
PHP does not provide a way to reliably and strictly compare values of heterogeneous types. Most of the built-in comparison operators perform often undesired type-juggling. The one exception is the strict equality operator, which has the further caveat that it can only compare objects by their identity. No type-strict mechanism is provided for comparing objects by their properties; nor are there any type-strict versions of the relative comparison operators (less-than, greater-than, etc).
Parity aims to fill the void by providing a comparison engine with the following features:
- Type-strict comparison of arrays and objects by their elements
- Recursion-safe comparison of objects
- Natural, type-strict comparison semantics for built-in types
- Powerful mechanisms for classes to customize comparison behavior
The Parity comparison engine is accessed via static methods on the Parity
facade class. These methods accept any
types and are guaranteed to produce a deterministic comparison result1. Some basic examples are
shown below using integers.
use Icecave\Parity\Parity;
// The compare() method provides a strcmp-style comparison, and hence can be
// used as a sorting function for operations such as usort()
assert(Parity::compare(1, 2) < 0);
// The following methods are convenience methods, implemented on top of compare().
assert(Parity::isEqualTo(1, 2) === false);
assert(Parity::isNotEqualTo(1, 2) === true);
assert(Parity::isNotEqualTo(1, 2) === true);
assert(Parity::isLessThan(1, 2) === true);
assert(Parity::isLessThanOrEqualTo(1, 2) === true);
assert(Parity::isGreaterThan(1, 2) === false);
assert(Parity::isGreaterThanOrEqualTo(1, 2) === false);
The core concept of Parity is the comparable. A comparable is any object that provides its own behavior for comparison with other values. The following refinements of the comparable concept are supported by the comparison engine:
- Restricted Comparable: A comparable that can be queried as to which values it may be compared to.
- Self Comparable: A comparable that may only be compared to other objects of exactly the same type.
- Sub Class Comparable: A comparable that may only be compared to other objects of the same or a derived type.
- Any Comparable: A comparable that may be freely compared to values of any other type.
A Comparator defines comparison behavior for values other than itself. Parity provides the following comparator implementations:
- Parity Comparator: Implements the logic surrounding the comparable concepts described in the section above.
- Deep Comparator: Performs deep comparison of arrays and objects. Object comparison is recursion-safe.
- Object Identity Comparator: Compares objects by identity.
- Strict PHP Comparator: Approximates PHP's strict comparison for the full suite of comparison operations.
- PHP Comparator: Exposes the standard PHP comparison behavior as a Parity comparator.
The following process is used by Parity::compare($A, $B)
to determine which comparison algorithm to use:
Use $A->compare($B)
if:
$A
is Any Comparable; or$A
is Restricted Comparable and$A->canCompare($B)
returnstrue
; or$A
is Self Comparable and$A
is exactly the same type as$B
; or$A
is Sub Class Comparable and$B
is an instance of the class where$A->compare()
is implemented
If none of the conditions above are true, the comparison is tried in reverse with $A
on the right hand side and $B
on the left; the result is also inverted. If still no comparison is possible, Parity falls back to a strictly-typed
deep comparison.
When comparing scalar types, integers and doubles (PHP's only true numeric types) are treated as though they were the
same type, such that the expression 3 < 3.5 < 4
holds true. Numeric strings are not compared in this way.
- Comparison of recursive objects is not a truly deterministic operation as objects are compared by their object hash where deeper comparison would otherwise result in infinite recursion.