Ground Sunlight

Windowsで作る - PHPプログラミングの開発環境

ユーザ用ツール

サイト用ツール


apricot:ext:interceptor

文書の過去の版を表示しています。


編集中

Apricot インターセプター

— y2sunlight 2020-05-25

Apricot に戻る

関連記事

インターセプター とはアクションの前処理の事です。ミドルウェアと同じでリクエストを中断してレスポンスオブジェクトを生成することもできますが、アクションの後処理はできません。これを図示すると以下のようになります。

                Middleware      Action
               ┌──────────┐     ┌───────────────────────────────────────────┐
               |          |     |  Interceptor   Interceptor   Real Action  |
               |          |     |  ┌─────────┐   ┌─────────┐   ┌──────────┐ |
[Request ] --> | -------> | --> |--|-------->|-->|-------->|-->| ────┐    | | 
               |    ↓     |     |  |    ↓    |   |    ↓    |   |     |    | |
               |    ↓     |     |  └─────────┘   └─────────┘   |     |    | | 
               |    ↓     |     |       ↓             ↓        |     |    | |
[Response] <-- | <------  | <-- | <--------------------------- | <───┘    | |
               |          |     |                              └──────────┘ |
               └──────────┘     └───────────────────────────────────────────┘

上図から分かるようにミドルウェアパイプラインから見ると、インターセプターはアクションに含まれます。ミドルウェアとの一番の違いは、ミドルウェアは基本的に全てのコントローラを対象としているのに対し、インターセプターは、各コントローラで独自に設定ができるという点です。

インターセプターの主な用途としては入力データの検証(バリデーション)、入力データのフィルタリングや変換です。インターセプターを作ることで、すっきりしたアクションを作ることができます。


ベースコントローラの変更

インターセプターの仕組みを実装するために、コアのベースコントローラ( BaseController )を以下のように変更します。

** /apricot/core/Foundation **

BaseController.php
<?php
namespace Core\Foundation;
 
/**
 * Request Controller Class (Controller Base)
 */
class BaseController
{
    /**
     * The interceptors registered on the controller.
     * @var array
     */
    protected $interceptors = [];
 
    /**
     * Register interceptors on the controller.
     *
     * @param  string $actionName
     * @param  array|mixed $interceptors array or arguments list
     */
    protected function intercept($actionName, $interceptors)
    {
        $interceptor_arr = is_array($interceptors) ? $interceptors : array_slice(func_get_args(),1);
        if (!array_key_exists($actionName, $this->interceptors))
        {
            $this->interceptors[$actionName] = array();
        }
        $this->interceptors[$actionName] = array_merge($this->interceptors[$actionName] , $interceptor_arr);
    }
 
    /**
     * Call real Action
     * @param string $actionName
     * @param array $params
     * @return \Core\Foundation\Response
     */
    protected function callAction($actionName, $params)
    {
        return call_user_func_array(array($this, $actionName), $params);
    }
 
    /**
     * Invoke Action
     * @param string $actionName
     * @param array $params
     * @return \Core\Foundation\Response
     */
    public function invokeAction($actionName, $params)
    {
        // Interceptor parameters
        $iparams = array_merge(array('_controller'=>$this), $params);
 
        // Invoke Interceptor
        if (array_key_exists($actionName, $this->interceptors))
        {
            $response = null;
            foreach($this->interceptors[$actionName] as $interceptor)
            {
                if (is_callable($interceptor))
                {
                    // Case of callable
                    $response = call_user_func_array($interceptor, $iparams);
                }
                elseif(strpos($interceptor,'@')!==false)
                {
                    // Case of Controller/Action
                    list($class, $method) = explode('@', $interceptor);
                    if (empty($class))
                    {
                        $instance = $this;
                    }
                    else
                    {
                        $class = "\\App\\Controllers\\Interceptors\\{$class}";
                        $instance = new $class;
                    }
 
                    // Call interceptor
                    $response = call_user_func_array(array($instance, $method), $iparams);
                }
 
                if ($response instanceof \Core\Foundation\Response)
                {
                    return $response;
                }
            }
        }
 
        // Call Action
        return $this->callAction($actionName, $params);
    }
}

callAction() メソッドに変更はありません。intercept() メソッドを追加し、invokeAction() メソッドを変更します。

  • intercept($actionName, $interceptors) メソッド
    • アクションにインターセプターを追加します。
    • BaseControllerは、インターセプターを配列( $this->$interceptors )で管理しています。
  • invokeAction($actionName, $params) メソッド
    • ミドルウェアパイプラインからはこのメソッドが呼び出されます。
    • インターセプターを登録順に実行し最後に実際のアクションを実行します。
    • インターセプターがResponseインスタンスを返した場合、処理は中止され、そのインスタンスをアクションのレスポンスとして返します。

インターセプターの使い方は、次項をご覧ください。


インターセプターの使用

インターセプターの登録は、コントローラーのコンストラクタで intercept() メソッドを使って行います。インターセプターにはクロージャーとメソッドの両方が使用できます。簡単なバリデーション処理ならクロジャーで以下のように書きます。

class FooController extends Controller
{
    public function __construct()
    {
        // インターセプターの登録
        $this->intercept('action', function(Controller $controller, int $id)
        {
            $inputs = Input::all();
            ...
        });
    }
    ...
}

インターセプターに渡される引数は、第1引数に、コントローラのインスタンスが、その後にアクションと同じの引数が続きます。

intercept() にメソッドを使用する場合は、以下のように 'クラス名@メソッド名' の形式で指定します。

class FooController extends Controller
{
    public function __construct(User $user)
    {
        // インターセプター登録
        $this->intercept('action1', 'FooInterceptor@action1');
        $this->intercept('action2', 'FooInterceptor@action2');
        ...
    }
    ....
}

Apricotでは、インターセプターを配置する場所は以下に決めれられいます。

aprocot/app/Controllers/Interceptors

結果として、インターセプタークラスの名前空間は \\App\\Controllers\\Interceptors になります。

自分自身( $this )のメソッドを指定する場合は、'@メソッド名' のように指定します。但し、自分自身のメソッドでも public でないとアクセスできません。


認証コントローラ

TODO:


ユーザコントローラ

TODO:


apricot/ext/interceptor.1590395830.txt.gz · 最終更新: by y2sunlight