【PHP】PHPフレームワーク「Flow」を試してみる～キャッシュ編～

# 初めに
以前[こちら](https://qiita.com/nnhkrnk/items/3e3caab707a30314e6de)の記事を書いた際、Flowではリフレクションデータを実行時にキャッシュしていると知りました。今回はFlowにおけるキャッシュの扱い方について調べてみました。

# Flowにおけるキャッシュ処理

Flowには、キャッシュ処理を実現するために重要な要素が3つあります。
* **Cache Frontend Interface**
* **Cache Backend Interface**
* **Cache Manager**

それぞれの要素について説明する前に、この3つの関係性をなんとなく把握しておきましょう。
以下のようなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/59a7acac-57f5-9698-b96c-814412b26301.png)


それでは、重要な3つの要素について解説します。


## Frontend Interface
キャッシュを扱う側のためのインターフェースです。
インターフェースを継承することで、キャッシュを扱う側が「**どんなデータをキャッシュしたいか**」を表現できるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/f676c6a1-811c-e116-c31c-7bfaf8ea6dfa.png)

FWでは、あらかじめいくつかの実装クラスを用意しています。
ここにないものを使う場合は、インターフェースを継承したクラスを作成する必要があります。

| クラス名           | 説明                                       |
|-------------------|--------------------------------------------|
| String Frontend   | Stringデータをキャッシュとして扱うための実装クラス        |
| Variable Frontend | String、Array、Objectなどをキャッシュとして扱うための実装クラス  |
| PHP Frontend      | PHPファイル自体をキャッシュとして扱うための実装クラス       |


などなど



## Backend Interface
キャッシュ保存先側のインターフェースです。
インターフェースを継承することで、「**キャッシュをどこに保存したいか**」を表現できるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/738229ac-614f-c1c4-3924-0e4c8017ab07.png)

Frontendと同様に、Backendについてもあらかじめ実装クラスが用意されています。

| クラス名                  | 説明                                      |
|--------------------------|------------------------------------------|
| File Backend             | Webサーバのファイルに書き出すための実装クラス                    |
| Redis Backend            | Redisに書き出すための実装クラス                            |
| Memcached Backend        | Memcachedに書き出すための実装クラス                        |


などなど


## Cache Manager
キャッシュを定義するためのクラスです。
FrontendとBackendのインターフェースを継承することで、キャッシュデータの形式と保存場所を定義することができました。
Cache Managerでは、定義した形式と保存場所を組み合わせて**キャッシュ自体の定義を作成する**ことができます。
（キャッシュを保存するための入れ物を作成するイメージです）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430841/3a396e21-2cc0-14b5-59cd-baba03e04209.png)



# キャッシュ操作試してみる
それでは実際にキャッシュを操作してみましょう。
以下の流れで実装していきます。

* **キャッシュを定義する**
* **キャッシュをDIする**
* **キャッシュを操作する（保存、取得、削除など）**

プロジェクト構成は以下です。
```
Project/
  └ Packages/
      ├ Application/
      |    └ Neos.Welcome/
      |         ├ Classes/
      |         |    └ Controller/
      |         |         └ CacheController.php（★）
      |         |
      |         └ Configration/
      |              ├ Caches.yaml（★）
      |              └ Objects.yaml（★）
      |
      ├ Framework/
      └ Libraries/
```

## キャッシュを定義する
まずはキャッシュを定義していきます。
```Caches.yaml```にキャッシュの定義を記載しましょう。

```yaml:Caches.yaml
MyPackage_Cache:
  frontend: Neos\Cache\Frontend\StringFrontend
  backend: Neos\Cache\Backend\FileBackend
  backendOptions:
    defaultLifetime: 3600
```
ここでは、MyPackage_Cacheという名前のキャッシュを定義しました。
String形式で、Webサーバ内のファイルに保存するようなキャッシです。
```defaultLifetime```はキャッシュの有効期限を表す設定値です。

## キャッシュを作成する
キャッシュの定義を決めたので、作成してみましょう。
今回は```CacheController```というクラスを作成し、そこでキャッシュ操作をすることを考えています。

以下のように```CacheController```に2つのメンバ変数を定義しました。
* ```cacheManager```
* ```$cache```

```php:CacheController.php
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class CacheController extends ActionController
{
    /**
     * @Flow\Inject
     * @var Neos\Flow\Cache\CacheManager
     */
    protected $cacheManager;

    /**
     * @Flow\Inject
     * @var \Neos\Cache\Frontend\FrontendInterface
     */
    protected $cache;

}
```

続いて、DI時に```$cache```が```CacheManager```の```getCache```メソッドを用いて作成されるように定義します。
Flowでは```Objects.yaml```に以下のような定義をすることで設定できます。
```yaml:Objects.yaml
Neos\Welcome\Controller\CacheController:
  properties:
    cache:
      object:
        factoryObjectName: Neos\Flow\Cache\CacheManager
        factoryMethodName: getCache
        arguments:
          1:
            value: MyPackage_Cache
```

上記は「**CacheControllerクラスの```$cache```という変数をDIする際は、```CacheManager```の```getCache()```を使ってください。引数は```MyPackage_Cache```です**」という意味になります。
```MyPackage_Cache```は```Caches.yaml```で定義したキャッシュ定義です。

これにより、```CacheController```の```$cahce```を用いてキャッシュ操作が可能になります。

## 3. キャッシュを操作する
それではキャッシュ操作してみましょう。

### キャッシュ保存
まずはキャッシュ作成を試してみます。
以下のようなメソッドを用意しました。
Frontendにある```set()```メソッドでキャッシュを保存することができます。

```php
    public function createCacheAction(String $key, String $value)
    {
        // キャッシュにデータを保存
        $this->cache->set($key, $value);
    }
```

Frontendの```set()```メソッド内で```backend Intface```に格納するメソッドを呼び出しています。
これにより、データが保存されるというわけです。

実際に呼び出してみたところ、200応答が返ってきました。

```sh
$ curl -i -X POST ^
More?    -H "Content-Type:application/json" ^
More?    -d ^
More? "{^
More?     \"key\": \"myKey\",^
More?     \"value\": \"myValue\"^
More? "}^
More?  http://localhost:8081/Neos.Welcome/Cache/createCache?key=myKey&value=myValue

HTTP/1.1 200 OK
Host: localhost:8081
Date: Sat, 03 Feb 2024 03:23:00 GMT
Connection: close
X-Powered-By: PHP/8.1.25
Content-Type: application/json
X-Flow-Powered: Flow/8.3
Content-Length: 4
```

### キャッシュ取得
続いて、先ほど取得したキャッシュがを取得してみます。
Frontendにある```get()```メッドでキャッシュを取得してみましょう。

```php
    public function getCacheAction(String $key)
    {
        // キャッシュに入ってたら取得
        $data = $this->cache->get($key);
        if ($data !== false){
            $this->view->assign('value', $data);
        } else {
            $this->view->assign('value', 'no cache');
        }
    }
```

実際に試したところ、キャッシュが取得できました。
```sh
$ curl http://localhost:8081/Neos.Welcome/Cache/getCache?key=myKey
"myValue"
```

### キャッシュ削除
最後にキャッシュを削除してみます。
Frontendにある```remove()```メソッドでキャッシュを削除してみましょう。

```php
    public function deleteCacheAction(String $key)
    {
        // キャッシュを削除
        $this->cache->remove($key);
        $this->view->assign('value', 'delete cache');
    }
```

実際に試したところ、キャッシュが削除できました。
```sh
$ curl -X DELETE http://localhost:8081/Neos.Welcome/Cache/deleteCache?key=myKey
"delete cache"
$ 
$ 
$ curl http://localhost:8081/Neos.Welcome/Cache/getCache?key=myKey
"no cache"
```

# 終わりに
今回はFlowにおけるキャッシュ操作の方法について書きました。
インターフェスを用いたFWのキャッシュ処理の仕組みを読み解いていくのは面白かったし、非常に勉強になりました。
また次の記事でお会いしましょう。

ここまで読んでいただきありがとうございました！


# 参考
https://flowframework.readthedocs.io/en/9.0/TheDefinitiveGuide/PartIII/Caching.html