Slim 3 Frameworkリリースされたので、ざっと紹介

Slim 3 Framework
================

PHPの[Slimフレームワークのバージョン３がリリース](http://www.slimframework.com/2015/02/11/whats-up-with-version-3.html)されたので、簡単に紹介してみます。

【2016/01/16：主にルーティングについて追記・変更しました】

[Slimフレームワーク](http://www.slimframework.com/)はPHPのマイクロ・フレームワークのひとつです。今回のバージョン３は、

*	**PSR-7とミドルウェア構造**の採用、
*  **インターフェースに依存**することによる拡張性の高さ、
* 	**DIコンテナ**の利用（デフォルトはPimple）、
*  でも_*簡単に使い始められる*_、

のが特徴です。

何やら**難しそうな単語**が特徴に並んでますが、[Slimのユーザーガイド](http://www.slimframework.com/docs/)を**読めば、すぐ使える**ぐらい簡単です。なので難しそうな特徴を中心に紹介してみます。

### 簡単に使える

本当に簡単に使えます。[Slimのユーザーガイド](http://www.slimframework.com/docs/)からコードを貼り付けて、ちょっとコードを削っただけですが、これだけのコードで動きます。

```php
<?php
require dirname(__DIR__).'/vendor/autoload.php';

$app = new Slim\App();
$app->get('/hello/{name}', function ($request, $response, $args) {
    $response->getBody()->write("Hello, ".$args['name']);
    return $response;
});
$app->run();
```


### 設定などの置き場所

とは言うものの、プロダクションとして使う場合には設定やルーティングがあります。そのために[Slim Framework 3 Skelton Application](https://github.com/slimphp/Slim-Skeleton)があります。最初から簡単な設定ファイルがあるので、ここ開発を始めるのが楽だと思います。

始め方ですが、ドキュメントに書いてある通りです。

```sh
php composer.phar create-project slim/slim-skeleton [my-app-name]
```

`[my-app-name]`のところに、アプリケーションを作るディレクトリ名を入れて下さい。

* `logs/`ディレクトリにサーバーの書き込み権限をつける。
* `public/`をルートディレクトリと指定する。

で動くはず。

ルーティング
---------

ルーティングの基本は、[ユーザーガイドのRouting](http://www.slimframework.com/docs/objects/router.html#how-to-create-routes)に詳しく乗っているので、サラリと紹介します。

```php
$app->{メソード名}('ルートパターン', {ルートコールバック});
```

* **メソード名**：<br/>`get`, `post`などのHTTPメソード。`any`を使うと全てのメソッドでマッチします。複数のメソードにマッチさせたい場合は`map`を使います。
* **ルートパターン**：<br/>マッチするルートパターン。最初のコードのようにネームホルダーを使えます。デフォルトでは、[（スラッシュを除いて）何にでもマッチする](http://www.slimframework.com/docs/objects/router.html#route-placeholders)ので、文字種を絞りたい場合は`'/hello/{id:[0-9]+}'`とできます。逆にスラッシュも含める場合は`/hello/{path:.*}`とします。
* **ルートコールバック**：<br/>ルートにマッチした場合の処理用コールバックルーチン。

`any`と`map`を使ったルーティングの例です。

```php
// 全てのHTTPメソッドにマッチ。
$app->any('/any/method', 'AnyMethodHandler');

// マッチするHTTPメソッドを配列指定。
$app->map(['get', 'post'], '/get/or/post', 'GetOrPostHandler');
```

> Slim3では、PHPでは結構有名な[nikic/fastroute](https://github.com/nikic/FastRoute)をルーターとして使ってます。ルート数が増えても、マッチング処理のスピードが（ほぼ）変わらない、という特徴を持ってます。

### ルート名

ルートに名前をつけて、後で呼び出すことも出来ます。

```php
// ルートに名前をつけておいて、
$app->get('/hello/{name}', function ($req, $res, $args) {
    echo 'Hello ', $args['name'];
})->setName('hello');

// 後で名前でパスを呼び出す。
$app->router->pathFor('hello', ['name' => 'slim']);
```

詳細は[ユーザーガイドのRoute Names](http://www.slimframework.com/docs/objects/router.html#route-names)にあります。コード補完が効きそうですね。

### Groups

複数のルートをグループとして設定することが出来ます。

```php
$app->group('/users/{id:[0-9]+}', function () {
    $this->map([], '', function ($req, $res, $next) {
        // ユーザーに対して何か処理をする。
    });
    $this->get('/reset-password', function ($req, $res, $next) {
        // パスワードリセット処理
        // ルートは /user/####/reset-pasword
    });
});
```

こちらも詳細は[ユーザーガイドのRoute groups](http://www.slimframework.com/docs/objects/router.html#route-groups)にあります。


ルートコールバック
--------------

ルーティングがマッチしたら、[リクエストを処理するルーチンのこと](http://www.slimframework.com/docs/objects/router.html#route-callbacks)を「**Route Callbacks**」と呼んでます。

### PSR-7オブジェクト

コールバックは、PSR-7のリクエストとレスポンスのオブジェクトを受け取ります。レスポンスに必要な情報を設定します。リダイレクトの例を書くと、

```php
$app->get('/redirected', function($req, $res, $args) {
    return $res
        ->withStatus(302)
        ->withHeader('Location', '/back');
});
```

設定ができたら、最後にレスポンスオブジェクトを返します。

> [ドキュメントを読むと](http://www.slimframework.com/docs/objects/router.html#route-callbacks)、ハンドラーからHTMLなどを出力するには、
> 
> *  いきなりechoするか、
> *  内容を書き込んだレスポンスを返す、
>
> とありましたが、ちゃんとレスポンスを返したほうがテストしやすいと思います。

コールバックで受け取る変数の並びです。標準では、次のようになってます。

```php
function(
    \Psr\Http\Message\ServerRequestInterface $request, 
    \Psr\Http\Message\ResponseInterface $response, 
    array $args);
```

最後の`$args`には、ネームホールダーにマッチした値が入ってきます。`'/user/{name}'`の「name」のところです。

### コンテナを使う場合

ルートコールバックをコンテナを使って生成することもできます。例えば`MyHandler::class`という名前で、ファクトリをコンテナに設定しておきます。

```php
# dependencies.phpなどで
$app->getContainer()[MyHandler::class] = 
    function(ContainerInterfrace $c) {
        return new MyHandler(new MyDependency);
    };
```

ルートを設定するときに、同じく`MyHandler::class`で

```php
# routes.phpなどで
$app->get('/my/handler', MyHandler::class);
```

すると、`MyHandler`クラスを生成してから`__invoke`メソッドを呼び出します。


### コールバック関数の引数を変更する方法

実は、ユーザーガイドには[引数を変更する方法](http://www.slimframework.com/docs/objects/router.html#route-strategies)が書いてあります。`Slim\Interfaces\InvocationStrategyInterface`を実装すれば、**自分好みの動き**に変更できるとあります。

例えば、こう。

```php
class MyInvocation implements InvocationStrategyInterface
{
    public function __invoke(
        callable $callable,
        ServerRequestInterface $request,
        ResponseInterface $response,
        array $routeArguments
    ) {
        // ネームホールダーはクエリとして扱う。
        $query = $request->getQueryParams();
        $query = array_merge($query, $routeArguments);
        $request = $request->withQueryParams($query);

        return call_user_func($callable, $request, $response);
    }
}
```

こんなクラスを作っておきます。これを`$app`作成時に、

```php
$app = new \Slim\App([
    'foundHandler' => function() {
        return new MyInvocation();
    } 
]);
```

とすると、先の例だとこうなります。

```php
$app->get('/hello/{name}', function ($req, $res) {
    echo 'Hello ', $req->getQueryParams()['name'];
});
```

コードを見比べると少々バカバカしい例ですが、すでにコントローラーがある場合などに便利かもしれません。


### コールバックに何が使えるか？

実はコールバックには色々なパターンが使えます。標準でコールバックとして使えるパターンを調べました。

Slim3では、文字列からオブジェクトに解決するのは、[`CallableResolver`というクラス](https://github.com/slimphp/Slim/blob/3.0.0/Slim/CallableResolver.php)を使っています。コードを見ると、結構複雑で、色々なパターンに対応しているのが分かります。まとめると、こんなところでしょうか。

* 実行可能なオブジェクト：
    * クロージャー、
    * __invoke可能なオブジェクト、
* 文字列：
    * コンテナから取り出す文字列（コンテナ名）、
    * クラス名、
    * `コンテナ名:メソッド名`の文字列、
    * `クラス名:メソッド名`の文字列、

が使えます。文字列の場合は、コンテナから取り出してみて、無ければクラス名としてnewする感じですね。つまりクラス名の場合は

```php
class MyCallback {
    function __construct(ContainerInterface $c) {
        $this->service = $c->get('MyService'); // SL!!!
    }
}
```

と書けます！

> コンテナーが入ってくるので、何でもできちゃいますね。この例はサービスロケーターという使い方なので、あちこちから突っ込まれる方法です。

### CallableResolverInterface: 自作で解決

一方、自作のResolverを使うことも可能です。`CallableResolverInterface`を実装したクラスを作ります。

```php
class MyResolver implements CallableResolverInterface {
    public function resolve($toResolve) {
        return $toResolve::forge();
    }
}
$app = new Slim\App([
    'callableResolver' => function() {
        return new MyResolver();
    } 
]);
```

`new`する代わりにスタティックの`forgeメソッド`を使ってインスタンスを生成します。

ミドルウェア
----------

Slim3の特徴の一つが、[PSR-7のミドルウェアを使っていること](http://www.slimframework.com/docs/concepts/middleware.html)による拡張性の高さです。PSR-7を使っているので、フレームワークに限定されずに様々なミドルウェアを使うことが出来ます。

### ミドルウェアの構造

PHPのPSR-7のミドルウェアといえば、この形です。

```php
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

function __invoke(Request $request, Response $response, callable $next) {
    /**
     * 必要な処理を行う。
     */
    $response = $next($request, $response); // 次のミドルウェアを呼び出す。
    /**
     * 戻ってきたレスポンスを使った処理も可能。
     */
     
    return $response; // レスポンスは「必ず」返しましょう。
};
```

> もう標準化してしまえばいいのに…

ミドルウェアとして使えるのは、クロージャー、invoke可能なオブジェクト、クラス名…　あれ？どこかで見たことのあるリストですね。先のルートコールバックと同じく、ミドルウェアも`Resolver`を使って解決しています。

> 実は`__invoke`を実装してなくても動く、はず（実証まだです）。

### 指定できる箇所

ミドルウェアを指定できるのは、Slimアプリケーション、ルート、あるいはルートグループ、となります。

```php
// アプリ全体のミドルウェア。
$app->add(new MyMiddleware);

// 特定のルートのみでミドルウェアを実行する。
$app->get('/middle', function($req, $res) {return $res;})
    ->add(new MyRouteMiddleware);

// 特定のルートグループでミドルウェアを実行する。
$app->group(function() {})
    ->add(new MyGroupMiddleware);
```

### 実行順

複数のミドルウェアを使う場合、**最後に登録したミドルウェアから順番に**実行されます。

```php
$app = new Slim\App();
$app->add(function($req, $res, $next) {
    $res->getBody()->write("middleware #1\n");
    return $next($req, $res);
});
$app->add(function($req, $res, $next) {
    $res->getBody()->write("middleware #2\n");
    return $next($req, $res);
});
$app->get('/hello/Slim', function ($request, $response, $args) {
    $response->getBody()->write("Hello, Slim");

    return $response->withHeader('Content-Type', 'text/plain');
});
```

の場合、出力は

```text
middleware #2
middleware #1
Hello, Slim
```

となります。これは、ちょっと自分の感覚と逆なので注意が必要です。

マイクロ・フレームワーク
------------------

力尽きてきたので、ここらへんで切り上げます。

基本はマイクロ・フレームワークなので、必要な機能は自分で追加することになります。が、SlimPHPのレポジトリには、基本的な機能がいくつか存在します。

### アドオン・ミドルウェア

* [Slim-Csrf](https://github.com/slimphp/Slim-Csrf) : <br/>クロスサイトリソースフォーリッジ対策用のトークンの生成とバリデーションを行うミドルウェアです。

* [Slim-HttpCache](https://github.com/slimphp/Slim-HttpCache) : <br/>HttpのEtagを使ったレスポンスキャッシュを実装しています。ただし、まだ開発中のようで、（2012/12/14現在で）バージョン0.3.0です。

セッションも作ってるみたいで、[レポジトリは存在します](https://github.com/slimphp/Session-Middleware)。けど、まだ空っぽです。

### ビュー

ビュー、あるいはテンプレートだと、

* [PHP-View](https://github.com/slimphp/PHP-View)
* [Twig-View](https://github.com/slimphp/Twig-View)

がありました。