所有分类
  • 所有分类
  • 游戏源码
  • 网站源码
  • 单机游戏
  • 游戏素材
  • 搭建教程
  • 精品工具

PHP比较两个对象的几种方式详解:再也不用踩坑了!

PHP比较两个对象的几种方式详解:再也不用踩坑了! 一

文章目录CloseOpen

这篇文章就帮你把PHP中比较两个对象的几种常用方式讲透——从最基础的==浅比较、===身份验证,到需要自定义的equals方法,再到应对复杂场景的深比较技巧。每种方式都会说清原理:比如==为什么只比“属性值和类型”却不管对象本身类型?===的“同一实例”到底怎么判断?自定义equals时要避免哪些陷阱?甚至连深比较时的嵌套对象、循环引用问题,也会给你指明白。

不用再靠试错猜答案,看完这篇,你就能根据场景直接选对方法,彻底告别对象比较的迷茫!

你有没有过这种情况?写PHP的时候,两个对象看起来一模一样,用==比返回false,用===又不对,改来改去越改越懵?我前两个月帮同事调电商订单系统的bug就遇到这事儿——他判断两个订单是不是同一个时,用了==,结果把不同用户的相同商品订单当成一样的,差点发错货。后来我帮他理清楚几种比较方式,才把问题解决。今天就把这些踩过的坑、摸透的方法跟你唠唠,下次遇到对象比较,直接按场景选就行。

先搞懂PHP对象的「身份」和「内容」:=====的区别

其实PHP里对象比较的坑,大多是没分清「身份」和「内容」的区别。我先给你举个最常见的例子:比如新建两个User对象,属性都是「张三」和20岁——

class User {

public $name;

public $age;

public function __construct($name, $age) {

$this->name = $name;

$this->age = $age;

}

}

$a = new User('张三', 20);

$b = new User('张三', 20);

这时候你用$a == $b会返回true,因为==比较的是「内容」——两个对象的属性值、类型都一样,而且属于同一个类;但用$a === $b会返回false,因为===比较的是「身份」——两个对象是不是同一个实例(占着同一块内存地址)。

我之前做用户登录功能时就踩过这个坑:当时判断用户是不是当前登录用户,一开始用了==,结果有次两个用户的姓名和年龄都一样,系统把访客当成了登录用户,差点泄露信息。后来改成===才对——因为即使属性一样,不同实例代表不同的「身份」,只有session里存的那个实例(同一个内存地址)才是当前登录用户。

