【问题标题】:PHP object comparison and private propertiesPHP对象比较和私有属性
【发布时间】:2020-06-03 21:05:45
【问题描述】:

我想知道 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_arrayarray_searcharray_unique 等一起使用。

【问题讨论】:

  • “是否有可能只匹配公共属性?” - 不,这是不可能的。 (而且我一开始不知道这有多大意义。从这里开始,两个元素都必须属于同一类 - 然后“私有”在那个级别上什至无关紧要。如果$a$b 属于同一类,那么$a无论如何都可以访问$b 的私有属性。)
  • 我读到array_unique 在比较之前会自动转换为字符串。我正在寻找一致的array_search 例程。
  • 那你可能得自己写了。
  • 很遗憾不同的库方法表现不同。像array_unique 这样的行为会很棒。也许 PHP 9 将支持__compare 功能。

标签: php


【解决方案1】:

可能的解决方案是 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;
    }
}

Example 类的__compare 方法使用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 在两个实例中都存在并且具有相同的值。请记住,只有当第二个实例是同一个类时,这种比较才有效。可以进一步旋转该解决方案,并根据其特征比较所有可能的对象。

数组过滤器示例

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

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_uniquearray_searchìn_array 一样运行,您必须编写自己的回调函数,以您希望获得结果的方式执行 __compare 方法。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    相关资源
    最近更新 更多