PHP Conference Japan 2024

比较对象

当使用比较运算符(==)时,对象变量以简单的方式进行比较,即:如果两个对象实例具有相同的属性和值(值使用==进行比较),并且是同一类的实例,则它们相等。

当使用恒等运算符(===)时,对象变量仅当它们引用同一类的同一实例时才相同。

一个例子将阐明这些规则。

示例 #1 对象比较示例

<?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
__construct($flag = true) {
$this->flag = $flag;
}
}

class
OtherFlag
{
public
$flag;

function
__construct($flag = true) {
$this->flag = $flag;
}
}

$o = new Flag();
$p = new Flag();
$q = $o;
$r = new OtherFlag();

echo
"两个相同类的实例\n";
compareObjects($o, $p);

echo
"\n两个指向同一实例的引用\n";
compareObjects($o, $q);

echo
"\n两个不同类的实例\n";
compareObjects($o, $r);
?>

以上示例将输出

Two instances of the same class
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : FALSE
o1 !== o2 : TRUE

Two references to the same instance
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : TRUE
o1 !== o2 : FALSE

Instances of two different classes
o1 == o2 : FALSE
o1 != o2 : TRUE
o1 === o2 : FALSE
o1 !== o2 : TRUE

注意:

扩展可以为其对象比较(==)定义自己的规则。

添加注释

用户贡献的注释 11 条注释

jazfresh at hotmail.com
17 年前
请注意,在比较对象属性时,比较是递归的(至少在 PHP 5.2 中是这样)。也就是说,如果 $a->x 包含一个对象,那么它将以相同的方式与 $b->x 进行比较。请注意,这可能导致递归错误
<?php
class Foo {
public
$x;
}
$a = new Foo();
$b = new Foo();
$a->x = $b;
$b->x = $a;

print_r($a == $b);
?>
结果为
PHP 致命错误:嵌套级别过深 - 递归依赖?在 test.php 的第 11 行
匿名
14 年前
应该记录使用 <> 运算符的比较。在两个对象之间,至少在 PHP5.3 中,比较操作会停止并在找到第一个不相等的属性时返回。

<?php

$o1
= new stdClass();
$o1->prop1 = 'c';
$o1->prop2 = 25;
$o1->prop3 = 201;
$o1->prop4 = 1000;

$o2 = new stdClass();
$o2->prop1 = 'c';
$o2->prop2 = 25;
$o2->prop3 = 200;
$o2->prop4 = 9999;

echo (int)(
$o1 < $o2); // 0
echo (int)($o1 > $o2); // 1

$o1->prop3 = 200;

echo (int)(
$o1 < $o2); // 1
echo (int)($o1 > $o2); // 0

?>
rnealxp at yahoo dot com
7 年前
这三个函数自身递归调用,并处理数组/对象/值的任何嵌套级别,并进行严格比较。此函数集的入口点将是“valuesAreIdentical”。

<?php
function valuesAreIdentical($v1, $v2): bool {
$type1 = gettype($v1);
$type2 = gettype($v2);

if(
$type1 !== $type2){
return
false;
}

switch(
true){
case (
$type1==='boolean' || $type1==='integer' || $type1==='double' || $type1==='string'):
//在此处进行严格比较。
if($v1 !== $v2){
return
false;
}
break;

case (
$type1==='array'):
$bool = arraysAreIdentical($v1, $v2);
if(
$bool===false){
return
false;
}
break;

case
'object':
$bool = objectsAreIdentical($v1,$v2);
if(
$bool===false){
return
false;
}
break;

case
'NULL':
//由于两种类型都是 NULL 类型,因此认为它们的“值”相等。
break;

case
'resource':
//是否以及如何进行比较?
break;

case
'unknown type':
//是否以及如何进行比较?
break;
}
//end switch

//所有测试都通过了。
return true;
}

function
objectsAreIdentical($o1, $o2): bool {
//查看松散比较是否通过。
if($o1 != $o2){
return
false;
}

//现在进行更严格的比较。
$objReflection1 = new ReflectionObject($o1);
$objReflection2 = new ReflectionObject($o2);

$arrProperties1 = $objReflection1->getProperties(ReflectionProperty::IS_PUBLIC);
$arrProperties2 = $objReflection2->getProperties(ReflectionProperty::IS_PUBLIC);

$bool = arraysAreIdentical($arrProperties1, $arrProperties2);
if(
$bool===false){
return
false;
}

foreach(
$arrProperties1 as $key=>$propName){
$bool = valuesAreIdentical($o1->$propName, $o2->$propName);
if(
$bool===false){
return
false;
}
}

//所有测试都通过了。
return true;
}