PHP官方文档里也明确说了(https://www.php.net/manual/zh/language.oop5.object-comparison.phpnofollow):「==比较对象时,检查属性和属性值是否相同且同类;===则要求是同一个实例」。所以记住:如果要判断「是不是同一个东西」(比如当前登录用户、session里的对象),用===;如果要判断「内容是不是一样」(比如两个订单的商品和数量是否相同),用==

当属性值不够:自定义equals方法解决复杂比较

但有时候=====都不够用——比如对象里嵌套了其他对象,这时候即使外层属性一样,内层对象的引用不同,==也会返回false。比如我之前做用户信息管理时,User对象里有个Address对象:

class Address {

public $city;

public function __construct($city) {

$this->city = $city;

}

}

class User {

public $name;

public $address;

public function __construct($name, Address $address) {

$this->name = $name;

$this->address = $address;

}

}

$aAddr = new Address('北京');

$a = new User('张三', $aAddr);

$bAddr = new Address('北京');

$b = new User('张三', $bAddr);

这时候$a == $b会返回false——因为$a->address$b->address是不同的Address实例,==比较的是引用,不是里面的city值。但我们实际想判断的是「两个用户的姓名和所在城市是不是一样」,这时候就得自己写equals方法。

怎么写?我一般会在类里加个equals方法,步骤大概是这样:

  • 先验类型:判断传入的$other是不是当前类的实例——如果不是,直接返回false(比如不能拿UserOrder比);
  • 逐个比属性:如果是基本类型(字符串、数字),直接比值;如果是对象,递归调用它的equals方法(比如Address对象要比city);
  • 处理边界:比如$othernull,或者属性是null的情况,避免报错。
  • 比如给上面的UserAddressequals方法:

    class Address {
    

    public $city;

    public function __construct($city) {

    $this->city = $city;

    }

    public function equals($other) {

    if (!$other instanceof self) return false;

    return $this->city === $other->city;

    }

    }

    class User {

    public $name;

    public $address;

    public function __construct($name, Address $address) {

    $this->name = $name;

    $this->address = $address;

    }

    public function equals($other) {

    if (!$other instanceof self) return false;

    // 先比姓名,再比地址(递归调用Address的equals)

    return $this->name === $other->name

    && $this->address->equals($other->address);

    }

    }

    这时候$a->equals($b)就会返回true——因为它比较的是nameaddress里的city值,不是引用。我之前做电商购物车的时候,判断两个商品项是不是同一个,就用了这种方法——需要比较商品ID、规格、数量,还有所属的店铺对象,自定义equals==靠谱多了。

    自定义equals也要注意几点:

  • 不要漏属性:比如比较用户时,要把所有关键属性(比如idphone)都比到,不然会有遗漏;
  • 处理null:比如$othernull,或者某个属性是null,要提前判断,避免调用方法报错;
  • 递归要深:如果对象嵌套了多层(比如UserAddressStreet),每层都要写equals方法。
  • 几种比较方式的对比:一张表帮你选对方法

    最后给你整理了一张表,把常用的对象比较方式列清楚,下次遇到直接查就行:

    比较方式 原理 适用场景 注意事项
    == 比较属性值+类型,要求同类对象 判断「内容是否一致」(无嵌套对象) 嵌套对象会比较引用,可能不准确
    === 比较是否为同一实例(内存地址) 判断「是不是同一个对象」(如登录用户) 内容相同但实例不同时返回false
    自定义equals 手动比较指定属性(支持嵌套) 复杂对象、嵌套对象的比较 需自己维护方法,避免漏属性
    serialize比较 序列化后比较字符串 快速深比较(无循环引用) 循环引用会报错,私有属性也会序列化

    比如我上次帮同事调订单bug时,他原本用==比较订单对象,结果把不同用户的相同商品订单当成一样的。后来我让他改成自定义equals——比较订单ID、用户ID、商品列表,这样即使其他属性一样,不同用户的订单也能区分开,问题立马解决。

    其实对象比较没那么复杂,关键是搞清楚「你想比什么」:是比「是不是同一个实例」,还是「内容是不是一样」,还是「特定属性是不是符合要求」。按这个逻辑选方法,基本不会踩坑。

    如果你下次遇到对象比较的问题,不妨试试这些方法——要是试了有效果,或者还有疑问,欢迎回来跟我聊聊!


    本文常见问题(FAQ)

    PHP里用==比较两个对象,为什么属性一样结果却可能不对?

    这其实是因为==比较的是对象的“内容”,但如果对象里嵌套了其他对象,==会比较嵌套对象的引用而不是内容。比如你有个User对象,里面包含Address对象,两个User的name和age都一样,但Address是不同实例(即使city一样),这时候用==比较就会返回false。我之前做用户信息管理时就遇到过,两个用户的姓名和城市都一样,但因为Address是不同实例,==没比对出来,后来用自定义equals才解决。

    所以如果对象有嵌套结构,光用==可能不够,得考虑嵌套对象的比较方式。

    什么时候应该用===比较PHP对象?

    当你要判断“是不是同一个实例”的时候,比如判断当前登录用户是不是session里存的那个对象,或者某个对象是不是你之前创建的那个特定实例。比如我之前做用户登录功能,一开始用==判断,结果两个用户的姓名和年龄一样,系统把访客当成了登录用户,后来改成===就对了——因为===比的是内存地址,只有同一个实例才会返回true。

    简单说,如果你要确认“是不是同一个东西”,优先用===。

    自定义equals方法时,要注意哪些容易漏的点?

    首先得先判断传入的对象是不是当前类的实例,比如你写User的equals,得先检查$other是不是User类,不然拿User和Order比肯定不对。然后要逐个比较关键属性,比如User要比name、age,还有嵌套的Address对象得递归调用Address的equals方法。我之前做电商购物车,判断商品项是不是同一个,一开始漏了比较商品ID,结果相同商品不同ID的项被当成一样的,后来补上就对了。

    另外还要处理null的情况,比如某个属性是null,得提前判断,避免调用方法报错。比如如果User的address可能为null,得先检查两边的address是不是都为null,再去比内容。

    PHP里想比较嵌套对象的内容,除了自定义equals还有别的办法吗?

    可以试试序列化(serialize)后比较字符串,比如把两个对象都serialize成字符串,再用===比较字符串是否一样。这种方法适合快速深比较,比如你有个简单的嵌套对象,没有循环引用的情况。我之前做数据同步时用过,比自定义equals快,但要注意如果对象有循环引用(比如A包含B,B又包含A),serialize会报错,而且私有属性也会被序列化,所以得看场景用。

    不过这种方法不如自定义equals灵活,比如你想只比较某些特定属性,还是得用自定义方法。

    怎么快速判断该用哪种方式比较PHP对象?

    其实核心是看你的需求:如果要判断“是不是同一个实例”(比如当前登录用户、session里的对象),直接用===;如果要判断“内容是不是一样”(比如两个订单的商品和数量相同),用==;如果对象有嵌套结构或者需要比较特定属性,就自定义equals方法。我之前帮同事调订单bug时,他用==比订单对象,结果把不同用户的相同商品订单当成一样的,后来改成自定义equals,比较订单ID、用户ID和商品列表,问题立马解决。

    实在拿不准,可以先想“我到底想比什么”,跟着需求选方法就不会错。

    原文链接:https://www.mayiym.com/51204.html,转载请注明出处。
    0
    显示验证码
    没有账号?注册  忘记密码?

    社交账号快速登录

    微信扫一扫关注
    如已关注,请回复“登录”二字获取验证码