在PHP序列化对象之前更改对象


Changing object right before PHP serialises it

我有以下类树:

class A /* Base class */
{ 
  private/protected/public $state 
} 
class B extends A /* Auto generated class, not to be modified */
{
  private $v
  public function getV() { return $this->v; }
  public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }

只有一个类A。有多个类,比如类B,所有这些类都有一个子类,比如C。类B是自动生成的,不应该被修改。

我正在会话中存储C类型的对象。我想做的是在PHP序列化之前,在每个实例中存储一些状态信息,当它未序列化时,这将对它进行处理。我希望所有这些都能在A类中实现。

考虑到,我需要使用__sleep()Serializable接口。使用__sleep是不可能的,因为PHP手册上说:

__sleep()不可能返回父类中私有属性的名称。执行此操作将导致E_NOTICE级别错误。相反,您可以使用Serializable接口。

这意味着,如果我休眠类C的实例,我将释放在B中声明的私有变量。所以我想使用Serializable,但由于某种原因,我根本无法让它做我想做的事情。

本质上,我希望对象串行化,就像我自己没有实现任何串行化一样,我只想在它发生之前将信息添加到$state中。我尝试过用ReflectionObject->getProperties()覆盖所有数据,但似乎找不到正确的方法来获取和设置类B中的私有值,使其串行化和非串行化。

我该怎么做?

您可以使用反射类来完成此操作。您必须获得类本身及其每个父类的属性。可以使用ReflectionPropertygetValuesetValue方法来获取和设置属性值,并结合setAccessible来访问私有和受保护的属性。结合这些,我想出了以下代码:

<?php
class A implements Serializable /* Base class */
{
  protected $state;
  public function serialize()
  {
    $this->state = "something";
    return serialize($this->_getState());
  }
  public function unserialize($data)
  {
    $this->_setState(unserialize($data));
  }
  protected function _getState()
  {
    $reflClass = new ReflectionClass(get_class($this));
    $values = array();
    while ($reflClass != null)
    {
      foreach ($reflClass->getProperties() as $property)
      {
        if ($property->getDeclaringClass() == $reflClass)
        {
          $property->setAccessible(true);
          $values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
        }
      }
      $reflClass = $reflClass->getParentClass();
    }
    return $values;
  }
  protected function _setState($values)
  {
    foreach ($values as $_)
    {
      list($className, $propertyName, $propertyValue) = $_;
      $property = new ReflectionProperty($className, $propertyName);
      $property->setAccessible(true);
      $property->setValue($this, $propertyValue);
    }
  }
}
class B extends A /* Auto generated class, not to be modified */
{
  private $v;
  public function getV() { return $this->v; }
  public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);

这似乎能满足你的需求。