PHP メソッドの戻り値をキャッシュする簡単なテクニック

処理コストの高いメソッドの戻り値を簡易的にキャッシュする方法を紹介します。

次のような記述を用います。

class T {
  function f() {
    return $this->cache ?? $this->cache = (function () {
      return '時間のかかる処理の結果を返す';
    })();
  }
}
null合体演算子(??)が利用できない場合(※1)。
a ?? b処理はisset(a) ? a : bとの同等の処理。
issetに依存するためメソッドの結果がnullの場合はキャッシュされない。

このイディオムを用いて文字列結合の結果をキャッシュした例が以下の通りです。

class T {
  public $a = 'Tom';
  public $b = 'Cat';
  
  function concat() {
    return $this->concat ?? $this->concat = (function () {
      return $this->a . $this->b;
    })();
  }
}

$t = new T();
$t->concat(); // `.`結合演算子による文字列結合は一度のみ実行され、結合された値が返される
$t->concat(); // キャッシュされた`$this->concat`の値が返される
文字列結合はそこまで大きな処理コストにはならないため、実際にはより負荷の高い処理のみをキャッシュするようにしましょう。
プロパティ名にはメソッド名と同名のものを利用することができます。

簡潔な処理であれば、無名関数の即時実行((function () {})())を用いる必要はありませんが、複文を扱う際や、return文の即時返却を行いたい場合などに利用するとよいでしょう。

class T {
  function is_enabled() {
    return $this->is_enabled ?? $this->is_enabled = (function () {
      if ("a") return true;
      if ("b") return true;
      return false;
    })();
  }
}

無名関数を用いないと、次のような煩雑な処理を記述することになり、コードの可読性や明瞭さに欠けてしまいます。

class T {
  function is_enabled() {
    if (isset($this->is_enabled)) return $this->is_enabled;
    if ("a") return $this->is_enabled = true;
    if ("b") return $this->is_enabled = true;
    return $this->is_enabled = false;
  }
  
  function is_null() {
    if (isset($this->is_null)) return $this->is_null;
    $is_null = false;
    if ("a") return $is_null = true;
    else if ("b") return $is_null = true;
    return $this->is_null = $is_null;
  }
}

補足

※1 null合体演算子(??)が利用できないPHP 7未満のバージョンでは次のように記述します。

class T {
  function f() {
    if (isset($this->cache)) return $this->cache;
    return $this->cache = (function () {
      return '時間のかかる処理の結果を返す';
    })();
  }
}

広告