2024 日本 PHP 研討會

比較物件

使用比較運算子 (==) 時,物件變數的比較方式很簡單,也就是:兩個物件實例相等,如果它們具有相同的屬性和值(值使用 == 進行比較),並且是同一類別的實例。

使用識別運算子 (===) 時,物件變數相同,若且唯若它們指向同一類別的同一個實例。

以下範例將闡明這些規則。

範例 #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
"Two instances of the same class\n";
compareObjects($o, $p);

echo
"\nTwo references to the same instance\n";
compareObjects($o, $q);

echo
"\nInstances of two different classes\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 年前
應該要記錄使用 <> 運算子進行比較的說明。在兩個物件之間,至少在 PHP 5.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'):
//Do strict comparison here.
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':
//Since both types were of type NULL, consider their "values" equal.
break;

case
'resource':
//How to compare if at all?
break;

case
'unknown type':
//How to compare if at all?
break;
}
//end switch

//All tests passed.
return true;
}

function
objectsAreIdentical($o1, $o2): bool {
//See if loose comparison passes.
if($o1 != $o2){
return
false;
}

//Now do strict(er) comparison.
$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;
}
}

//All tests passed.
return true;
}

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

//Require that they have the same size.
if(count($arr2) !== $count){
return
false;
}

//Require that they have the same keys.
$arrKeysInCommon = array_intersect_key($arr1, $arr2);
if(
count($arrKeysInCommon)!== $count){
return
false;
}

//Require that their keys be in the same order.
$arrKeys1 = array_keys($arr1);
$arrKeys2 = array_keys($arr2);
foreach(
$arrKeys1 as $key=>$val){
if(
$arrKeys1[$key] !== $arrKeys2[$key]){
return
false;
}
}

//They do have same keys and in same order.
foreach($arr1 as $key=>$val){
$bool = valuesAreIdentical($arr1[$key], $arr2[$key]);
if(
$bool===false){
return
false;
}
}

//All tests passed.
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; //程式碼流程不應執行到這裡。
} //switch 結束
}
?>
cpmjr1 at gmail dot com
1 年前
根據文件並非立即顯而易見,但等式比較運算子也會檢查受保護和私有的屬性。

範例
<?php
類別 A { 公開 $a = 0; 私有 $b = 1; 公開 函數 __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 {
公開 靜態 函數
compare( EQU $me, EQU $you );
公開 函數
equals( EQU $you );
}
?>

如果您有一個父類別,您可以建立泛型函數(不安全,但在不複雜的類別中可以使用)

<?php
抽象類別 SuperClass {
公開 函數
__construct( ) {
// 做您需要做的事情
}
公開 靜態 函數
compare( $obj1, $obj2 ) {
return
serialize( $obj1 ) == serialize( $obj2 );
}
公開 函數
equals( $obj ) {
return static::
compare( $this, $obj );
}
}
?>
rune at zedeler dot dk
17 年前
哎呀,顯然我沒有很好地檢查以下程式碼的陣列部分。
忘記測試陣列是否具有相同的長度,並且有一些括號位置錯誤。
這個應該可以更好地運作 :+)

<?
函數 deepCompare($a,$b) {
如果(是物件($a) && 是物件($b)) {
如果(取得類別($a)!=取得類別($b))
返回 false;
foreach($a as $key => $val) {
如果(!deepCompare($val,$b->$key))
返回 false;
}
返回 true;
}
否則如果(是陣列($a) && 是陣列($b)) {
當(!是空值(鍵($a)) && !是空值(鍵($b))) {
如果 (鍵($a)!==鍵($b) || !deepCompare(目前值($a),目前值($b)))
返回 false;
下一個($a); 下一個($b);
}
返回 是空值(鍵($a)) && 是空值(鍵($b));
}
否則
return $a===$b;
}
?>
wbcarts at juno dot com
16 年前
使用 PHP 的 usort() 方法比較物件。

PHP 和 MySQL 都提供了排序資料的方法,如果可能的話,最好使用這些方法。然而,由於本節討論的是比較您自己的 PHP 物件(而且您可能需要更改 PHP 中的排序方法),以下是如何使用 PHP 的「使用者定義」排序方法 usort() 和您自己的類別 compare() 方法來完成此操作的範例。

<?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>';
}
?>

結果現在按 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.dev.org.tw/manual/en/function.usort.php 以取得更多 PHP 排序功能的範例。
cross+php at distal dot com
16 年前
針對「rune at zedeler dot dk」關於類別內容相等的評論,我遇到了類似的問題。我想使用 sort() 對物件陣列進行排序。

我知道可以用 usort() 來做,但我習慣於 C++,在 C++ 中可以定義允許比較的運算子。我在 Zend 原始碼中看到它呼叫了一個 compare_objects 函式,但我沒有看到任何為物件實作該函式的方法。是否必須透過擴充功能來提供該介面?

如果是這樣,我建議您允許在 PHP 的類別定義中定義等價和/或比較運算。那麼,rune 和我想做的事情就會容易得多。
rune at zedeler dot dk
17 年前
我沒有找到內建函式來檢查兩個物件是否相同——也就是說,它們的所有欄位都相同。
換句話說,

<?
class A {
var $x;
function __construct($x) { $this->x = $x; }

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

使用 "==" 比較物件會聲稱它們三個都相等。使用 "===" 比較會聲稱它們都不相等。
我沒有找到內建函式來檢查兩個相同的物件是
相同的,但與不同的物件不相同。

以下函式可以做到這一點

<?
函數 deepCompare($a,$b) {
如果(是物件($a) && 是物件($b)) {
如果(取得類別($a)!=取得類別($b))
返回 false;
foreach($a as $key => $val) {
如果(!deepCompare($val,$b->$key))
返回 false;
}
返回 true;
}
否則如果(是陣列($a) && 是陣列($b)) {
while(!is_null(key($a) && !is_null(key($b)))) {
如果 (鍵($a)!==鍵($b) || !deepCompare(目前值($a),目前值($b)))
返回 false;
下一個($a); 下一個($b);
}
返回 true;
}
否則
return $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
類別 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