PHP軽量FrameworkのSlim3

この記事はRE:CODEアドベントカレンダーの4日目の記事です。
Slimフレームワークについて一般的に使われている実装方法なども含めて、今時な使い方を紹介していきます。

# はじめに
最近、私の中でこれだ！！！
というフレームワークが見つかって、かなり色々な場面でSlim3を書かせていただいている。
今回は、Slim3でのAPIの実装方法をメインにViewにBladeを採用した方法も記載する。
ViewにBladeを使うことでPHPフレームワークで人気の高いLaravel風な書き方をすることができるので、とても簡単に利用することができる。

# なぜ、軽量フレームワークなのか
まず、この記事を見た人は絶対にこう思うはずである。

「PHPにはフルスタックで強力な人気フレームワークLaravelがあるのに、なぜ今更軽量フレームワークのSlim3を使うんだ？」と思うはずである。

私もLaravelを触る前、そして、Slim3の存在を知るまではそう思っていた。
そのことについても少し触れていこうと思う。(実装方法を知りたい人は読み飛ばしてください)

## フレームワークの種類
世の中にあるフレームワークは軽量フレームワーク・フルスタックフレームワークの2種類に分類できると思う。(もしかしたらミドルスタックフレームワークもあるかも・・・？)

### フルスタックフレームワーク
フルスタックフレームワークはフレームワークとしてのRequestやResponse処理の実装はもちろんのこと、Auth認証やSession管理といったところや他色々なWebの実装で必要な機能をもともと用意してくれている。

なので、必要とするコンポーネント部品をフレームワーク利用者が作らなくても実装できる。
また、デザインパターンもMVCモデルを採用しているフレームワークが多く、Viewとモデルを分割できるので、拡張性が高く、バグも入りにくくなる。

また、LaravelではViewコンポーザなどを利用してViewの中にビジネスロジックを含まない実装が可能になっている。

### 軽量フレームワーク
軽量フレームワークはRequestやResponse処理、ミドルウェア、ルーティングの機能など最小限Webをフレームワークで管理するために必要な機能のみを提供してくれる。

最小限の機能のみしか提供されていないので、それ以外の実装はすべて、フレームワーク利用者がコンポーネント部品を作ることになる。

また、フレームワーク自体のカスタマイズ性も高く、開発者の使いたい環境にカスタマイズできる。

## どちらがいいの？
上記以外にも色々な機能がフレームワークには実装されている。
しかし、結局のところどっちがいいの？と聴きたくなるだろう。
これに関しては、時と場合によると思う。

しかし、この記事は軽量フレームワークを推すための記事なので、軽量フレームワークを採用する利点を述べて行く。

### 学習コスト
まず、フルスタックフレームワークは学習コストの壁がある。
実装されている機能が多いということはその機能を把握しなければいけないという問題が出てくる。
理解していない機能を使うとたちまちフレームワークのエラー画面に大量のエラーが出てそれをググりながら修正するという無駄しか出てこない。

それに比べて軽量フレームワークは最小限の機能しかないため、学習コストはほとんどない。
Slim3フレームワークはリファレンス自体も1日あれば全て読めるだろう。

### 階層問題
フルスタックフレームワークは様々な階層を経由してプログラムが実行される。
そのため、適切な階層でサービスプロバイダーやViewコンポーザの処理をしないといけない。
その階層構造と実行順序を理解するまでにかなり時間がかかる。

軽量フレームワークは通過する階層が浅いので、階層構造をあまり意識しまくても利用できる。


# Slim3での実装方法
ここまで、Slim3などで分類される軽量フレームワークについて利点をメインに述べてきた。
ここからは具体的に実装するときにどうやって実装していくのか紹介していく。

