改变了(un)serialize()的行为?

时间:2016-02-20 21:53:09

标签: php serialization private php-7 arrayobject

  

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

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

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

找到

问题在于,如果某个类扩展ArrayObject,那么如果您serialize()然后unserialize()该对象,则所有私有属性似乎都会消失:

  1. 使用私有属性和该属性的getter / setter方法创建一个类
  2. 创建该类的对象
  3. 通过setter方法设置私有财产
  4. serialize() object
  5. unserialize()第4步的结果
  6. 调用私有属性的getter方法,结果取决于你的PHP版本
    • PHP 5.3 - PHP 5.6:结果是在步骤3中设置的值
    • PHP 7:结果为空
  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的行为与PHP 5.3+行为100%兼容。

    问题:如何使PHP 7的行为类似PHP 5.3 + ??

1 个答案:

答案 0 :(得分:2)

虽然这对于PHP的下一个版本是固定的,但是您的代码依赖于未记录的行为这一事实是一个错误,称为&#34; Programming by Coincidence&#34;。从精美的文章:

  

如何巧合编程

     

假设Fred有一个编程任务。 Fred在某些代码中键入,尝试它,它似乎工作。 Fred在更多代码中输入,尝试它,它似乎仍然有用。经过几周的编码,程序突然停止工作,经过几个小时的尝试修复后,他仍然不知道为什么。弗雷德可能会花费大量时间来追逐这段代码而无法修复它。无论他做什么,它似乎都无法正常工作。

     

实施意外

     

实现的意外事件只是因为这是当前编写代码的方式。您最终依赖于未记录的错误或边界条件。

在这种情况下,无法保证在扩展ArrayObject时,子类的值将被正确地反序列化。

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