function
arraysAreIdentical(array $arr1, array $arr2): bool {
$count = count($arr1);

//要求它们具有相同的大小。
if(count($arr2) !== $count){
return
false;
}

//要求它们具有相同的键。
$arrKeysInCommon = array_intersect_key($arr1, $arr2);
if(
count($arrKeysInCommon)!== $count){
return
false;
}

//要求它们的键顺序相同。
$arrKeys1 = array_keys($arr1);
$arrKeys2 = array_keys($arr2);
foreach(
$arrKeys1 as $key=>$val){
if(
$arrKeys1[$key] !== $arrKeys2[$key]){
return
false;
}
}

//它们确实具有相同的键,并且顺序相同。
foreach($arr1 as $key=>$val){
$bool = valuesAreIdentical($arr1[$key], $arr2[$key]);
if(
$bool===false){
return
false;
}
}

//所有测试都通过了。
return true;
}
?>
rnealxp at yahoo dot com
4 年前
请使用此更正后的 "valuesAreIdentical" 函数版本,而不是我之前发布的版本(依赖项在之前的帖子中找到);如果管理员可以替换函数代码片段,那就太好了/谢谢,否则,我表示歉意。
<?php
public static function valuesAreIdentical($v1, $v2):bool{
$type1 = gettype($v1);
$type2 = gettype($v2);
switch(
true){
case (
$type1 !== $type2):
return
false;
case (
$type1==='boolean' || $type1==='integer' || $type1==='double' || $type1==='string'):
//在此处进行严格比较。
return ($v1===$v2);
case (
$type1==='array'):
return
self::arraysAreIdentical($v1, $v2);
case (
$type1==='object'):
return
self::objectsAreIdentical($v1,$v2);
case (
$type1==='NULL'):
//由于两种类型都是 NULL 类型,因此认为它们的“值”相等。
return true;
case (
$type1==='resource' || $type1==='unknown type'):
//是否以及如何进行比较?
return true;
default:
return
true; //代码流程不应到达此处。
} //end switch
}
?>
cpmjr1 at gmail dot com
1 年前
根据文档,这一点并不立即显而易见,但相等比较运算符也检查受保护和私有属性。

示例
<?php
A { public $a = 0; private $b = 1; public function __construct($test) {$this->b = $test;}}
echo
"A(1) == A(2) " . var_export((new A(1)) == (new A(2)), true) . "\n";
echo
"A(1) == A(1) " . var_export((new A(1)) == (new A(1)), true) . "\n";
?>
输出
A(1) == A(2) false
A(1) == A(1) true
nhuhoai
10年前
对于比较类中的两个对象,您可以使用这样的接口并为每个类自定义函数

<?php
接口 EQU {
public static function
compare( EQU $me, EQU $you );
public function
equals( EQU $you );
}
?>

如果您有一个超类,您可以创建通用函数(不安全,但适用于不复杂的类)

<?php
抽象类 SuperClass {
public function
__construct( ) {
// 做你需要做的事情
}
public static function
compare( $obj1, $obj2 ) {
return
serialize( $obj1 ) == serialize( $obj2 );
}
public function
equals( $obj ) {
return static::
compare( $this, $obj );
}
}
?>
rune at zedeler dot dk
17 年前
糟糕,显然我还没有很好地检查下面的数组部分。
忘记测试数组是否具有相同的长度,并且有一些括号错位。
这个应该工作得更好:+)

<?
函数 deepCompare($a,$b) {
如果(is_object($a) && is_object($b)) {
如果(get_class($a)!=get_class($b))
返回false;
foreach($a as $key => $val) {
如果(!deepCompare($val,$b->$key))
返回false;
}
返回true;
}
否则如果(is_array($a) && is_array($b)) {
当(!is_null(key($a)) && !is_null(key($b))) {
如果 (key($a)!==key($b) || !deepCompare(current($a),current($b)))
返回false;
next($a); next($b);
}
返回 is_null(key($a)) && is_null(key($b));
}
否则
返回 $a===$b;
}
?>
wbcarts at juno dot com
16年前
使用PHP的usort()方法比较对象。

PHP和MySQL都提供了对数据进行排序的方法,如果可能,最好使用这些方法。但是,由于本节内容是关于比较您自己的PHP对象(以及您可能需要更改PHP中的排序方法),以下是如何使用PHP的“用户定义”排序方法usort()和您自己的类compare()方法来实现此目的。

<?php