詳しいことは[このサイト](https://www.slimframework.com)に全て書いてある。
ただ、結構推測しないといけない箇所があり、本当に実装するときに知っていると得する部分などを踏まえて、本当にサービス稼働させる設計を考えていく。

*実装については基本的にPHP5.6以上を想定している。本当はPHP7.1以上の方が型を指定できるので、より型に厳密なプログラムを書くことができる。
*また、PHP5.6以上の実行環境はすでに構築されていることを前提に記事を書く。Macであればもとから環境がすでに構築されているはずである。
*slim-skeletonに関しては個人的に非推奨である。Slim3はコーディングの柔軟性が売りの一つなので、スケルトンを使うとそのメリットが一つ減るためである。

## 最低限の初期実装
まず、公式ドキュメントのコードを実際に動かしてみる。

```setup.sh
 $ mkdir testSlim
 $ cd testSlim/
 $ mkdir public
 $ cd public/
 $ vim index.php
```
これで、index.phpの中には以下のコードを記述する。
以下のコードは[こちら](https://www.slimframework.com)の公式ドキュメントを参考に一部変更している。

```public/index.php
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require __DIR__ . '/../vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response) {
    $name = $request->getAttribute('name');
    $response->getBody()->write("Hello, $name");

    return $response;
});
$app->run();
```
最後にルートディレクトリに戻って、以下のコマンドを実行してSlim3フレームワークのライブラリをcomposerを使ってダウンロードする。

```setup.sh
$ cd ..
$ composer require slim/slim "^3.0"
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 6 installs, 0 updates, 0 removals
  - Installing psr/container (1.0.0): Loading from cache
  - Installing container-interop/container-interop (1.2.0): Loading from cache
  - Installing nikic/fast-route (v1.2.0): Loading from cache
  - Installing psr/http-message (1.0.1): Loading from cache
  - Installing pimple/pimple (v3.2.2): Loading from cache
  - Installing slim/slim (3.9.2): Downloading (100%)
Writing lock file
Generating autoload files
```
これでSlim3に依存する6つのライブラリをダウンロードできたので、実際に実行する。
実行するときには以下のビルトインウェブサーバーを起動する。

```setup.sh
php -S localhost:8080 -t public public/index.php
```

Safariなどのブラウザを起動して、 *http://localhost:8080/hello/test* にアクセスする。
すると、「Hello, test」と表示される。
これで、基本的にはSlim3を動かせたことになる。
初心者でも10分程度でSlim3を起動できただろう。

ディレクトリ構成は以下の通りである。

```
.
├── composer.json
├── composer.lock
├── public
│   └── index.php
└── vendor
    └── 省略
```
## ルーティングテーブルをLaravel風にする
今の実装だとルーティングをindex.phpに全て記述してしまうため、エンドポイントが増えたときにindex.phpが肥大化するので、ルートディレクトリにRoutes.phpを置くことにする。
また、ルーティングテーブルにクロージャを書くと同様にルーティングテーブルが肥大化してしまうので、Controllerに投げるようにする。
Controllerディレクトリをそのために作成する。

```public/index.php
<?php

require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../Controller/SampleController.php';
require __DIR__ . '/../Routes.php';

```

```Routes.php
<?php

$app = new \Slim\App;
$app->get('/hello/{name}', SampleController::class . ':index');
$app->run();
```

```Controller/SampleController.php
<?php

use Slim\Http\Request;
use Slim\Http\Response;

class SampleController
{
    public function index(Request $request, Response $response)
    {
        $name = $request->getAttribute('name');
        $response->getBody()->write("Hello, $name");
        return $response;
    }
}
```

このようにルーティングテーブルを一つのエンドポイントごとに1行にまとめると使いやすくなる。
ディレクトリ構成は以下の通りである。

```
.
├── Controller
│   └── SampleController.php
├── Routes.php
├── composer.json
├── composer.lock
├── public
│   └── index.php
└── vendor
    └── 省略
```

## WebAPIを作ってみよう
Slim3の特徴はなんといってもWebAPIの実装の容易さである。
素早く安全なWebAPIを作るならSlim3を是非オススメする。
まず、Controller/SampleController.phpを少し修正してみる。
WebAPIは最近ではjsonで一般的にやり取りされるので、送信データをjsonにする。
といっても、元々SlimにはwithJson()メソッドがResponseオブジェクトにあるため、それを呼び出して配列を引数に取るだけで使える。
では早速、Controller/SampleController.phpを修正しよう！

```Controller/SampleController.php
<?php

use Slim\Http\Request;
use Slim\Http\Response;

class SampleController
{
    public function index(Request $request, Response $response)
    {
        $name = $request->getAttribute('name');
        $response = $response->withJson(["response" => "Hello, $name"], 200);
        return $response;
    }
}
```

それではビルトインウェブサーバーを起動して実際に確認してみよう！
以下のレスポンスデータが受け取れるはずだ。
これでWebAPIも作成できた。

```
{"response":"Hello, aaa"}
```

## Viewにテンプレートエンジンを使う
Viewに関してはとくに今まで記載していなかったが、動的なWebページを作成する場合にテンプレートエンジンというPHPを直書きするのではなく、もう少し人間にわかりやすい形でViewを作成する方法がある。実際にはそのファイルを読み込む時にphpのプログラムにコンパイルする仕組みである。
今回はこのテンプレートエンジンの中でもBladeというLaravelに採用されているテンプレートエンジンをSlim3で読み込んでみる。

### Bladeの設定
まず、Bladeのライブラリをダウンロードする。

```setup.sh
composer require rubellum/slim-blade-view
```

Routes.phpとController/SampleController.phpを書き換える。
views/sample.blade.phpを新規で作成する。
viewsとcacheディレクトリは新規で作成しとく！

```Routes.php
<?php

$config = [
    'settings' => [
        'displayErrorDetails' => true, 
        'renderer'            => [
            'blade_template_path' => __DIR__ . '/views',
            'blade_cache_path'    => __DIR__ . '/cache', 
        ],
    ],
];

$app = new \Slim\App($config);

$container = $app->getContainer();

$container['view'] = function ($container) {
    return new \Slim\Views\Blade(
        $container['settings']['renderer']['blade_template_path'],
        $container['settings']['renderer']['blade_cache_path']
    );
};

$app->get('/hello/{name}', SampleController::class . ':index');

$app->run();
```

```Controller/SampleController.php
<?php

use Slim\Container;
use Slim\Http\Request;
use Slim\Http\Response;

class SampleController
{
    private $app;

    public function __construct(Container $app)
    {
        $this->app = $app;
    }

    public function index(Request $request, Response $response)
    {
        $name = $request->getAttribute('name');
        $array = ['test' => "Hello, $name"];
        return $this->app->view->render($response, 'sample', $array);
    }
}
```

```views/sample.blade.php
{{$test}}
```
これで、ビルトインウェブサーバーを起動して実際に確認してみる！
これでBladeを使える環境ができた。

ディレクトリ構成は以下の通りである。

```
.
├── Controller
│   └── SampleController.php
├── Routes.php
├── cache
│   └── f8c721f9c9249264c7cecf259cdb1cd760a59ffa.php
├── composer.json
├── composer.lock
├── public
│   └── index.php
├── vendor
│   └── 省略
└── views
    └── sample.blade.php
```

# セキュリティ
Slim3はとてもミニマムなフレームワークであるためにセキュリティに関しても最低限の水準に留めてある。
例えば、Webアプリケーションを作成する時にcsrfの対策をするためにcsrfトークンを発行しなければいけない時は公式がサポートしている[slimphp/Slim-Csrf](https://github.com/slimphp/Slim-Csrf)というライブラリを利用してcsrf対策を行なう。
参考に実装方法を掲載してあるサイトで一番わかりやすかった記事のリンクを提示している。

# おわりに
今回は、Slim3フレームワークをガンガン推す記事+簡単な使い方ということに主眼をおいて説明した。
これらのことは公式ドキュメントを読めば簡単に理解できることである。
今後はSlim3自体からはORMも提供されていないので、Slim3と親和性の高いORMについてなども記事を書こうと考えている。
この記事はまだまだ不十分なので、今後より良いものに改良・修正していく予定である。


# 参考
[Slim Framework](https://www.slimframework.com)
[CSRF Protection in Slim 3 PHP Framework](https://dzone.com/articles/csrf-protection-in-slim-3-php-framework)
[CSRF対策のトークンをワンタイムにしたら意図に反して脆弱になった実装例](https://blog.tokumaru.org/2011/01/anti-csrf-onetime-token-failure-case.html)
[クロスサイトリクエストフォージェリ（CSRF）[トークン]](http://noumenon-th.net/programming/2016/02/03/csrf/)