代码之家  ›  专栏  ›  技术社区  ›  Rob Gwynn-Jones

php取消序列化移除对象属性

  •  5
  • Rob Gwynn-Jones  · 技术社区  · 8 年前

    PDOException ,将其通过导线发送,然后在以后取消对其的序列化。当我取消它的市面价值时, $code

    我的代码是针对PostgreSQL数据库运行的。使用以下DDL:

    CREATE TABLE test (
        id INTEGER
    );
    

    <?php
    
    $dsn = "pgsql: dbname=postgres;host=/var/run/postgresql;port=5432";
    $user = "postgres";
    $password = "";
    try
    {
        $pdo = new PDO($dsn, $user, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $res = $pdo->exec("INSERT INTO test (id) VALUES (999999999999999)");
    }
    catch (PDOException $e)
    {
        var_dump((array) $e);
        print "\n";
        print $e->getCode();
        print "\n";
        $s = serialize($e);
        print $s;
        print "\n";
        $d = unserialize($s);
        var_dump((array) $d);
        print "\n";
        print $d->getCode();
        print "\n";
        print serialize($e->getCode());
        print "\n";
    }
    
    ?>
    

    在我的输出中, 最终输出中缺少属性。此外,我还收到以下通知:

    PHP Notice: Undefined property: PDOException::$code in /home/developer/test_serialize.php on line 20

    PDO例外 之后的属性 unserialize

    序列化

    PHP 7.1.6 (cli) (built: Jun 18 2018 12:25:10) ( ZTS )
    Copyright (c) 1997-2017 The PHP Group
    Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    

    编辑-添加打印输出

    print_r 具有 var_dump

    array(8) {
      ["*message"]=>
      string(75) "SQLSTATE[22003]: Numeric value out of range: 7 ERROR:  integer out of range"
      ["Exceptionstring"]=>
      string(0) ""
      ["*code"]=>
      string(5) "22003"
      ["*file"]=>
      string(34) "/home/developer/test_serialize.php"
      ["*line"]=>
      int(10)
      ["Exceptiontrace"]=>
      array(1) {
        [0]=>
        array(6) {
          ["file"]=>
          string(34) "/home/developer/test_serialize.php"
          ["line"]=>
          int(10)
          ["function"]=>
          string(4) "exec"
          ["class"]=>
          string(3) "PDO"
          ["type"]=>
          string(2) "->"
          ["args"]=>
          array(1) {
            [0]=>
            string(73) "INSERT INTO km_role (role_id, role_name) VALUES (999999999999999, 'test')"
          }
        }
      }
      ["Exceptionprevious"]=>
      NULL
      ["errorInfo"]=>
      array(3) {
        [0]=>
        string(5) "22003"
        [1]=>
        int(7)
        [2]=>
        string(28) "ERROR:  integer out of range"
      }
    }
    
    22003
    O:12:"PDOException":8:{s:10:"*message";s:75:"SQLSTATE[22003]: Numeric value out of range: 7 ERROR:  integer out of range";s:17:"Exceptionstring";s:0:"";s:7:"*code";s:5:"22003";s:7:"*file";s:34:"/home/developer/test_serialize.php";s:7:"*line";i:10;s:16:"Exceptiontrace";a:1:{i:0;a:6:{s:4:"file";s:34:"/home/developer/test_serialize.php";s:4:"line";i:10;s:8:"function";s:4:"exec";s:5:"class";s:3:"PDO";s:4:"type";s:2:"->";s:4:"args";a:1:{i:0;s:73:"INSERT INTO km_role (role_id, role_name) VALUES (999999999999999, 'test')";}}}s:19:"Exceptionprevious";N;s:9:"errorInfo";a:3:{i:0;s:5:"22003";i:1;i:7;i:2;s:28:"ERROR:  integer out of range";}}
    array(7) {
      ["*message"]=>
      string(75) "SQLSTATE[22003]: Numeric value out of range: 7 ERROR:  integer out of range"
      ["Exceptionstring"]=>
      string(0) ""
      ["*file"]=>
      string(34) "/home/developer/test_serialize.php"
      ["*line"]=>
      int(10)
      ["Exceptiontrace"]=>
      array(1) {
        [0]=>
        array(6) {
          ["file"]=>
          string(34) "/home/developer/test_serialize.php"
          ["line"]=>
          int(10)
          ["function"]=>
          string(4) "exec"
          ["class"]=>
          string(3) "PDO"
          ["type"]=>
          string(2) "->"
          ["args"]=>
          array(1) {
            [0]=>
            string(73) "INSERT INTO km_role (role_id, role_name) VALUES (999999999999999, 'test')"
          }
        }
      }
      ["Exceptionprevious"]=>
      NULL
      ["errorInfo"]=>
      array(3) {
        [0]=>
        string(5) "22003"
        [1]=>
        int(7)
        [2]=>
        string(28) "ERROR:  integer out of range"
      }
    }
    
    PHP Notice:  Undefined property: PDOException::$code in /home/developer/test_serialize.php on line 24
    
    s:5:"22003"
    

    $e->getCode() 是:

    i:7;
    
    2 回复  |  直到 8 年前
        1
  •  4
  •   jstur    8 年前

    $code

    PDOException documentation page 几乎所有人都在谈论错误代码被创建为字符串而不是int所导致的问题。

    try
    {
        $pdo = new PDO($dsn, $user, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $res = $pdo->exec("INSERT INTO test_schema.test (id) VALUES (999999999999999)");
    }
    catch (PDOException $e)
    {
        // the new bit is here
        if (!is_int($e->getCode())) {
            $reflectionClass = new ReflectionClass($e);
            $reflectionProperty = $reflectionClass->getProperty('code');
            $reflectionProperty->setAccessible(true);
            $reflectionProperty->setValue($e, (int)$reflectionProperty->getValue($e));    
        }
        // the rest is the same
        var_dump((array) $e);
        print "\n";
        print $e->getCode();
        print "\n";
        $s = serialize($e);
        print $s;
        print "\n";
        $d = unserialize($s);
        var_dump((array) $d);
        print "\n";
        print $d->getCode();
        print "\n";
        print serialize($e->getCode());
        print "\n";
    }
    

    当然,如果代码包含字母数字值而不是字符串形式的整数,则会丢失信息。消息可能会重复错误号,但这可能不太值得信赖。

    function will_crash($pdo) {
        $res = $pdo->exec("INSERT INTO test_schema.test (id) VALUES (999999999999999)");
    }
    
    try
    {
        $pdo = new PDO($dsn, $user, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        will_crash($pdo);
    }
    catch (PDOException $e)
    {
        $s = serialize($e);
        // PHP Fatal error:  Uncaught PDOException: You cannot serialize or unserialize PDO instances in...
    }
    

    哇哦。

    json_encode json_decode

        2
  •  5
  •   Blackbam    8 年前

    PDOException extends RuntimeException {
      /* Properties */
      public array $errorInfo ;
      protected string $code ;
    
      /* Inherited properties */
      protected string $message ;
      protected int $code ;
      protected string $file ;
      protected int $line ;
    
      /* Inherited methods */
      final public string Exception::getMessage ( void )
      final public Throwable Exception::getPrevious ( void )
      final public mixed Exception::getCode ( void )
      final public string Exception::getFile ( void )
      final public int Exception::getLine ( void )
      final public array Exception::getTrace ( void )
      final public string Exception::getTraceAsString ( void )
      public string Exception::__toString ( void )
      final private void Exception::__clone ( void )
    }
    

    https://github.com/php/php-src/blob/cd953269d3d486f775f1935731b1d6d44f12a350/ext/spl/spl.php

    /** @return the code passed to the constructor
     */
    final public function getCode()
    {
        return $this->code;
    }
    

    这是一个PHP异常的构造函数:

    /** Construct an exception
     *
     * @param $message Some text describing the exception
     * @param $code    Some code describing the exception
     */
    function __construct($message = NULL, $code = 0) {
        if (func_num_args()) {
            $this->message = $message;
        }
        $this->code = $code;
        $this->file = __FILE__; // of throw clause
        $this->line = __LINE__; // of throw clause
        $this->trace = debug_backtrace();
        $this->string = StringFormat($this);
    }
    

    $code 只有在生成异常时才能填充异常的属性,如果没有,则应为零 通过了。

    http://fabien.potencier.org/php-serialization-stack-traces-and-exceptions.html

    本质上说:

    堆栈跟踪包含对pdo实例的引用,因为它被传递给will_crash()函数,并且由于pdo实例不可序列化,所以当php序列化堆栈跟踪时会引发异常。

    异常将无法序列化。

    解决方案:

    class SerializableException extends Exception implements Serializable {
    // ... go ahead :-)
    }
    
    推荐文章