14
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

例外オブジェクトはシリアライズ可能か?

Last updated at Posted at 2014-09-27

前提(予備知識)

まず基本的なことから。

  • Closure はシリアライズ出来ない。
  • スタックトレースは trace プロパティに保持される。

trace プロパティ配列の各要素は以下のようなキーを持つ連想配列からなる。 fileline はクロージャ呼び出しに関しては未定義になる。

  • file (ファイル)
  • line (行)
  • function (関数/メソッド/クロージャ)
  • args (引数)

検証

変数に代入して呼び出す

コード
<?php
session_start();
try {
    $func = function () { throw new \Exception; };
    $func();
} catch (\Exception $e) {
    var_dump($e->getTrace());
    $_SESSION['exceptions'][] = $e;
}
結果
array(1) {
  [0]=>
  array(4) {
    ["file"]=>
    string(12) "test.php"
    ["line"]=>
    int(5)
    ["function"]=>
    string(9) "{closure}"
    ["args"]=>
    array(0) {
    }
  }
}

call_user_func 関数で呼び出す

コード
session_start();
try {
    call_user_func(function () { throw new \Exception; });
} catch (\Exception $e) {
    var_dump($e->getTrace());
    $_SESSION['exceptions'][] = $e;
}
実行結果
array(2) {
  [0]=>
  array(2) {
    ["function"]=>
    string(9) "{closure}"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(38) "test.php"
    ["line"]=>
    int(4)
    ["function"]=>
    string(14) "call_user_func"
    ["args"]=>
    array(1) {
      [0]=>
      object(Closure)#1 (0) {
      }
    }
  }
}

Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in [no active file]:0
Stack trace:
#0 {main}
  thrown in [no active file] on line 0

結果

  • function がクロージャであっても、 "{closure}" という文字列になるので問題なくシリアライズ出来る。
  • args の要素にクロージャが含まれていると、オブジェクトのままなのでシリアライズ出来なくなる。

対策

安全にセッションに格納したいときはスタックトレースを全部潰してしまえば問題ない。

追記: 2023-07-15 更新
親クラスを全て辿るようにしました

スタックトレースを強制的に空配列にする関数
<?php
function clear_trace_recursive(Throwable $e): void
{
    do {
        $r = new ReflectionClass($e);
        do {
            if ($r->hasProperty('trace')) {
                $r->getProperty('trace')->setValue($e, []);
            }
        } while ($r = $r->getParentClass());
    } while ($e = $e->getPrevious());
}
14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?