首页 > 解决方案 > PHP对象比较和私有属性

问题描述

我想知道 PHP 如何确定具有私有属性的类实例的相等性:

class Example {
    private $x;
    public $y;
    public __construct($x,$y) {
        $this->x = $x; $this->y = $y;
    }
}

和类似的东西

$needle = new Example(1,2);
$haystack = [new Example(2,2), new Example(1,2)];
$index = array_search($needle, $haystack); //  result is 1

结果确实是 1,所以比较私有成员。是否有可能只匹配公共属性?

我知道我可以覆盖该__toString方法并将所有数组和指针转换为字符串,但这会导致代码难看。

我希望找到一个足够优雅的解决方案,可以使用in_array, array_search,array_unique等。

标签: php

解决方案


一个可能的解决方案可能是 PHP 反射 API。考虑到这一点,您可以读取一个类的公共属性并将它们与同一类的另一个实例的其他公共属性进行比较。

下面的代码是公共类属性的简单对比。比较的基础是一个简单的值对象。

declare(strict_types=1);
namespace Marcel\Test;

use ReflectionClass;
use ReflectionProperty;

class Example
{
    private string $propertyA;
    public string $propertyB;
    public string $propertyC;

    public function getPropertyA(): string
    {
        return $this->propertyA;
    }

    public function setPropertyA(string $propertyA): self
    {
        $this->propertyA = $propertyA;
        return $this;
    }

    public function getPropertyB(): string
    {
        return $this->propertyB;
    }

    public function setPropertyB($propertyB): self
    {
        $this->propertyB = $propertyB;
        return $this;
    }

    public function getPropertyC(): string
    {
        return $this->propertyC;
    }

    public function setPropertyC($propertyC): self
    {
        $this->propertyC = $propertyC;
        return $this;
    }

    public function __compare(Example $b, $filter = ReflectionProperty::IS_PUBLIC): bool
    {
        $reflection = new ReflectionClass($b);
        $properties = $reflection->getProperties($filter);
    
        $same = true;
    
        foreach ($properties as $property) {
            if (!property_exists($this, $property->getName())) {
                $same = false;
            }
        
            if ($this->{$property->getName()} !== $property->getValue($b)) {
                $same = false;
            }
        }
    
        return $same;
    }
}

该类的__compare方法Example使用PHP Reflection API。首先,我们构建一个我们想要与当前实例进行比较的类的反射实例。然后我们请求我们想要比较的类的所有公共属性。如果实例中不存在公共属性,或者该属性的值与我们要比较的对象中的不同,则该方法返回 false,否则返回 true。

一些例子。

$objectA = (new Example())
    ->setPropertyA('bla')
    ->setPropertyB('yadda')
    ->setPropertyC('bar');

$objectB = (new Example())
    ->setPropertyA('foo')
    ->setPropertyB('yadda')
    ->setPropertyC('bar');

$result = $objectA->__compare($objectB);
var_dump($result); // true

在此示例中,比较结果true是因为公共属性存在于两个实例中并且具有相同的值PropertyBPropertyC请记住,只有当第二个实例是同一个类时,这种比较才有效。人们可以进一步旋转这个解决方案,并根据它们的特性比较所有可能的对象。

数组过滤器示例

它是一种in_array基于所示__compare方法的功能重建。

declare(strict_types=1);
namespace Marcel\Test;

class InArrayFilter 
{
    protected ArrayObject $data;

    public function __construct(ArrayObject $data)
    {
        $this->data = $data;
    }

    public function contains(object $b)
    {
        foreach ($this->data as $object) {
            if ($b->__compare($object)) {
                return true;
            }
        }
    
        return false;
    }
}

这个过滤器类的作用类似于in_array函数。它需要一个对象集合并检查集合中是否存在具有相同公共属性的对象。

结论

如果您希望此解决array_unique方案像array_searchìn_array__compare

这取决于要处理的数据量和回调方法的性能。应用程序可能会消耗更多内存,因此变得更慢。


推荐阅读