Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4f26e8f
Add __compareTo to struct _zend_class_entry
rtheunissen Jun 24, 2018
e8f12ae
Register __compareTo in zend_register_functions
rtheunissen Jun 24, 2018
bd4bcfe
Add __compareTo to class_container in struct _zend_fcall_info_cache
rtheunissen Jun 24, 2018
4504026
Add __compareTo to visbility and static checks
rtheunissen Jun 24, 2018
6b914f4
Add ZEND_COMPARETO_FUNC_NAME constant
rtheunissen Jun 24, 2018
9db2762
Add inheritance support for __compareTo
rtheunissen Jun 24, 2018
e2cb3cc
Add default object handler for compare
rtheunissen Jun 24, 2018
dae3517
Fix type in zend_object_compare_zvals_t
rtheunissen Jun 24, 2018
91e7294
Normalise compare return and add compare fallthrough
rtheunissen Jun 24, 2018
1866849
Use is_equal_function directly rather than compare_function in ZEND_V…
rtheunissen Jun 24, 2018
a0d084f
Add serialize/unserialize of __compareTo pointer in zend_file_cache
rtheunissen Jun 24, 2018
1fab514
Add __compareTo to zend_update_parent_ce
rtheunissen Jun 24, 2018
d89cb77
Add tests for __compareTo
rtheunissen Jun 24, 2018
6ec54ec
Revert unintended whitespace changes
rtheunissen Jun 24, 2018
cde9763
Move tests to compareTo dir
rtheunissen Jun 24, 2018
4f3bd77
Extend tests that compare objects to null
rtheunissen Jun 25, 2018
d79ca48
Add new tests and some for __equals too
rtheunissen Jun 25, 2018
08e0985
Add __equals magic method and equals object handler
rtheunissen Jun 25, 2018
c9ee7ec
Remove the intrinsic __equals check during comparison
rtheunissen Jun 25, 2018
af25d26
Remove commented code
rtheunissen Jun 25, 2018
af3796d
Generate zend_vm
rtheunissen Jun 25, 2018
5a64530
Remove seemingly unrelated files
rtheunissen Jun 26, 2018
8f71ea0
Attempt to remove whitespace changes
rtheunissen Jun 26, 2018
76eed35
Revert "Revert unintended whitespace changes"
rtheunissen Jun 26, 2018
d7d880d
Allow equality to automatically determine equal ordering
rtheunissen Jun 26, 2018
5f9eeb9
Update for changes to the RFC
rtheunissen Jun 27, 2018
ab98008
Updated Zend/zend_vm_execute.h with vm_gen
rtheunissen Jun 27, 2018
c0789be
Fix unnecessary EXPECTF and fill out some comment placeholders
rtheunissen Jun 27, 2018
2c349d9
Fix bad is_smaller_or_equal_function call
rtheunissen Jun 27, 2018
1b8d8b4
Fix bad is_smaller_function call
rtheunissen Jun 27, 2018
6f520e4
Fix broken test
rtheunissen Jun 28, 2018
b433734
Update IN_ARRAY op handler and in_array sccp optimisation to use is_e…
rtheunissen Jun 28, 2018
d218d32
Add __equals and __compareTo to zend_accelerator_util_funcs inheritan…
rtheunissen Jun 28, 2018
3c1c9ea
Move compareTo and equals tests into their own directories and improv…
rtheunissen Jun 28, 2018
e79ef08
Move existing comparison tests into their own directory
rtheunissen Jun 28, 2018
399febc
Minor changes, comments
rtheunissen Jul 2, 2018
0f6755b
Added draft upgrading and news additions
rtheunissen Jul 2, 2018
be33ca6
Remove some blank lines
rtheunissen Jul 4, 2018
7bbae20
Add small regression tests for objects of different classes
rtheunissen Jul 4, 2018
6ca0304
Change Z_TYPE_P call to Z_ISUNDF_P
rtheunissen Jul 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ PDO_Firebird:
logging. (Philip Prindeville)
. Fixed bug #63217 (Constant numeric strings become integers when used as
ArrayAccess offset). (Rudi Theunissen, Dmitry)
. Added user-defined object comparison.
(https://wiki.php.net/rfc/object-comparison) (Rudi Theunissen)

- DOM:
. Fixed bug #76285 (DOMDocument::formatOutput attribute sometimes ignored).
Expand Down
24 changes: 24 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,30 @@ Core:
are available under Linux, FreeBSD, Windows, Mac, SunOS, AIX and their
derivatives. If no required timers are provided by a corresponding
platform, the function returns false.
. Added two new magic methods: __compareTo and __equals. Classes may choose to
implement these to define relative natural ordering and equality, which
affects functions like in_array() and sort(), and comparison operators:
<, >, <=, >=, <=>, ==, !=. The behaviour of === and !== has not changed.
Classes that do not implement these methods will continue to be compared
according to the rules that were introduced in PHP 5.
(RFC: https://wiki.php.net/rfc/object-comparison)


+Changes to object comparison behaviour
+--------------------------------------
+
+* Classes may now implement a Comparable interface to override how objects are
+ compared to other values. If implemented, the class must implement a public
+ compareTo() method with the signature:
+
+ public function compareTo($other)
+
+ This method must return a negative integer to signal that the object is less
+ than $other, zero to signal that the object is equal to $other, or a positive
+ integer to signal that the object is greater than $other.
+
+ Objects that don't implement Comparable will continue to be compared
+ according to the rules used in PHP 5.

Date:
. Added the DateTime::createFromImmutable() method, which mirrors
Expand Down
31 changes: 31 additions & 0 deletions Zend/tests/comparisons/__compareTo/Comparable.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/**
* This is a class that compares by a given $value only. There is also a $decoy
* value that makes sure that the tests would fail if `__compareTo` is not used.
*/
class Comparable
{
protected $decoy;
protected $value;

public function __construct($value) {
$this->decoy = $value * -1;
$this->value = $value;
}

public function __compareTo($other) {
if ($other instanceof self) {
return $this->value <=> $other->value;
}

/**
* Allow comparing to scalar numeric values.
*/
if (is_numeric($other)) {
return $this->value <=> $other;
}

throw new Exception("Failed to compare");
}
}
121 changes: 121 additions & 0 deletions Zend/tests/comparisons/__compareTo/compare-array-func-array-keys.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
--TEST--
__compareTo: Called by array_keys which has equality semantics (searching)
--FILE--
<?php
include("Comparable.inc");

$a = new Comparable(1);
$b = new Comparable(2);
$c = new Comparable(3);

$array = [
'a' => $a,
'b' => $b,
'c' => $c,
];

var_dump(array_keys($array, $a, $strict = false)); // a
var_dump(array_keys($array, $b, $strict = false)); // b
var_dump(array_keys($array, $c, $strict = false)); // c

var_dump(array_keys($array, $a, $strict = true)); // a
var_dump(array_keys($array, $b, $strict = true)); // b
var_dump(array_keys($array, $c, $strict = true)); // c

/* Found, because Comparable::__compareTo returned 0 */
var_dump(array_keys($array, new Comparable(1), $strict = false)); // a
var_dump(array_keys($array, new Comparable(2), $strict = false)); // b
var_dump(array_keys($array, new Comparable(3), $strict = false)); // c

/* Not found because strict comparison doesn't call __compareTo */
var_dump(array_keys($array, new Comparable(1), $strict = true));
var_dump(array_keys($array, new Comparable(2), $strict = true));
var_dump(array_keys($array, new Comparable(3), $strict = true));

/* Found, because Comparable::__compareTo returned 0 and we haven't implemented __equals */
var_dump(array_keys($array, 1, $strict = false)); // a
var_dump(array_keys($array, 2, $strict = false)); // b
var_dump(array_keys($array, 3, $strict = false)); // c

/* Not found because strict comparison doesn't call __compareTo */
var_dump(array_keys($array, 1, $strict = true));
var_dump(array_keys($array, 2, $strict = true));
var_dump(array_keys($array, 3, $strict = true));

/* Not found */
var_dump(array_keys($array, new Comparable(4), $strict = true));
var_dump(array_keys($array, new Comparable(5), $strict = true));

var_dump(array_keys($array, new Comparable(4), $strict = false));
var_dump(array_keys($array, new Comparable(5), $strict = false));

?>
--EXPECT--
array(1) {
[0]=>
string(1) "a"
}
array(1) {
[0]=>
string(1) "b"
}
array(1) {
[0]=>
string(1) "c"
}
array(1) {
[0]=>
string(1) "a"
}
array(1) {
[0]=>
string(1) "b"
}
array(1) {
[0]=>
string(1) "c"
}
array(1) {
[0]=>
string(1) "a"
}
array(1) {
[0]=>
string(1) "b"
}
array(1) {
[0]=>
string(1) "c"
}
array(0) {
}
array(0) {
}
array(0) {
}
array(1) {
[0]=>
string(1) "a"
}
array(1) {
[0]=>
string(1) "b"
}
array(1) {
[0]=>
string(1) "c"
}
array(0) {
}
array(0) {
}
array(0) {
}
array(0) {
}
array(0) {
}
array(0) {
}
array(0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
--TEST--
__compareTo: Called by array_search which has equality semantics (searching)
--FILE--
<?php
include("Comparable.inc");

$a = new Comparable(1);
$b = new Comparable(2);
$c = new Comparable(3);

$array = [$a, $b, $c];

var_dump(array_search($a, $array, $strict = false)); // 0
var_dump(array_search($b, $array, $strict = false)); // 1
var_dump(array_search($c, $array, $strict = false)); // 2

var_dump(array_search($a, $array, $strict = true)); // 0
var_dump(array_search($b, $array, $strict = true)); // 1
var_dump(array_search($c, $array, $strict = true)); // 2

/* Found, because Comparable::__compareTo returned 0 */
var_dump(array_search(new Comparable(1), $array, $strict = false)); // 0
var_dump(array_search(new Comparable(2), $array, $strict = false)); // 1
var_dump(array_search(new Comparable(3), $array, $strict = false)); // 2

/* Not found, because not the same instance */
var_dump(array_search(new Comparable(1), $array, $strict = true)); // false
var_dump(array_search(new Comparable(2), $array, $strict = true)); // false
var_dump(array_search(new Comparable(3), $array, $strict = true)); // false

/* Found, because Comparable::__compareTo returned 0 and we haven't implemented __equals */
var_dump(array_search(1, $array, $strict = false)); // 0
var_dump(array_search(2, $array, $strict = false)); // 1
var_dump(array_search(3, $array, $strict = false)); // 2

/* Not found, because strict comparison doesn't call __compareTo */
var_dump(array_search(1, $array, $strict = true)); // false
var_dump(array_search(2, $array, $strict = true)); // false
var_dump(array_search(3, $array, $strict = true)); // false

/* Not found */
var_dump(array_search(new Comparable(4), $array, $strict = false)); // false
var_dump(array_search(new Comparable(5), $array, $strict = false)); // false

var_dump(array_search(new Comparable(4), $array, $strict = true)); // false
var_dump(array_search(new Comparable(5), $array, $strict = true)); // false

?>
--EXPECT--
int(0)
int(1)
int(2)
int(0)
int(1)
int(2)
int(0)
int(1)
int(2)
bool(false)
bool(false)
bool(false)
int(0)
int(1)
int(2)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
--TEST--
__compareTo: Called by in_array which has equality semantics (searching)
--FILE--
<?php
include("Comparable.inc");

$a = new Comparable(1);
$b = new Comparable(2);
$c = new Comparable(3);

$array = [$a, $b, $c];

var_dump(in_array($a, $array, $strict = false));
var_dump(in_array($b, $array, $strict = false));
var_dump(in_array($c, $array, $strict = false));

var_dump(in_array($a, $array, $strict = true));
var_dump(in_array($b, $array, $strict = true));
var_dump(in_array($c, $array, $strict = true));

/* Found, because Comparable::__compareTo returned 0 */
var_dump(in_array(new Comparable(1), $array, $strict = false));
var_dump(in_array(new Comparable(2), $array, $strict = false));
var_dump(in_array(new Comparable(3), $array, $strict = false));

/* Not found, because not the same instance */
var_dump(in_array(new Comparable(1), $array, $strict = true));
var_dump(in_array(new Comparable(2), $array, $strict = true));
var_dump(in_array(new Comparable(3), $array, $strict = true));

/* Found, because Comparable::__compareTo returned 0 and we haven't implemented __equals */
var_dump(in_array(1, $array, $strict = false));
var_dump(in_array(2, $array, $strict = false));
var_dump(in_array(3, $array, $strict = false));

/* Not found, because strict comparison doesn't call __compareTo */
var_dump(in_array(1, $array, $strict = true));
var_dump(in_array(2, $array, $strict = true));
var_dump(in_array(3, $array, $strict = true));

/* Not found */
var_dump(in_array(new Comparable(4), $array, $strict = false));
var_dump(in_array(new Comparable(5), $array, $strict = false));

var_dump(in_array(new Comparable(4), $array, $strict = true));
var_dump(in_array(new Comparable(5), $array, $strict = true));

?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
38 changes: 38 additions & 0 deletions Zend/tests/comparisons/__compareTo/compare-array-sort.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
__compareTo: Called by array functions with ordering semantics (sorting)
--FILE--
<?php
include("Comparable.inc");

$a = new Comparable(3);
$b = new Comparable(1);
$c = new Comparable(2);

$array = [$a, $b, $c];

sort($array);

var_dump($array[0]);
var_dump($array[1]);
var_dump($array[2]);

?>
--EXPECT--
object(Comparable)#2 (2) {
["decoy":protected]=>
int(-1)
["value":protected]=>
int(1)
}
object(Comparable)#3 (2) {
["decoy":protected]=>
int(-2)
["value":protected]=>
int(2)
}
object(Comparable)#1 (2) {
["decoy":protected]=>
int(-3)
["value":protected]=>
int(3)
}
Loading