{"id":15740082,"url":"https://github.com/marcospassos/phpcommon-comparison","last_synced_at":"2025-03-13T08:31:52.622Z","repository":{"id":57040238,"uuid":"61682522","full_name":"marcospassos/phpcommon-comparison","owner":"marcospassos","description":"PHP 5.4+ library to represent equivalence relations and strategies for hashing and sorting values.","archived":false,"fork":false,"pushed_at":"2017-09-09T14:20:48.000Z","size":168,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-26T19:37:24.611Z","etag":null,"topics":["compare-values","comparison","equality","equivalence-relations","hashcode","php","php-library","sorting-values"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcospassos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-22T02:27:35.000Z","updated_at":"2020-01-19T10:21:40.000Z","dependencies_parsed_at":"2022-08-24T00:50:54.255Z","dependency_job_id":null,"html_url":"https://github.com/marcospassos/phpcommon-comparison","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcospassos%2Fphpcommon-comparison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcospassos%2Fphpcommon-comparison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcospassos%2Fphpcommon-comparison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcospassos%2Fphpcommon-comparison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcospassos","download_url":"https://codeload.github.com/marcospassos/phpcommon-comparison/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243369893,"owners_count":20280097,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compare-values","comparison","equality","equivalence-relations","hashcode","php","php-library","sorting-values"],"created_at":"2024-10-04T02:11:19.774Z","updated_at":"2025-03-13T08:31:52.217Z","avatar_url":"https://github.com/marcospassos.png","language":"PHP","readme":"# PhpCommon Comparison\n\n[![Build Status](https://travis-ci.org/marcospassos/phpcommon-comparison.svg?branch=master)](https://travis-ci.org/marcospassos/phpcommon-comparison)\n[![Code Coverage](https://scrutinizer-ci.com/g/marcospassos/phpcommon-comparison/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/marcospassos/phpcommon-comparison/?branch=master)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/marcospassos/phpcommon-comparison/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/marcospassos/phpcommon-comparison/?branch=master)\n[![StyleCI](https://styleci.io/repos/61682522/shield)](https://styleci.io/repos/61682522)\n[![Latest Stable Version](https://poser.pugx.org/phpcommon/comparison/v/stable)](https://packagist.org/packages/phpcommon/comparison)\n[![Dependency Status](https://www.versioneye.com/user/projects/576a0606fdabcd003c031888/badge.svg?style=flat)](https://www.versioneye.com/user/projects/576a0606fdabcd003c031888)\n\nLatest release: [1.0.0-beta](https://packagist.org/packages/phpcommon/comparison#1.0.0)\n\nPHP 5.4+ library to represent equivalence relations and strategies for hashing\nand sorting values.\n\nEquivalence relations are useful to establish a generalized way for comparing\nvalues in respect to domain-specific requirements, as well as to represent\ncustom criteria for comparing values into bounded contexts, specially for use\nin collections.\n\nComplementary capabilities, such as hashing and sorting, are also covered by\nthis library, making it a valuable addition to your development tool belt. \n\nThe API is extensively documented in the source code. In addition, an\n[HTML version][link-apidoc] is also available for more convenient viewing in\nbrowser.\n\n# Installation\n\nUse [Composer][link-composer] to install the package:\n\n```\n$ composer require phpcommon/comparison\n```\n\n# Relations\n\nA relation is a mathematical tool for describing associations between elements\nof sets. Relations are widely used in computer science, especially in databases\nand scheduling applications.\n\nUnlike most modern languages, PHP does not support [operator overloading],\nhistorically avoided as a design choice. In other words, it is not possible to\noverride the default behaviour of native operators, such as equal, identical,\ngreater than, less than, etc. For example, Java provides the\n[Comparable][java-comparable] interface, while Python provides some\n[magic methods][python-magic-methods].\n\nThe importance of such concept become more evident in situations where the\nnotion of equivalence or ordering varies according to the subject of\ncomparison or to the context, as discussed in the following sections.\n\n# Equivalence\n\nIn mathematics, an [equivalence relation] is a binary relation that is\n_reflexive_, _symmetric_ and _transitive_. In the computing field, however,\nthere is another property that must be take into account: _consistency_.\nConsistency means that a relation should not produce different results for the\nsame input.\n\nA ubiquitous equivalence relation is the equality relation between elements of\nany set. Other examples includes:\n\n* _\"Has the same birthday as\"_ on the set of all people.\n* _\"Is similar to\" or \"congruent to\"_ on the set of all triangles.\n* _\"Has the same absolute value\"_ on the set of real numbers.\n\nFor the purpose of this library, an equivalence relation can be generic or\ntype-specific. Type-specific relations are defined by implementing either\n`Equatable` or `Equivalence` interfaces, while generic equivalences must\nimplement the last one.\n\n## Equatable Objects\n\nThe `Equatable` interface defines a generalized method that a class implements\nto create a type-specific method for determining equality of instances.\n\nTo illustrate, considers a class `Money`, which aims to represent monetary\nvalues. This class is a good candidate for implementing the `Equatable`\ninterface, because `Money` is a [Value Object], that is, the notion of\nequality of those objects isn't based on identity. Instead, two instances of\n`Money` are equal if they have the same values. Thus, while\n`Money::USD(5) === Money::USD(5)` returns `false`,\n`Money::USD(5)-\u003eequals(Money::USD(5))` returns `true`.\n\nHere is the class previously mentioned:\n\n```php\nfinal class Money implements Equatable\n{\n    private $amount;\n    private $currency;\n    \n    public function __construct($amount, $currency)\n    {\n        $this-\u003eamount = (int) $amount;\n        $this-\u003ecurrency = (string) $currency;\n    }\n    \n    public function equals(Equatable $other)\n    {\n        if (!$other instanceof self) {\n            return false;\n        }\n        \n        return $this-\u003eamount === $other-\u003eamount \u0026\u0026 $this-\u003ecurrency === $other-\u003ecurrency; \n    }\n}\n```\n\n## Equivalence Relations\n\nThere are many cases, though, where having a non-standard, or _external_, way\nfor comparing two values become necessary. Perhaps, the most obvious use case\nfor those custom relations is for use with collections, but it is also useful\nfor providing those capabilities to scalar values or an existing class\nthat cannot provide it itself, because it belongs to a third-party package or\nbuilt into PHP.\n\nSuppose you are developing a software to help hospitals to manage blood\ndonations. One of the requirements says that a nurse can not collect blood from\ndonors who have the same blood type. A relation for this scenario would look\nlike this:\n\n```php\nuse PhpCommon\\Comparison\\Equivalence;\n\nclass BloodGroupEquivalence implements Equivalence\n{\n    public function equals(Equatable $other)\n    {\n        return get_class($other) === static::class;\n    }\n\n    public function equivalent($left, $right)\n    {\n        if (!$left instanceof Person) {\n            UnexpectedTypeException::forType(Person::class, $left);\n        }\n\n        if (!$right instanceof Person) {\n            return false;\n        }\n\n        return $left-\u003egetBloodType() === $right-\u003egetBloodType();\n    }\n}\n```\n\nThis relation determines whether two people are of same blood group:\n\n```php\n$equivalence = new BloodGroupEquivalence();\n$donors = new BloodDonors($equivalence);\n$james = new Person('James', 'A');\n$john = new Person('John', 'A');\n\n// James and John are considered equivalent once they are of the same blood group\nvar_dump($equivalence-\u003eequivalent($james, $john)); // Outputs bool(true)\n\n// Initially, none of them are present in the collection\nvar_dump($volunteers-\u003econtains($james)); // Outputs bool(false)\nvar_dump($volunteers-\u003econtains($john)); // Outputs bool(false) \n\n// Add James to the set of volunteers\n$donors-\u003eadd($james);\n\n// Now, considering only the blood group of each donor for equality, both of\n// them are considered present in the collection\n$donors-\u003econtains($james); // Outputs bool(true)\n$donors-\u003econtains($john); // Outputs bool(true)\n```\n\nSince `BloodGroupEquivalence` establishes an equivalence relation between\npeople based on their blood group, any attempt to add John to the collection\nwill be ignored, because James is already present and they are of the same\nblood type.\n\nIt may look a bit complicated for a simple requirement at first, but in real\ncases it can be used to compare the equivalence among compatible blood types,\nin order to partition donors into groups.\n\n### Built-in Equivalence Relations\n\nThis library provides some generic equivalence relations as part of the\nstandard library, as described below.\n\n#### Identity Equivalence\n\n_Compares two values for identity._\n\nThis relation is based on the identical operator. Most of cases, two values are\nconsidered equivalent if they have the same type and value, but there are a few\nexceptions:\n\n* Two strings are equivalent if they have the same sequence of characters,\n  same length and same characters in corresponding positions.\n* Two numbers are equivalent if they are numerically equal\n  (have the same number value).\n  * Positive and negative zeros are equivalent to one another.\n  * `NAN` is unequal to every other value, including itself.\n  * Positive and negative infinities are equal only to themselves.\n* Two boolean values are equivalent if both are true or both are false.\n* Two distinct objects are never equivalent. An expression comparing objects is\n  only true if the operands reference the same instance.\n* Two arrays are equivalent if they they contain the equivalent entries\n  according to this relation, in the same order. Empty arrays are equivalent to\n  one another.\n* Two resources are equivalent if they have the same unique resource number.\n* Null is only equivalent to itself.\n\nThe following table summarizes how operands of the various types are compared:\n\n| `$A \\ $B` | NULL       | Boolean         | Integer         | Float           | String          | Resource        | Array           | Object          |\n------------|------------|-----------------|-----------------|-----------------|-----------------|-----------------|-----------------|-----------------|\n| NULL      | **`true`** | `false`         | `false`         | `false`         | `false`         | `false`         | `false`         | `false`         |\n| Boolean   | `false`    | **`$A === $B`** | `false`         | `false`         | `false`         | `false`         | `false`         | `false`         | \n| Integer   | `false`    | `false`         | **`$A === $B`** | `false`         | `false`         | `false`         | `false`         | `false`         | \n| Float     | `false`    | `false`         | `false`         | **`$A === $B`** | `false`         | `false`         | `false`         | `false`         | \n| String    | `false`    | `false`         | `false`         | `false`         | **`$A === $B`** | `false`         | `false`         | `false`         |\n| Resource  | `false`    | `false`         | `false`         | `false`         | `false`         | **`$A === $B`** | `false`         | `false`         | \n| Array     | `false`    | `false`         | `false`         | `false`         | `false`         | `false`         | **`$A === $B`** | `false`         |\n| Object    | `false`    | `false`         | `false`         | `false`         | `false`         | `false`         | `false`         | **`$A === $B`** |\n\n#### Value Equivalence\n\n_Compares two values for equality._\n\nThe value equivalence behaves exactly as the identity equivalence, except that\nit delegates comparison between `Equatable` objects to the objects being\ncompared. Additionally, external relations can be specified for comparing\nvalues of a particular type. It is useful in cases where is desirable to\noverride the default behaviour for a specific type, but keep all the others.\nIt is also useful for defining a relation for objects of classes that belong\nto third-party packages or built into PHP.\n\nThe following rules are used to determine whether two values are considered\nequivalent:\n\n* Two strings are equivalent if they have the same sequence of characters,\n  same length, and same characters in corresponding positions.\n* Two numbers are equivalent if they are numerically equal\n  (have the same number value).\n  * Positive and negative zeros are equivalent to one another.\n  * `NAN` is unequal to every other value, including itself.\n  * Positive and negative infinities are equal only to themselves.\n* Two boolean values are equivalent if both are true or both are false.\n* Two objects are equivalent if any of the following conditions hold:\n  * The both objects are instances of `Equatable` and the expression\n   `$left-\u003eequals($right)` is evaluated to `true`.\n  * A specific equivalence relation is mapped to the type of the left-hand \n    value and the expression `$relation-\u003eequivalent($left, $right)` is\n    evaluated to `true`.\n  * Both values refer to the same instance of the same class in a particular\n    namespace.\n* Two arrays are equivalent if they they contain the equivalent entries\n  according to this relation, in the same order. Empty arrays are equivalent to\n  one another.\n* Two resources are equivalent if they have the same unique resource number.\n* Null is only equivalent to itself.\n\nThe following table summarizes how operands of the various types are compared:\n\n| `$A \\ $B` | NULL       | Boolean         | Integer         | Float           | String          | Resource        | Array            | Object          | Equatable            |\n------------|------------|-----------------|-----------------|-----------------|-----------------|-----------------|------------------|-----------------|----------------------|\n| NULL      | **`true`** | `false`         | `false`         | `false`         | `false`         | `false`         | `false`          | `false`         | `false`              | \n| Boolean   | `false`    | **`$A === $B`** | `false`         | `false`         | `false`         | `false`         | `false`          | `false`         | `false`              | \n| Integer   | `false`    | `false`         | **`$A === $B`** | `false`         | `false`         | `false`         | `false`          | `false`         | `false`              | \n| Float     | `false`    | `false`         | `false`         | **`$A === $B`** | `false`         | `false`         | `false`          | `false`         | `false`              | \n| String    | `false`    | `false`         | `false`         | `false`         | **`$A === $B`** | `false`         | `false`          | `false`         | `false`              |\n| Resource  | `false`    | `false`         | `false`         | `false`         | `false`         | **`$A === $B`** | `false`          | `false`         | `false`              | \n| Array     | `false`    | `false`         | `false`         | `false`         | `false`         | `false`         | **`eq($A, $B)`** | `false`         | `false`              |\n| Object    | `false`    | `false`         | `false`         | `false`         | `false`         | `false`         | `false`          | **`$A === $B`** | `false`              |\n| Equatable | `false`    | `false`         | `false`         | `false`         | `false`         | `false`         | `false`          | `false`         | **`$A‑\u003eequals($B)`** |\n\nWhere `eq()` denotes a function that compares each pair of corresponding\nentries recursively, according to the rules described above.\n\nThis relation also provides a way to override the equivalence logic for a\nparticular class without the need to create a new relation. For example,\nsuppose you want to compare instances of `\\DateTime` based on their values,\nbut keep the default behaviour for the other types. It can be accomplished by\nspecifying a custom relation to be used whenever an instance of `\\DateTime`\nis compared against another value:\n\n```php\nuse PhpCommon\\Comparison\\Hasher\\ValueHasher as ValueEquivalence;\nuse PhpCommon\\Comparison\\Hasher\\DateTimeHasher as DateTimeEquivalence;\nuse DateTime;\n\n$relation = new ValueEquivalence([\n    DateTime::class =\u003e new DateTimeEquivalence()\n]);\n\n$date = '2017-01-01';\n$timezone = new DateTimeZone('Pacific/Nauru');\n\n$left = new DateTime($date, $timezone);\n$right = new DateTime($date, $timezone);\n\n// Outputs bool(true)\nvar_dump($relation-\u003eequivalent($left, $right));\n```\n\n#### Semantic Equivalence\n\n_Compares two values for semantic equality._\n\nA semantic equivalence is planned for future versions. It would allow the\ncomparison of values that look semantically similar - even if they are of\ndifferent types. It is similar to how loose comparison works in PHP, but under\nmore restrictive conditions, in such a way that properties of reflexivity,\nsymmetry and transitivity hold.\n\n#### DateTime Value Equivalence\n\n_Compares two `\\DateTime` instances based on their date, time and time zone._\n\nThis relation considers two instances of `\\DateTime` to be equivalent if they\nhave the same date, time and time zone:\n\n```php\nuse PhpCommon\\Comparison\\Hasher\\IdentityHasher as IdentityEquivalence;\nuse PhpCommon\\Comparison\\Hasher\\DateTimeHasher as DateTimeEquivalence;\nuse DateTime;\n\n$identity = new IdentityEquivalence();\n$value = new DateTimeEquivalence();\n\n$date = '2017-01-01';\n$timezone = new DateTimeZone('Pacific/Nauru');\n\n$left = new DateTime($date, $timezone);\n$right = new DateTime($date, $timezone);\n\n// Outputs bool(false)\nvar_dump($identity-\u003eequivalent($left, $right));\n\n// Outputs bool(true)\nvar_dump($value-\u003eequivalent($left, $right));\n```\n\n# Hashing\n\nIn PHP, array keys can be only represented as numbers and strings. However,\nthere are several cases where storing complex types as keys is helpful.\nTake as example classes that represent different kinds of numbers or strings,\nsuch as GMP objects, Unicode strings, etc. It would be convenient to be able to\nuse such objects as array keys too.\n\nTo fill this gap, this library introduces the interfaces `Hashable` and\n`Hasher`, which specify a protocol for providing hash codes for values. These\ninterfaces does not require implementors to provide perfect hashing functions.\nThat is, two values that are not equivalent may have the same hash code.\nHowever, to determine whether two values with the same hash code are, in fact,\nequal, the concepts of hashing and equivalence should be combined in a\ncomplementary way. It explains why `Hasher` and `Hashable` extends\n`Equivalence` and `Equatable` respectively.\n\n\u003e **A word of warning**\n\n\u003e A hash code is intended for efficient insertion and lookup in collections that\n\u003e are based on a hash table and for fast inequality checks. A hash code is not a\n\u003e permanent value. For this reason:\n\n\u003e * Do not serialize hash code values or store them in databases.\n\u003e * Do not use the hash code as the key to retrieve an object from a keyed\n\u003e   collection.\n\u003e * Do not send hash codes across application domains or processes. In some\n\u003e   cases, hash codes may be computed on a per-process or per-application\n\u003e   domain basis.\n\u003e * Do not use the hash code instead of a value returned by a cryptographic\n\u003e   hashing function if you need a cryptographically strong hash.\n\u003e * Do not test for equality of hash codes to determine whether two objects are\n\u003e   equal, once unequal values can have identical hash codes.\n\n## Hashable\n\nThere are cases where it might be desirable to define a custom hashing logic\nfor a class to best fit your requirements. For example, suppose you have a\nPoint class to represent a 2D point:\n\n```php\nnamespace PhpCommon\\Comparison\\Equatable;\n\nfinal class Point implements Equatable\n{\n    private $x;\n    private $y;\n    \n    public function __construct($x, $y)\n    {\n        $this-\u003ex = (int) $x;\n        $this-\u003ey = (int) $y;\n    }\n    \n    public function equals(Equatable $point)\n    {\n        if (!$point instanceof Point) {\n            return false;\n        }\n        \n        return $this-\u003ex === $point-\u003ex \u0026\u0026 $this-\u003ey === $point-\u003ey;\n    }\n}\n```\n\nA `Point` holds the x and y coordinates of a point. According to the definition\nof the class, two points are considered equal if they have the same\ncoordinates. However, if you intend to store instances of `Point` in an\nhash-based map, for example, because you want to associate coordinates to\nlabels, then you must ensure that your class produces hash codes that are\n_coherent_ with the logic used for determining when two points are considered\nequal:\n\n```php\nnamespace PhpCommon\\Comparison\\Equatable;\n\nfinal class Point implements Hashable\n{\n    private $x;\n    private $y;\n    \n    public function __construct($x, $y)\n    {\n        $this-\u003ex = (int) $x;\n        $this-\u003ey = (int) $y;\n    }\n    \n    public function equals(Equatable $point)\n    {\n        if (!$point instanceof Point) {\n            return false;\n        }\n        \n        return $this-\u003ex === $point-\u003ex \u0026\u0026 $this-\u003ey === $point-\u003ey;\n    }\n\n    public function getHash()\n    {\n        return 37 * (31 + $this-\u003e$x) + $this-\u003e$x;\n    }\n}\n```\n\nIn that way, the `getHash()` method works in accordance to the `equals()`\nmethod, although the hashing algorithm may not be ideal. The implementation\nof efficient algorithms for hash code generation is beyond the scope of this\nguide. However, it is recommended to use a fast algorithm that produces\nreasonably different results for unequal values, and shift the heavy comparison\nlogic to `Equatable::equals()`.\n\n\u003e Notice that hashable objects should either be immutable, or you need to\n\u003e exercise discipline in not changing them after they have been used in a\n\u003e hash-based structures.\n\n## Hasher\n\nA Hasher provides hashing functionality for primitive types and objects of\nclasses that do not implement `Hashable`. \n\nThe method `hash()` introduced by this interface is intended to provide a\nmeans for performing fast _inequivalence_ checks and efficient insertion and\nlookup in hash-based data structures. This method is always _coherent_ with\n`equivalent()`, which means that for any references `$x` and `$y`, if\n`equivalent($x, $y)`, then `hash($x) === hash($y)`. However, if\n`equivalence($x, $y)` evaluates to `false`, `hash($x) === hash($y)` may\nstill be true. Hence why the `hash()` method is suitable for _inequivalence_\nchecks, but not _equivalence_ checks.\n\nAll implementations of `Equivalence` included in this library also provide\nhashing functionality. More information about how values are hashed can be\nfound in the documentation of the respective implementation.\n\n# Sorting\n\nFollowing the same logic of the concepts previously discussed, `Comparable` and\n`Comparator` are interfaces to provide _natural_ and custom sorting strategies,\nrespectively. Both interfaces specify a [total order] relation, a relation\nthat is _reflexive_, _antisymmetric_ and _transitive_.\n\n## Comparable\n\nThis interface imposes a total ordering on the objects of each class that\nimplements it. This ordering is referred to as the _natural ordering_ of the\nclass, and the method `Comparable::compareTo()` is referred to as its\n_natural comparison method_.\n\nThe following example shows how a class can define the natural order of its\ninstances:\n\n```php\nuse PhpCommon\\Comparison\\UnexpectedTypeException;\n\nfinal class BigInteger implements Comparable\n{\n    private $value;\n\n    public function __construct($value)\n    {\n        $this-\u003evalue = (string) $value;\n    }\n\n    public function compareTo(Comparable $other)\n    {\n        if (!$other instanceof self) {\n            throw UnexpectedTypeException::forType(BigInteger::class, $other);\n        }\n\n        return bccomp($this-\u003evalue, $other-\u003evalue);\n    }\n}\n```\n\n## Comparator\n\nThe purpose of a `Comparator` is to allow you to define one or more comparison\nstrategies that are not the natural comparison strategy for a class. Ideally, a\n`Comparator` must be implemented by a class different from the one it defines\nthe comparison strategy for. If you want to define a natural comparison\nstrategy for a class, you can implement `Comparable` instead.\n\nComparators can be passed to a sort method of a collection to allow precise\ncontrol over its sort order. It can also be used to control the order of\ncertain data structures, such as sorted sets or sorted maps. For example,\nconsider the following comparator that orders strings according to\ntheir length:\n\n```php\nuse PhpCommon\\Comparison\\Comparator;\n\nclass StringLengthComparator implements Comparator\n{ \n    public function compare($left, $right)\n    {\n        return strlen($left) \u003c=\u003e strlen($right);\n    }\n}\n\n$comparator = new StringLengthComparator();\n\n// Outputs int(-1)\nvar_dump($comparator-\u003ecompare('ab', 'a'));\n```\n\nThis implementation represents one of many possible ways of sorting strings.\nOther strategies includes sorting alphabetically, lexicographically, etc.\n\n## Change log\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.\n\n\n## Testing\n\n``` bash\n$ composer test\n```\n\nCheck out the [Test Documentation][link-testsdoc] for more details.\n\n## Contributing\n\nContributions to the package are always welcome!\n\n* Report any bugs or issues you find on the [issue tracker][link-issue-tracker].\n* You can grab the source code at the package's\n[Git repository][link-repository].\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for\ndetails.\n\n## Security\n\nIf you discover any security related issues, please email\nmarcos@marcospassos.com instead of using the issue tracker.\n\n## Credits\n\n* [Marcos Passos][link-author]\n- [All Contributors][link-contributors]\n\n## License\n\nAll contents of this package are licensed under the [MIT license](LICENSE).\n\n[operator overloading]: https://en.wikipedia.org/wiki/Operator_overloading\n[java-comparable]: https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html\n[python-magic-methods]: https://docs.python.org/3/reference/datamodel.html#object.__lt__\n[equivalence relation]: https://en.wikipedia.org/wiki/Equivalence_relation\n[value object]: https://en.wikipedia.org/wiki/Value_object\n[total order]: https://en.wikipedia.org/wiki/Total_order\n[link-apidoc]: http://marcospassos.github.io/phpcommon-comparison/docs/api\n[link-testsdoc]: http://marcospassos.github.io/phpcommon-comparison/docs/test\n[link-composer]: https://getcomposer.org\n[link-author]: http://github.com/marcospassos\n[link-contributors]: https://github.com/marcospassos/phpcommon-comparison/graphs/contributors\n[link-issue-tracker]: https://github.com/marcospassos/phpcommon-comparison/issues\n[link-repository]: https://github.com/marcospassos/phpcommon-comparison\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcospassos%2Fphpcommon-comparison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcospassos%2Fphpcommon-comparison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcospassos%2Fphpcommon-comparison/lists"}