This has already been mentioned (see jazfresh at hotmail.com's note), but here it is again in more detail because for objects the difference between == and === is significant.
Loose equality (==) over objects is recursive: if the properties of the two objects being compared are themselves objects, then those properties will also be compared using ==.
<?php
class Link
{
public $link; function __construct($link) { $this->link = $link; }
}
class Leaf
{
public $leaf; function __construct($leaf) { $this->leaf = $leaf; }
}
$leaf1 = new Leaf(42);
$leaf2 = new Leaf(42);
$link1 = new Link($leaf1);
$link2 = new Link($leaf2);
echo "Comparing Leaf object equivalence: is \$leaf1==\$leaf2? ", ($leaf1 == $leaf2 ? "Yes" : "No"), "\n";
echo "Comparing Leaf object identity: is \$leaf1===\$leaf2? ", ($leaf1 === $leaf2 ? "Yes" : "No"), "\n";
echo "\n";
echo "Comparing Link object equivalence: is \$link1==\$link2? ",($link1 == $link2 ? "Yes" : "No"), "\n";
echo "Comparing Link object identity: is \$link1===\$link2? ", ($link1 === $link2 ? "Yes" : "No"), "\n";
?>
Even though $link1 and $link2 contain different Leaf objects, they are still equivalent because the Leaf objects are themselves equivalent.
The practical upshot is that using "==" when "===" would be more appropriate can result in a severe performance penalty, especially if the objects are large and/or complex. In fact, if there are any circular relationships involved between the objects or (recursively) any of their properties, then a fatal error can result because of the implied infinite loop.
<?php
class Foo { public $foo; }
$t = new Foo; $t->foo = $t;
$g = new Foo; $g->foo = $g;
echo "Strict identity: ", ($t===$g ? "True" : "False"),"\n";
echo "Loose equivalence: ", ($t==$g ? "True" : "False"), "\n";
?>
So preference should be given to comparing objects with "===" rather than "=="; if two distinct objects are to be compared for equivalence, try to do so by examining suitable individual properties. (Maybe PHP could get a magic "__equals" method that gets used to evaluate "=="? :) )
オブジェクトの比較
PHP 5では、オブジェクトの比較は、オブジェクト指向言語に期待される動作に対応し、 PHP 4よりも複雑になっています。(ただし、PHP 5はオブジェクト指向言語ではありません。)
比較演算子(==)を使用する際、 オブジェクト変数は、単純に比較されます。つまり、 二つのオブジェクトのインスタンスは、 同じ属性と値を有し、同じクラスのインスタンスである場合に、 等しいとされます。
一方、一致演算子(===)を使用する場合、 オブジェクト変数は、同じクラスの同じインスタンスを参照する場合のみ、 等しいとされます。
これらのルールを明確に示す例を以下に示します。
例1 PHP 5におけるオブジェクト比較の例
<?php
function bool2str($bool)
{
if ($bool === false) {
return 'FALSE';
} else {
return 'TRUE';
}
}
function compareObjects(&$o1, &$o2)
{
echo 'o1 == o2 : ' . bool2str($o1 == $o2) . "\n";
echo 'o1 != o2 : ' . bool2str($o1 != $o2) . "\n";
echo 'o1 === o2 : ' . bool2str($o1 === $o2) . "\n";
echo 'o1 !== o2 : ' . bool2str($o1 !== $o2) . "\n";
}
class Flag
{
public $flag;
function Flag($flag = true) {
$this->flag = $flag;
}
}
class OtherFlag
{
public $flag;
function OtherFlag($flag = true) {
$this->flag = $flag;
}
}
$o = new Flag();
$p = new Flag();
$q = $o;
$r = new OtherFlag();
echo "同一クラスの2つのインスタンス\n";
compareObjects($o, $p);
echo "\n同じインスタンスへの2つのリファレンス\n";
compareObjects($o, $q);
echo "\n2つの異なるクラスのインスタンス\n";
compareObjects($o, $r);
?>
上の例の出力は以下となります。
同一クラスの2つのインスタンス
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : FALSE
o1 !== o2 : TRUE
同じインスタンスへの2つのリファレンス
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : TRUE
o1 !== o2 : FALSE
2つの異なるクラスのインスタンス
o1 == o2 : FALSE
o1 != o2 : TRUE
o1 === o2 : FALSE
o1 !== o2 : TRUE
注意: 拡張モジュール内では、自前で作成したオブジェクトの比較方法を独自に定義することができます。
オブジェクトの比較
Hayley Watson
17-Sep-2008 08:33
17-Sep-2008 08:33
wbcarts at juno dot com
09-Sep-2008 06:36
09-Sep-2008 06:36
COMPARING OBJECTS using PHP's usort() method.
PHP and MySQL both provide ways to sort your data already, and it is a good idea to use that if possible. However, since this section is on comparing your own PHP objects (and that you may need to alter the sorting method in PHP), here is an example of how you can do that using PHP's "user-defined" sort method, usort() and your own class compare() methods.
<?php
/*
* Employee.php
*
* This class defines a compare() method, which tells PHP the sorting rules
* for this object - which is to sort by emp_id.
*
*/
class Employee
{
public $first;
public $last;
public $emp_id; // the property we're interested in...
public function __construct($emp_first, $emp_last, $emp_ID)
{
$this->first = $emp_first;
$this->last = $emp_last;
$this->emp_id = $emp_ID;
}
/*
* define the rules for sorting this object - using emp_id.
* Make sure this function returns a -1, 0, or 1.
*/
public static function compare($a, $b)
{
if ($a->emp_id < $b->emp_id) return -1;
else if($a->emp_id == $b->emp_id) return 0;
else return 1;
}
public function __toString()
{
return "Employee[first=$this->first, last=$this->last, emp_id=$this->emp_id]";
}
}
# create a PHP array and initialize it with Employee objects.
$employees = array(
new Employee("John", "Smith", 345),
new Employee("Jane", "Doe", 231),
new Employee("Mike", "Barnes", 522),
new Employee("Vicky", "Jones", 107),
new Employee("John", "Doe", 2),
new Employee("Kevin", "Patterson", 89)
);
# sort the $employees array using Employee compare() method.
usort($employees, array("Employee", "compare"));
# print the results
foreach($employees as $employee)
{
echo $employee . '<br>';
}
?>
Results are now sorted by emp_id:
Employee[first=John, last=Doe, emp_id=2]
Employee[first=Kevin, last=Patterson, emp_id=89]
Employee[first=Vicky, last=Jones, emp_id=107]
Employee[first=Jane, last=Doe, emp_id=231]
Employee[first=John, last=Smith, emp_id=345]
Employee[first=Mike, last=Barnes, emp_id=522]
Important Note: Your PHP code will never directly call the Employee's compare() method, but PHP's usort() calls it many many times. Also, when defining the rules for sorting, make sure to get to a "primitive type" level... that is, down to a number or string, and that the function returns a -1, 0, or 1, for reliable and consistent results.
Also see: http://www.php.net/manual/en/function.usort.php for more examples of PHP's sorting facilities.
wbcarts at juno dot com
07-Sep-2008 07:02
07-Sep-2008 07:02
COMPARISONS AND EQUALITY are NOT the same
I'm not sure that the PHP Example #1 above is clear enough. In my own experience, I have found there is a distinct difference between a "comparison" and a "test for equality". The difference is found in the possible return values of the function being used, for example.
/*
* Test two values for EQUALITY - returns (boolean) TRUE or FALSE.
*/
function equals($a, $b)
{
return ($a == $b);
}
/*
* COMPARE two values - returns (int) -1, 0, or 1.
*/
function compare($a, $b)
{
if($a < $b) return -1;
else if($a == $b) return 0;
else if($a > $b) return 1;
else return -1;
}
My examples clarify the difference between "making a comparison" and "testing for equality". You can substitute any of the "==" with "===" for example, but the point is on the possible return values of the function. All tests for EQUALITY will return TRUE or FALSE, and a COMPARISON will give a "<", "==", or ">" answers... which you can then use for sorting.
cross+php at distal dot com
06-Mar-2008 03:50
06-Mar-2008 03:50
In response to "rune at zedeler dot dk"s comment about class contents being equal, I have a similar issue. I want to sort an array of objects using sort().
I know I can do it with usort(), but I'm used to C++ where you can define operators that allow comparison. I see in the zend source code that it calls a compare_objects function, but I don't see any way to implement that function for an object. Would it have to be an extension to provide that interface?
If so, I'd like to suggest that you allow equivalence and/or comparison operations to be defined in a class definition in PHP. Then, the sorts of things rune and I want to do would be much easier.
dionyziz at deviantart dot com
11-Mar-2007 02:20
11-Mar-2007 02:20
Note that classes deriving from the same parent aren't considered equal when comparing even using ==; they should also be objects of the same child class.
<?php
class Mom {
private $mAttribute;
public function Mom( $attribute ) {
$this->mAttribute = $attribute;
}
public function Attribute() {
return $this->mAttribute;
}
}
final class Sister extends Mom {
public function Sister( $attribute ) {
$this->Mom( $attribute );
}
}
final class Brother extends Mom {
public function Brother( $attribute ) {
$this->Mom( $attribute );
}
}
$sister = new Sister( 5 );
$brother = new Brother( 5 );
assert( $sister == $brother ); // will FAIL!
?>
This assertion will fail, because sister and brother are not of the same child class!
If you want to compare based on the parent class object type only, you might have to define a function for comparisons like these, and use it instead of the == operator:
<?php
function SiblingsEqual( $a, $b ) {
if ( !( $a instanceof Mom ) ) {
return false;
}
if ( !( $b instanceof Mom ) ) {
return false;
}
if ( $a->Attribute() != $b->Attribute() ) {
return false;
}
return true;
}
assert( SiblingsEqual( $sister, $brother ) ); // will succeed
?>
rune at zedeler dot dk
01-Mar-2007 03:34
01-Mar-2007 03:34
Whoops, apparently I hadn't checked the array-part of the below very well.
Forgot to test if the arrays had same length, and had some misaligned parenthesis.
This one should work better :+)
<?
function deepCompare($a,$b) {
if(is_object($a) && is_object($b)) {
if(get_class($a)!=get_class($b))
return false;
foreach($a as $key => $val) {
if(!deepCompare($val,$b->$key))
return false;
}
return true;
}
else if(is_array($a) && is_array($b)) {
while(!is_null(key($a)) && !is_null(key($b))) {
if (key($a)!==key($b) || !deepCompare(current($a),current($b)))
return false;
next($a); next($b);
}
return is_null(key($a)) && is_null(key($b));
}
else
return $a===$b;
}
?>
rune at zedeler dot dk
28-Feb-2007 03:27
28-Feb-2007 03:27
I haven't found a build-in function to check whether two obects are identical - that is, all their fields are identical.
In other words,
<?
class A {
var $x;
function __construct($x) { $this->x = $x; }
}
$identical1 = new A(42);
$identical2 = new A(42);
$different = new A('42');
?>
Comparing the objects with "==" will claim that all three of them are equal. Comparing with "===" will claim that all are un-equal.
I have found no build-in function to check that the two identicals are
identical, but not identical to the different.
The following function does that:
<?
function deepCompare($a,$b) {
if(is_object($a) && is_object($b)) {
if(get_class($a)!=get_class($b))
return false;
foreach($a as $key => $val) {
if(!deepCompare($val,$b->$key))
return false;
}
return true;
}
else if(is_array($a) && is_array($b)) {
while(!is_null(key($a) && !is_null(key($b)))) {
if (key($a)!==key($b) || !deepCompare(current($a),current($b)))
return false;
next($a); next($b);
}
return true;
}
else
return $a===$b;
}
?>
jazfresh at hotmail.com
08-Dec-2006 09:36
08-Dec-2006 09:36
Note that when comparing object attributes, the comparison is recursive (at least, it is with PHP 5.2). That is, if $a->x contains an object then that will be compared with $b->x in the same manner. Be aware that this can lead to recursion errors:
<?php
class Foo {
public $x;
}
$a = new Foo();
$b = new Foo();
$a->x = $b;
$b->x = $a;
print_r($a == $b);
?>
Results in:
PHP Fatal error: Nesting level too deep - recursive dependency? in test.php on line 11