/*
* Employee.php
*
* 此类定义了一个compare()方法,它告诉PHP此对象的排序规则
* 对于此对象 - 即按emp_id排序。
*
*/
Employee
{
public
$first;
public
$last;
public
$emp_id; // 我们感兴趣的属性...

public function __construct($emp_first, $emp_last, $emp_ID)
{
$this->first = $emp_first;
$this->last = $emp_last;
$this->emp_id = $emp_ID;
}

/*
* 定义此对象的排序规则 - 使用emp_id。
* 确保此函数返回-1、0或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]";
}
}

# 创建一个PHP数组并使用Employee对象对其进行初始化。
$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)
);

# 使用Employee compare()方法对$employees数组进行排序。
usort($employees, array("Employee", "compare"));

# 打印结果
foreach($employees as $employee)
{
echo
$employee . '<br>';
}
?>

结果现在按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]

重要说明:您的PHP代码永远不会直接调用Employee的compare()方法,但PHP的usort()会多次调用它。此外,在定义排序规则时,请确保达到“基本类型”级别...即降至数字或字符串,并且该函数返回-1、0或1,以获得可靠且一致的结果。

另请参阅:https://php.net/manual/en/function.usort.php以获取更多PHP排序功能的示例。
cross+php at distal dot com
16年前
针对“rune at zedeler dot dk”关于类内容相等的评论,我遇到了类似的问题。我想使用sort()对对象数组进行排序。

我知道我可以使用usort()来做到这一点,但我习惯了C++,在那里您可以定义允许比较的操作符。我在zend源代码中看到它调用了一个compare_objects函数,但我没有看到任何为对象实现该函数的方法。是否必须扩展才能提供该接口?

如果是这样,我想建议您允许在PHP的类定义中定义等价和/或比较操作。然后,rune和我想做的事情会变得容易得多。
rune at zedeler dot dk
17 年前
我还没有找到检查两个对象是否相同的内置函数 - 即所有字段都相同。
换句话说,

<?
类 A {
var $x;
函数 __construct($x) { $this->x = $x; }

}
$identical1 = new A(42);
$identical2 = new A(42);
$different = new A('42');
?>

使用“==”比较对象将声称这三个对象都相等。使用“===”比较将声称所有对象都不相等。
我还没有找到检查这两个相同对象是否
相同的内置函数,但与不同的对象不同。

以下函数执行此操作

<?
函数 deepCompare($a,$b) {
如果(is_object($a) && is_object($b)) {
如果(get_class($a)!=get_class($b))
返回false;
foreach($a as $key => $val) {
如果(!deepCompare($val,$b->$key))
返回false;
}
返回true;
}
否则如果(is_array($a) && is_array($b)) {
当 (!is_null(key($a)) 并且 !is_null(key($b))) 时 {
如果 (key($a)!==key($b) || !deepCompare(current($a),current($b)))
返回false;
next($a); next($b);
}
返回true;
}
否则
返回 $a===$b;
}
?>
Hayley Watson
16年前
这一点之前已经提到过(参见 jazfresh at hotmail.com 的备注),但这里再次详细说明,因为对于对象来说,== 和 === 之间的区别非常重要。

对象之间的松散比较 (==) 是递归的:如果正在比较的两个对象的属性本身也是对象,那么这些属性也将使用 == 进行比较。

<?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
"比较 Leaf 对象等价性:\$leaf1==\$leaf2 吗?", ($leaf1 == $leaf2 ? "是" : "否"), "\n";
echo
"比较 Leaf 对象同一性:\$leaf1===\$leaf2 吗?", ($leaf1 === $leaf2 ? "是" : "否"), "\n";
echo
"\n";
echo
"比较 Link 对象等价性:\$link1==\$link2 吗?",($link1 == $link2 ? "是" : "否"), "\n";
echo
"比较 Link 对象同一性:\$link1===\$link2 吗?", ($link1 === $link2 ? "是" : "否"), "\n";
?>

即使 $link1 和 $link2 包含不同的 Leaf 对象,它们仍然是等价的,因为 Leaf 对象本身是等价的。

实际的结果是,在更适合使用 "===" 的情况下使用 "==" 会导致严重的性能损失,特别是如果对象很大且/或复杂时。事实上,如果对象之间或(递归地)它们的任何属性之间存在任何循环关系,则由于隐含的无限循环,可能会导致致命错误。

<?php
class Foo { public $foo; }
$t = new Foo; $t->foo = $t;
$g = new Foo; $g->foo = $g;

echo
"严格同一性:", ($t===$g ? "True" : "False"),"\n";
echo
"松散等价性:", ($t==$g ? "True" : "False"), "\n";
?>

因此,应优先使用 "===" 而不是 "==" 来比较对象;如果要比较两个不同的对象的等价性,请尝试通过检查合适的单个属性来进行。(也许 PHP 可以获得一个魔法 "__equals" 方法,用于评估 "=="?:) )
To Top