̃Gg[͂ĂȃubN}[Nɒlj

PHP :: simplexml_load_string と serialize



simplexml_load_string で xml をパースできるのはとても便利。
しかし serialize との相性は悪い。

以下のようなケースが発生

PHP 5.2.10 にて
  1. simplexml_load_string() で xml からオブジェクトを生成。
  2. 生成したオブジェクトを serialize() でシリアライズして、ファイルに書き出す。
  3. シリアライズしたファイルを読み込み、unserialize() でアンシリアライズ。
  4. すると、下記のようなエラーが
Warning: unserialize(): Node no longer exists in /path/to/hoge.php on line 11


ググってみると、どうやら、simplexml_load_string() で生成したオブジェクト(SimpleXMLObject)をシリアライズすると、unserialize() で元に戻せない PHP のバグであることが判明。


対応策

少し考えた末、シリアライズは必須だが、データ形式はオブジェクトである必要はないので、オブジェクト(SimpleXMLObject)を配列にキャストした後、シリアライズすることにした。
以下は、オブジェクトを再帰的に配列にキャストする toArray 関数です。

toArray 関数

/**
 * recursive toArray
 * @param $mixed
 */
function toArray($mixed)
{
    if (is_array($mixed)) {
        return array_map('toArray', $mixed);
    } elseif (is_object($mixed)) {
        return array_map('toArray', (array)$mixed);
    } else {
        return $mixed;
    }
}


toArray メソッド

class 内でメソッドとして定義する時は、array_map の第一引数を array('self', 'toArray') のようにします。
※. php4系の場合は、array('クラス名', 'toArray') とします。
php4では simplexml_load_string() がサポートされてませんでした。

/**
 * recursive toArray
 * @access protected
 * @static
 */
protected static function toArray($mixed)
{
    if (is_array($mixed)) {
        return array_map(array('self', 'toArray'), $mixed);
    } elseif (is_object($mixed)) {
        return array_map(array('self', 'toArray'), (array)$mixed);
    } else {
        return $mixed;
    }
}


上記の toArray を実装後、以下のようにシリアライズして解決しました。

旧)

$sx = simplexml_load_string($xml);
$serial = serialize($sx);

新)

$sx = simplexml_load_string($xml);
$serial = serialize(self::toArray($sx)); // toArray で配列にキャスト


  • toArray をグローバル関数で定義した場合は以下のようにします。(念のため)
$serial = serialize(toArray($sx));









programming/php/etc/simplexml_load_string_serialize.txt