[翻译]PHP序列化跟踪栈和异常

原文地址:http://fabien.potencier.org/php-serialization-stack-traces-and-exceptions.html

昨天我修复了一个看起来非常奇怪的bug。在这篇文章里,我将描述这个问题,并说明我找到的解决之道,并说明PHP在这个场景的一些表现。

缺陷报告

这个bug首先提到:当试图序列化一个symfony表单实例的时候,一个PDO的异常就会被抛出:

"You cannot serialize or unserialize PDO instances"

这个异常之所以被PDO抛出,是因为POD的实例不能被正确的序列化。

但奇怪的是,sfForm这个类并不依赖于PDO。那这到底是怎么回事呢?

这个问题的本质

经过一番研究,我们发现,这个bug只会出现在用户使用PDO将session信息存储在数据库的时候。因此,我们在不是瞎猜的情况下,试图验证这个表单实例是否在某种程度上和session有关联。sfForm类除了widget、验证类、验证异常类外和其他并没有依赖。因此我们试图在只引入一个widget、一个验证类(validator)和一个验证异常类的情况下复现这个bug。

令人惊讶的是,问题居然出在验证的异常类上。在symfony中,验证的异常类直接继承自PHP的Exception类。

当代码中包含一个PDO的实例时,试图序列化一个异常的实例就能复现这个bug,可以用如下的代码证明:

$dbh = new PDO('sqlite:memory:');

function will_crash($dbh)
{
  // serialize an exception
  echo serialize(new Exception());
}

// this will throw a PDOException
will_crash($dbh);

发生了什么?当PHP序列化一个异常时,除了序列化异常的错误码,异常信息外,还需要包含调用栈

这个调用栈是一个数组,包含了所有这个脚本在这个时刻执行过的所有函数和方法。跟踪信息包含文件名,文件的行数,函数名,以及一个包含所有传递给函数的所有参数的数组。你发现问题了吗?

这个跟踪栈包含了一个PDO实例的引用,当它传递给will_crash()函数的时候,因此当PDO的实例不能被序列化时,PHP试图序列化调用栈的时候一个异常就抛出了。

因此,当一个调用栈中出现一个非序列化的对象时,这个异常都不能被序列化。

解决办法

在PHP中,你可以通过实现一个序列化的接口来覆盖序列化的过程。解决方案可以用如下的代码说明:

class sfValidatorError extends Exception implements Serializable
{
  // class code

  public function serialize()
  {
    return serialize(array($this->validator, $this->arguments, $this->code, $this->message));
  }

  public function unserialize($serialized)
  {
    list($this->validator, $this->arguments, $this->code, $this->message) = unserialize($serialized);
  }
}

serialize()函数应该返回一个表示对象的字符串。在我们的使用场景下, 我们序列化了所有的属性出现跟踪栈。

unserialize()反序列化函数将序列化过的字符串当做参数,应该可以构造出一个对象就像执行了构造函数 __construct()一样。

一些验证测试

到目前为止,一切都很如意。但是为了确保问题已经修复,我还是需要找到写一些测试脚本。但是我又不想在测试中继续依赖PDO。只要能仿造PDO的特性就足够了。我们只需要写一个不能被序列化的类即可:

class NotSerializable implements Serializable
{
  public function serialize()
  {
    throw new LogicException('You cannot serialize or unserialize NotSerializable instances');
  }

  public function unserialize($serialized)
  {
    throw new LogicException('You cannot serialize or unserialize NotSerializable instances');
  }
}

  1. ¢奢侈品代购网
    JORDAN(乔丹)
    JAMES HAYFEN(詹姆斯·哈登)
    ALEXANDER FACILA VENDI(亚力山大 · 法熙拉 · 芸迪)
    FUTURE SHIES(未来天空)
    魅蔓莎
    shechipin.ga