PHP -散列对象的方式distint对象具有相同的字段值具有相同的散列

8nuwlpux  于 2023-04-10  发布在  PHP
关注(0)|答案(3)|浏览(101)

我正在寻找一种方法来生成某种PHP对象的散列(通用解决方案,与所有类,内置和自定义,如果可能的话)。
SplObjectStorage::getHash不是我想要的,因为它会为给定类的每个示例生成不同的哈希。为了描述这个问题,让我们考虑简单的类:

class A() {
public $field; //public only for simplicity
}

和该类的2个示例:

$a = new A(); $a->field = 'b';
$b = new A(); $b->field = 'b';

我尝试过的每个内置函数都会为这些对象返回不同的哈希值,而我希望有一些带有f($a) == f($b) => $a == $b属性的function f($x)
我知道我可以写一个函数,递归地遍历所有对象的属性,直到我找到一个可以转换为字符串的属性,以花哨的方式连接这些字符串并哈希,但这样的解决方案的性能将是可怕的。
有没有一个有效的方法来做到这一点?

omhiaaxx

omhiaaxx1#

假设我理解正确,你可以序列化对象,然后md5序列化对象。由于序列化创建相同的字符串,如果所有属性都相同,你应该每次都得到相同的哈希值。除非你的对象有某种时间戳属性。示例:

class A {
    public $field;
}
$a = new A;
$b = new A;
$a->field = 'test';
$b->field = 'test';
echo md5(serialize($a)) . "\n";
echo md5(serialize($b)) . "\n";

输出:

0a0a68371e44a55cfdeabb04e61b70f7
0a0a68371e44a55cfdeabb04e61b70f7

你的结果不同,因为php内存中的对象是用每个示例化的编号id存储的:

object(A)#1 (1) {...
object(A)#2 (1) {...
jfewjypa

jfewjypa2#

你似乎在谈论一个值对象。它是一种模式,其中每个这样的对象不是根据对象标识进行比较,而是关于组成对象的全部或部分属性的内容。
我在一个项目中使用了其中的一些:

public function equals(EmailAddress $address)
{
    return strtolower($this->address) === strtolower((string) $address);
}

更复杂的对象可以简单地将更多项添加到比较函数中。

return ($this->one === $address->getOne() && 
    $this->two === $address->getTwo());

这样的条件句(都是用“&&”连接的)一旦有任何项不匹配,就会被快捷方式转换为false。

vsikbqxv

vsikbqxv3#

实际上,这个问题提出了两个可能相互矛盾的问题。
1.一种以一致(且高性能)的方式散列任何对象的方法。
1.使用此散列方法对对象进行有效比较。
首先,哈希。其他人出于性能原因建议使用serialize(),但它确实引入了一个限制:PHP对象可以在外部添加字段,也可以在类中声明字段。因此,有可能(尽管不太可能,而且肯定是有问题的编码实践的指示),您的对象可以具有相同的字段,但以不同的顺序声明。这将产生不同的序列化,根据您的问题的措辞,您不希望这样。
为了防止这种情况,你需要将对象转换为数组并对其成员进行排序。如果任何字段本身就是对象或数组,可能会有同样的问题,你应该递归地工作。

function sortObject($obj) {
  $arr = (array) $obj;
  ksort($arr);
  foreach($arr as $k => $v) {
    if(is_array($v) || is_object($v)) {
      $arr[$k] = sortObject($v);
    }
  }
  return $arr;
}

这提供了可以序列化和哈希的对象的一致表示。或者,您可以在函数本身中实际构建哈希:

function hashObject($obj) {
  $arr = (array) $obj;
  ksort($arr);
  $hash = '';
  foreach($arr as $k => $v) {
    if(is_array($v)) {
      $hash .= '['.hashObject($v).']';
    elseif(is_object($v)) {
      $hash .= '{'.hashObject($v).'}';
    } else {
      $hash .= var_export($v);
    }
  }
  return $hash;
}
//The brackets are added to preserve structure.

json_encode()可以用来代替var_export(),但我选择后者是为了保证PHP值的忠实表示(JSON中可能会发生冲突,我不知道),我怀疑它可能会表现得更好。
如果一个对象包含循环引用,例如,它有一个对象或数组字段包含一个值,该值是对它的引用?serialize()可以处理这种情况;上面的功能不能。
现在:比较。最好的方法是由 * 答案最有可能是什么 * 来决定。也就是说:
如果您期望正在比较的大多数对象对都不同,那么逐段比较它们会更有效,这样您就可以尽快建立差异。

function matchObj($a,$b) {
  if(gettype($a) !== gettype($b)) {
    return false;
  }
  $arrA = (array) $a;
  $arrB = (array) $b;
  if(count($arrA) <> count($arrB)) {
    return false;
  }
  ksort($arrA);
  ksort($arrB);
  foreach($arrA as $k => $v) {
    if($k !== key($arrB) || gettype($v) !== gettype($arrB[$k]) {
      return false;
    }
    if(is_array($v) || is_object($v)) {
      matchObj($v,$arrB[$k]) || return false;
    } elseif($v !== $arrB[$k]) {
      return false;
    }
    next($arrB);
  }
  return true;
}

如果你希望大多数对都匹配,那么你也可以对每个对象进行完整的哈希,然后比较它们(如果使用serialize(),这可能比使用上面的递归函数更有效),因为无论如何,在每个示例中,你都没有找到答案的捷径。

相关问题