2018-06-21 11:27 GMT+02:00 Rudi Theunissen <rtheunis...@php.net>: > The Comparable RFC (http://wiki.php.net/rfc/comparable) was written in > 2010 > and > revised in 2015 by Adam Harvey, but was not conclusive. I would like to > take > on some shared responsibility to push this forward and re-open the > discussion. > > Why is this useful, and why should it be added to PHP? > > > 1. The default comparison of objects can be expensive. > > Objects are compared and tested for equality by recursively comparing their > properties. If an object has many properties, or properties that are > objects > or have large arrays, this comparison may dive very deep. > > It may not be necessary to do all this work to determine whether two > objects > are equal. For example, if you have a data model that represents a database > entity, you're only concerned about the data attributes, and there's no > need > to compare the database connection or other internal metadata. > > Objects that have a defining value (like a number type), can simply compare > itself in value to the other object (if also a number type), and can skip > checking other properties. This has the potential to save a lot of > unnecessary comparisons. > > > 2. The default comparison of objects can be risky. > > There could be cases where two objects have the same public properties but > one of them (perhaps a subclass) has a private property. These two objects > could be considered equal to the outside world, but `==` would return > `false`. When a developer added this private property, it might not have > been obvious that they were affecting equality. This makes `==` checks on > objects very fragile where it could be explictly defined and controlled. > > > 3. There is no way to specify strict comparison. > > With PHP becoming more type-safe since PHP 7 with strict mode and scalar > type-hints, the current behaviour feels out of date. For example: > > ``` > $a = new stdClass(); > $b = new stdClass(); > > $a->prop = 1; > $b->prop = true; > > $a == $b; // true > ``` > > 4. Adding this functionality does not affect backwards-compatibility and > could > probably be implemented in 5.x as well. To my knowledge, The only major > changes required are in the default object handlers. This would > automatically affect functions like `array_search` and `sort`. > > > 5. Other major languages support this and there seems to be a decent amount > of > public support for it. > > > What is the proposed API for this? > > Magic methods. Objects that don't implement them maintain the current > behaviour. > > ``` > /** > * @returns -1 if less than, > * 0 if equal to, > * 1 if greater than the other value (in terms of relative ordering). > */ > __compareTo(mixed $other): int; >
When comparing objects should be obvoius to use `object` type hint not `mixed` > > /** > * @returns true if this instance is equal to the given value, or false if > not. > */ > __equals(mixed $other): bool; ``` > If __compareTo($other) gives 0 then there is no justification for additional method. IMO you're duplicating functionality here. > > > Why magic methods? > > - No risk of breaking backwards compatibility because method names that > start > with `__` are always reserved. > > - All objects can already be compared and tested for equality, so checking > if > an object implements an interface would not give you any useful > information. > Alternatively, we could introduce a function like `is_comparable`. > > - Python uses magic methods because there are no interfaces. Java has a > Comparable interface because not all objects are comparable by default. > It's > important to note that we're not exposing the ability to make an object > comparable, but instead to change the default behaviour when compared. All > objects are technically already comparable. > > - PHP already uses magic methods to change the default behaviour of > objects. > > - Interfaces like `Iterable` or `IteratorAggregate` must be interfaces > because > not all objects are iterable. > > > Why do we need `__equals` when `__compareTo` can just return `0`? > > 1. Anything can be tested for equality, but all things are not comparable. > For example, an apple can be equated with an orange and we can say that the > apple does not equal the orange. However, comparing the apple to an orange > makes no logical sense. There is nothing about either object that would > allow us to say that the apple is greater or less than the orange. > > 2. Some things that are not equal have the same relative ordering. For > example, > a floating point value of `2.0` does not equal the integer `2`, but > their > relative ordering is the same, and a comparison would return `0`. > That's true, but you want to compare objects not scalar types, right? > > 3. The contexts in which they are called are not the same. Testing for > equality > occurs when an object is compared with another using `==` or in functions > like `array_search`. The question we're asking is whether the two values > are > considered equal, and we're not concerned with ordering. The context for > comparison is when we need to determine the relative ordering of two values > using `<`, `>`, or in functions like `sort`. > > > Issues to be discussed: > > 1. What happens when we use `<=` and `>=`? Does the "or equal to" part call > `__equals` or does it check if `__compareTo` returns 0? > > 2. Should we also expose strict comparison, ie. `===` ? > > 3. Naming: > - `__eq` > - `__equalTo` > - `__equals` > - `__compareTo` > - `__comparedTo` > - `__compare` > - `__cmp` > Thanks for reanimating this subject. -- regards / pozdrawiam, -- Michał Brzuchalski about.me/brzuchal brzuchalski.com