Help us understand the problem. What is going on with this article?

PHPでSimpleXMLElementを持ったオブジェクトをキャッシュしたい時

結論:sleep()とwakeup()を使う

以下のようなSimpleXMLElementをプロパティとして持ったクラスのオブジェクトを、Serializeしてキャッシュしたい場合があります。

MyClass.php
<?php
namespace App;
use SimpleXMLElement;

class MyClass
{
    /**
     * @var SimpleXMLElement
     */
    public $xml;

    /**
     * @param  SimpleXMLElement  $xml
     * @return void
     */
    public function __construct(SimpleXMLElement $xml)
    {
        $this->xml = $xml;
    }
}

例えばこのクラスのオブジェクトをLaravelでそのままキャッシュしようとすると、「SimpleXMLElementをシリアライズできないよ」というエラーで怒られます。

解決策としてPHPのマジックメソッドであるsleep()とwakeup()を使用します。
先ほどのMyClassに以下のメソッドを追記。

    public function __sleep()
    {
        $this->xml = $this->xml->asXml();

        return ['xml'];
    }

    public function __wakeup()
    {
        $this->xml = simplexml_load_string($this->xml);
    }

シリアライズされる前にsleep()が呼ばれ、その中ではSimpleXMLElementをXML文字列に変換しています。
そしてアンシリアライズされる時に
wakeup()の中で再度SimpleXMLElementに変換するような形。

参考:PHP: マジックメソッド - Manual
http://php.net/manual/ja/language.oop5.magic.php

Laravelの時の注意点

Laravelではrememberというキャッシュがなければオリジンを取得し、そして保存して返すというメソッドがあります。
保存して返すというところがミソですが、これは以下のような問題を引き起こします。

  1. MyClassをキャッシュへ保存しようとする
  2. 保存する際に__sleep()メソッドによりxmlプロパティがstringへ変換される
  3. xmlプロパティがstringであるオブジェクトがrememberメソッドより返される

このように、初回(オリジンを取得する際)に__wakeup()が呼ばれないという事態に陥ってしまいます。
これは以下のように対処します。

$item= Cache::remember('item', 30, function () {
    return new MyClass($someSimpleXMLElement);
});

if (is_string($item->xml)) {
   $item->__wakeup();
}

return $item;
hareku
AWS / Go / Laravel / Vue.js / React
https://mycode.rip/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした