2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PHP】マイナーフレームワーク「Flow」を試してみる~イベント管理編~

Posted at

初めに

「ある処理が実行された時に、別の処理を割り込みで実行させたい」という時ありますよね?「APIのレスポンスが作成されたら、それをログに書き出す」のような。
このようなイベント管理の処理を実現するために、Flowには"Signal & Slot"と呼ばれる機能が備わっています。

イベント管理とは

イベント管理は、「ある処理をトリガーとして別の処理が実行される」という概念です。GUIアプリケーションにおいて、ユーザの操作に応じて対応する処理が実行される仕組みを指します。バックエンドよりはフロントエンドの開発においてより頻繁に利用される印象です。

Flowにおけるイベント管理

Flowでは、Signal & Slotという機能でイベント管理を行います。
特定のイベント(Signal)が発生したときに特定の処理(Slot)を実行するというものです。

※AOPと似てますよね。違いについては後述。

Signal & Slotは、C++のQt(キュート)というフレームワークで利用されていた概念です。Flowではその概念を元にイベント管理を実現しています。

試してみる

それでは、実際にイベント管理を実装してみましょう。
「APIのレスポンスが作成されたら、それをログに書き出す」という処理を実装してみます。

以下の3ステップで実装します。

  • Signal(レスポンスを書き出す処理)を定義
  • Slot(ログに書き出す処理)を定義
  • SignalとSlotを結びつける

プロジェクト構成は以下です。

Packages/
  ├ Application/
  |    └ Neos.Welcome/
  |         └ Classes/
  |              ├ Controller/
  |              |    └ EventController.php
  |              |
  |              ├ Service/
  |              |    ├ SignalService.php
  |              |    └ SlotService.php
  |              |
  |              └ Package.php
  |
  ├ Framework/
  └ Libraries/

1. Signalを定義

Signalを定義します。
以下のルールを満たすようにしてください。

  • ポイント1:メソッド名をemit~~にする
  • ポイント2:@Flow\Signalを付与する
Signal.php
<?php
namespace Neos\Welcome\Service;

use Neos\Flow\Annotations as Flow;

class SignalService
{

    /**
     * @Flow\Signal
     */
    public function emitCreateResponse($responseValue): array
    {
        return ['response' => $responseValue];
    }
}

2. Slotを定義

続いてSlotを定義します。
Slotには特にルールはありません。普通にメソッドを書くように定義します。

Slot.php
<?php
namespace Neos\Welcome\Service;

use Neos\Flow\Annotations as Flow;
use Psr\Log\LoggerInterface;

class SlotService
{

    /**
     * @Flow\Inject(name="Neos.Flow:SystemLogger")
     * @var LoggerInterface
     */
    protected $logger;

    public function outputResponse($responseValue): void
    {
        $this->logger->debug("Outputting response:" . $responseValue);
    }
}

3. SignalとSlotを結びつける

SignalとSlotを結びつけます。
各パッケージのClasses配下にPackage.phpを作成し、bootメソッド内で以下のように書くことでSignalとSlotを紐づけることができます。

Package.php
<?php
namespace Neos\Welcome;

use Neos\Flow\Package\Package as BasePackage;
use Neos\Flow\Core as Core;


class Package extends BasePackage
{
    public function boot(Core\Bootstrap $bootstrap)
    {
        $dispatcher = $bootstrap->getSignalSlotDispatcher();
        $dispatcher->connect(
            \Neos\Welcome\Service\SignalService::class, 'createResponse',
            \Neos\Welcome\Service\SlotService::class, 'outputResponse'
        );
    }
}

4. 動作確認

動作確認してみましょう。
適当なControllerのアクションメソッドからSignalで定義したメソッドを呼び出してみます。

EventController.php
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;

/**
 * Event controller for the Neos.Welcome package
 */
class EventController extends \Neos\Flow\Mvc\Controller\ActionController
{

    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @Flow\Inject
     * @var \Neos\Welcome\Service\SignalService
     */
    protected $signalService;

    /**
     * @return void
     */
    public function indexAction()
    {
        $response = $this->signalService->emitCreateResponse("testResponse");
        $this->view->assign('value', $response);
    }
}

実行してみます。
レスポンスは問題なく返ってきました。

$ curl http://localhost:8081/Neos.Welcome/Event/index | jq
{
  "response": "testResponse"
}

ログにもSlotで定義した形式で出力されていることが確認できました。

24-03-10 05:32:52 1220  DEBUG  Outputting response:testResponse

AOPと何が違うの?

ここまで読んでみて、AOPと何が違うの?と思われた方もいると思います。私もその一人でした。

Flowの公式ドキュメントにも以下のような記載があり、"Signal & Slot"はAOPの機能を利用して実現されていることが分かります。

The Signal annotation is picked up by the AOP framework and the method is filled with implementation code as needed.

であれば、ますますAOPでいいのでは?と思ってしまいますよね。

Signal & Slotの強み

そんな"Signal & Slot"ですが、明確な強み一つが存在します。
それは、FWが最初からSignalを用意しているという点です。

以下は、Flowがあらかじめ用意しているSignalの一覧になります。

一部のSignalを抜粋してみました。

Signal 説明
compiledClasses コンパイルが完了した時のSignal
configurationManagerReady 設定ファイルの読み込みが完了した時のSignal
beforeControllerInvocation 各Controllerへの割り振り前のSignal

また、これらはすべてFW内の処理内容がトリガーになっているというのもポイントです。

このようなトリガーをAOP単体で書くのは難しいと思います。
こういったところで、AOPと"Signal & Slot"の差別化ができるのかなと感じました。

終わりに

今回はFlowにおける"Signal & Slot"を利用したイベント管理方法を紹介しました。使いこなすにはFlowのフレームワーク内部の処理をよく知る必要がありますね。
これからもいろいろ調べて知識を深めようと思います。

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

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?