davey

Davey Shafik

ajf

Andrea Faulds

stas

Stas Malyshev , from Zend Technologies
(user votes do not necessarily reflect their company's views)

Contents

PHP RFC: Combined Comparison (Spaceship) Operator

Introduction

This RFC adds a new operator for combined comparison. Similar to strcmp() or version_compare() in behavior, but it can be used on all generic PHP values with the same semantics as <, <=, ==, >=, >.

Proposal

Add a new operator (expr) <=> (expr), it returns 0 if both operands are equal, 1 if the left is greater, and -1 if the right is greater. It uses exactly the same comparison rules as used by our existing comparison operators: <, <=, ==, >= and >. (See the manual for details)

This "three-way comparison operator", also known as the “spaceship operator” (a common name in other languages), works on all standard PHP values. It exists in other languages: Perl, Ruby, and Groovy.

For consistency with Perl, it has the same precedence as == and !=.

It is implemented by using the result of the existing internal compare_function that underlies the other comparison operators. The existing comparison operators could be considered mere shorthands for <=>:

operator <=> equivalent
$a < $b ($a <=> $b) === -1
$a <= $b ($a <=> $b) === -1 || ($a <=> $b) === 0
$a == $b ($a <=> $b) === 0
$a != $b ($a <=> $b) !== 0
$a >= $b ($a <=> $b) === 1 || ($a <=> $b) === 0
$a > $b ($a <=> $b) === 1

Here are some examples of its behaviour:

// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
 
// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
 
// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
 
echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1
 
// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1
 
// Objects
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 0
 
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "c"]; 
echo $a <=> $b; // -1
 
$a = (object) ["a" => "c"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 1
 
$a = (object) ["a" => "b"]; 
$b = (object) ["b" => "b"]; 
echo $a <=> $b; // 0

Usort Example:

if (($handle = fopen("people.csv", "r")) !== FALSE) {
    while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) {
         $data[] = $row;
    }
    fclose($handle);
}
 
// Sort by last name:
usort($data, function ($left, $right) {
     return $left[1] <=> $right[1];
});

Usefulness

It makes writing ordering callbacks for use with usort() easier. Commonly, users write poor or incorrect ordering functions like this one:

function order_func($a, $b) {
    return $a >= $b;
}

When users do write correct ordering functions, they have to be quite verbose:

function order_func($a, $b) {
    return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
}

This becomes particularly bad when sorting by multiple columns lexicographically.

With this operator, you can easily write proper ordering functions, like this one:

function order_func($a, $b) {
    return $a <=> $b;
}

Sorting by multiple columns is simpler now, too:

function order_func($a, $b) {
    return [$a->x, $a->y, $a->foo] <=> [$b->x, $b->y, $b->foo];
}

Or:

function order_func($a, $b) {
    return ($a->$x <=> $b->x) ?: ($a->y <=> $b->y) ?: ($a->foo <=> $b->foo);
}

It is also useful in some other contexts.

Backward Incompatible Changes

This introduces no backwards incompatible changes.

Proposed PHP Version(s)

The next major version of PHP, currently PHP 7.0.

New Constants

A T_SPACESHIP constant for use with ext/tokenizer has been added.

Unaffected PHP Functionality

All existing comparison operators are unaffected by this addition.

Future Scope

None.

Patches and Tests

A patch targeting php-src master is here: https://github.com/php/php-src/pull/1007

There is not yet a language specification patch.

Shafik's original patch against 5.6 was here: https://github.com/dshafik/php-src/compare/add-spaceship-operator

Versions

Version Changed Date
0.2.1 Clarity on type-juggling behaviour and relation to other comparison operators
0.2 Updated, retargeted to PHP 7 by Andrea
0.1 Initial version by Shafik

Votes

An option needs 2/3 votes to win

Accept the Combined Comparison (Spaceship) Operator RFC and merge patch into master? (79.6% approved)
User Vote
aharvey Yes
ajf Yes
auroraeosrose Yes
brandon Yes
bwoebi No
chregu Yes
colder Yes
cyberline Yes
datibbaw No
davey Yes
derick Yes
dm Yes
duodraco Yes
fa No
francois Yes
guilhermeblanco No
gwynne Yes
hywan Yes
irker Yes
jedibc Yes
jgmdev Yes
jwage Yes
kalle Yes
kinncj Yes
klaussilveira No
laruence Yes
lcobucci Yes
leigh No
levim No
lstrojny Yes
malukenho Yes
marco Yes
mbeccati Yes
mfischer Yes
mfonda No
mike Yes
mrook Yes
nikic No
pajoye Yes
pauloelr Yes
ramsey Yes
rdlowrey No
rdohms Yes
salathe Yes
santiagolizardo Yes
seld Yes
sobak No
stas Yes
stelianm Yes
thijs Yes
treffynnon Yes
weierophinney Yes
yohgaki Yes
yunosh Yes