3

编辑:问题是一个记录在案的 php 错误:https : //bugs.php.net/bug.php?id=71617 感谢您找到了@Danack

我只是将应用程序从 PHPH 5.5 迁移到 PHP 7,并在序列化对象时偶然发现了一些奇怪的行为。

我试图将其简化为一个最小、完整和可验证的示例,可以在http://sandbox.onlinephpfunctions.com/code/e926a7398119ea715531cafe4ce6a22c329e53b8找到

问题是,如果一个类扩展了ArrayObjectserialize() ,那么如果你和unserialize()那个对象,所有私有属性似乎都消失了:

  1. 创建一个具有私有属性和该属性的 getter/setter 方法的类
  2. 创建该类的对象
  3. 通过 setter 方法设置私有属性
  4. serialize()目的
  5. unserialize()步骤 4 的结果
  6. 调用私有属性的getter方法,结果取决于你的PHP版本
    • PHP 5.3 - PHP 5.6:结果是在步骤 3 中设置的值
    • PHP 7:结果为空

我试图将其简化为一个最小的、完整的和可验证的示例,可以在http://sandbox.onlinephpfunctions.com/code/e926a7398119ea715531cafe4ce6a22c329e53b8中找到,您可以在其中使用不同的 PHP 版本测试代码。

<?php
class demoStdObject {
    public $public = ''; protected $protected = ''; private $private = '';

    public function getPublic() { return $this->public; }    
    public function getProtected() { return $this->protected; }    
    public function getPrivate() { return $this->private; }        
    public function setPublic($public) { $this->public = $public; }    
    public function setProtected($protected) { $this->protected = $protected; }    
    public function setPrivate($private) { $this->private = $private; }
}

class demoArrayObject extends ArrayObject {
    public $public = ''; protected $protected = ''; private $private = '';
    public function getPublic() { return $this->public; }    
    public function getProtected() { return $this->protected; }    
    public function getPrivate() { return $this->private; }        
    public function setPublic($public) { $this->public = $public; }    
    public function setProtected($protected) { $this->protected = $protected; }    
    public function setPrivate($private) { $this->private = $private; }
}


$arrayObject = new demoArrayObject();
$stdObject = new demoStdObject();

testSerialize($arrayObject);
echo str_repeat('-',30) . "\n";
testSerialize($stdObject);

function testSerialize($object) {
  $object->setPublic('public');
  $object->setProtected('protected');
  $object->setPrivate('private');

  $serialized = serialize($object);

  $unserialized = unserialize($serialized);

  echo get_class($object) . ":\n";
  echo $unserialized->getPublic() . "\n";
  echo $unserialized->getProtected() . "\n";
  echo $unserialized->getPrivate() . "\n";
}

PHP 5.6 的输出:

demoArrayObject:
public
protected
private
------------------------------
demoStdObject:
public
protected
private

PHP 7 的输出:

demoArrayObject:
public
protected

------------------------------
demoStdObject:
public
protected
private

我找不到任何与serialize(),unserialize()ArrayObject类相关的记录更改,所以我想知道发生了什么。它是一个错误吗?未记录的功能?;-)

由于我们在项目中做了很多serialize()/unserialize()我真的需要确保 PHP 7 的行为 100% 兼容 PHP 5.3+ 的行为。

问题:我怎样才能让 PHP 7 表现得像 PHP 5.3+?

4

1 回答 1

2

虽然这在 PHP 的下一个版本中已修复,但您的代码依赖于未记录的行为这一事实是一个称为“巧合编程”的错误。来自精品文章:

如何巧合编程

假设给 Fred 一个编程任务。Fred 输入一些代码,尝试一下,它似乎可以工作。Fred 输入更多代码,尝试了一下,它似乎仍然有效。以这种方式编码数周后,程序突然停止工作,经过数小时的尝试修复它,他仍然不知道为什么。Fred 很可能会花费大量时间来追踪这段代码,但始终无法修复它。无论他做什么,它似乎都无法正常工作。

实施事故

实施事故是因为代码当前的编写方式而发生的事情。您最终会依赖未记录的错误或边界条件。

在这种情况下,不能保证当您扩展 ArrayObject 时,子类的值将被正确反序列化。

使用组合而不是继承会更安全,或者在子方法上编写序列化/反序列化方法将允许您控制序列化/反序列化。或者,不使用序列化/反序列化而是使用您自己的接口也比“魔术”内部方法更可预测。

于 2016-02-21T13:28:04.567 回答