日頃の行い

個人的な日頃の行いをつらつら書いてます\\\\ ٩( 'ω' )و ////

PHPのLumenフレームワークでテンプレートエンジンとしてtwigを利用する

Laravel製のマイクロフレームワークLumenにちょっと触ってみました。
その時にテンプレートエンジンにTwigは使えないのかなぁと思ったので色々試してみた時の話です。

lumen.laravel.com

LumenではデフォルトのテンプレートとしてLaravelのBladeが利用されています。
個人的にいつもTwigをテンプレートエンジンとして利用していたので、Twigを使うにはどうすれば良いのかなぁと思って方法を探しました。
探してみたところTwigBridgeというものが見つかりました。
Lumenでの使い方も書かれていたので備忘録としてそのやり方を書いておこうかなと思います。

github.com

やること

  1. Lumenのインストール
  2. LumenのConfigを設定
  3. templateとrouting記述
  4. 動かしてみる

1. Lumenのインストール

こちらを参考にインストールしました。
http://lumen.laravel.com/docs/installation

ざっとやってみようと思います。
環境にはこちらのDockerコンテナ利用しました。
https://hub.docker.com/r/tarata/centos6-with-php56-node/

$curl -sS https://getcomposer.org/installer | php
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: //composer.phar
Use it: php composer.phar
$php composer.phar global require "laravel/lumen-installer=~1.0"
Changed current directory to /root/.composer
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing symfony/process (v2.7.3)
    Downloading: 100%

  - Installing symfony/console (v2.7.3)
    Downloading: 100%

  - Installing react/promise (v2.2.1)
    Downloading: 100%

  - Installing guzzlehttp/streams (3.0.0)
    Downloading: 100%

  - Installing guzzlehttp/ringphp (1.1.0)
    Downloading: 100%

  - Installing guzzlehttp/guzzle (5.3.0)
    Downloading: 100%

  - Installing laravel/lumen-installer (v1.0.1)
    Downloading: 100%

symfony/console suggests installing symfony/event-dispatcher ()
symfony/console suggests installing psr/log (For using the console logger)
Writing lock file
Generating autoload files
$env PATH=$PATH:$HOME/.composer/vendor/bin lumen new lumen-with-twig
Crafting application...
Application ready! Build something amazing.
$tree -L 2
.
└── lumen-with-twig
    ├── app
    ├── artisan
    ├── bootstrap
    ├── composer.json
    ├── composer.lock
    ├── database
    ├── phpunit.xml
    ├── public
    ├── readme.md
    ├── resources
    ├── server.php
    ├── storage
    ├── tests
    └── vendor
$cd lumen-with-twig
# composer.pharへのPATHは適当です
$php /path/to/composer.phar require rcrowe/twigbridge 0.8.x
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing twig/twig (v1.21.1)
    Downloading: 100%

  - Installing rcrowe/twigbridge (v0.8.1)
    Downloading: 100%

rcrowe/twigbridge suggests installing twig/extensions (~1.0)
rcrowe/twigbridge suggests installing laravelcollective/html (For bringing back html/form in Laravel 5.x)
Writing lock file
Generating autoload files

これで準備完了。

2.LumenのConfigを設定

2つやることがあります。

  1. /bootstrap/app.phpに設定を追加。
  2. Configファイルの追加

70行目あたりのService Providersに追記。
/bootstrap/app.php

<?php
// ...省略

/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/

// $app->register(App\Providers\AppServiceProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);

$app->configure('twigbridge'); //ここと
$app->register('TwigBridge\ServiceProvider'); //ここを追記

/vendor/rcrowe/twigbridge/config/twigbridge.phpをconfigディレクトリに追加します。
はじめから用意されてないので、作らないといけないとかちょっとハマりました。
Auth, Translator, Urlの拡張機能を無効化しないと使えないようなので、それを無効にする設定を書くための追加です。

$mkdir -p config
$cp vendor/rcrowe/twigbridge/config/twigbridge.php config/
$vi config/twigbridge.php

100行目あたりにExtensionが書いてあるのでそこの記述をコメントアウトします。

<?php
...省略

    'extensions' => [

        /*
        |--------------------------------------------------------------------------
        | Extensions
        |--------------------------------------------------------------------------
        |
        | Enabled extensions.
        |
        | `Twig_Extension_Debug` is enabled automatically if twig.debug is TRUE.
        |
        */
        'enabled' => [
            'TwigBridge\Extension\Loader\Facades',
            'TwigBridge\Extension\Loader\Filters',
            'TwigBridge\Extension\Loader\Functions',

//            'TwigBridge\Extension\Laravel\Auth', ここと
            'TwigBridge\Extension\Laravel\Config',
            'TwigBridge\Extension\Laravel\Dump',
            'TwigBridge\Extension\Laravel\Input',
            'TwigBridge\Extension\Laravel\Session',
            'TwigBridge\Extension\Laravel\String',
//            'TwigBridge\Extension\Laravel\Translator', ここと
//            'TwigBridge\Extension\Laravel\Url', ここ

            // 'TwigBridge\Extension\Laravel\Form',
            // 'TwigBridge\Extension\Laravel\Html',
            // 'TwigBridge\Extension\Laravel\Legacy\Facades',
        ],

これで設定も完了。

3. templateとrouting記述

lumenのテンプレートは/resources/viewsディレクトリに置くことになっているので、そちらにTwigのテンプレートを追加します。
bladeの場合、hogehoge.blade.phpのように拡張子phpですが、twigはいりません。
/resources/views/hello.twig

<h1>{{hello}}</h1>

そして、そのテンプレートを利用するように/app/Http/routes.phpへと追記します。

<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/

$app->get('/', function () use ($app) {
    return $app->welcome();
});

//ここを追記
$app->get('/hello', function () use ($app) {
    return view('hello', ['hello' => 'world']);
});

これで動くはず。

4.動かしてみる

画面で写すほどのものでもないので、curlを叩きました。

$php artisan serve &
Lumen development server started on http://localhost:8000/
$curl localhost:8000/hello
<h1>world</h1>

動いた。
でもまあ最近bladeを使っている限りそんなに大変でもなかったので、bladeでもいいんじゃないかなって思いました。
bladeとtwigどんな違いがあるのかなぁ。

LumenというLaravel製マイクロフレームワーク(?)をSilexとSlimと比較してみた

LumenというLaravelのコンポーネントを利用したフレームワークが出たので、ちょっと触ってみました。

lumen.laravel.com

LumenはSilexやSlimに比べてREQUESTS PER SECONDの値が高いらしいですね。
なので、今回はsiegeコマンドを利用してそれらのベンチを取ってみました。
利用したレポジトリはこちらです。

github.com

※ベンチそんな取ったことないんで分析甘いかもしれませんがご了承いただきたいです。

Slim
Slim Framework

Silex
Homepage - Silex - The PHP micro-framework based on Symfony2 Components

流れ

  1. それぞれインストール
  2. 環境について
  3. siegeコマンド実行
  4. 所感

1.それぞれインストール

一応マイクロフレームワークらしいですが、
インストールはどれくらいかかるのでしょうか。
標準出力をそのままコピってみました。
※Lumenは composer create-project laravel/lumen --prefer-distを行ったあと、
vendorを消して、composer installしました。

Lumenインストール
https://gist.github.com/tarata/e51138cc3d1575bf034f

Silex インストール
https://gist.github.com/tarata/555b27d621a1c91a7606

Slim インストール
https://gist.github.com/tarata/63c84364dc388dadcb6f

Lumen長っ!
Slim短っ!!
これほんとにマイクロなんだろうか。

2.環境について

テスト環境についてです。
個人のさくらVPS上に乗っけて、PHPのビルトインサーバでそれぞれ起動してsiegeにかけてみました。
Lumenはデフォルトのwelcomeページ
SilexはHello Silexという文字のみ
SlimもHello Slimという文字のみ

siegeコマンドについてはこちらなどを参考に・・・

qiita.com

さくらVPS

  • メモリ 1 GB
  • ディスクHDD 100 GB
  • CPU 2コア

PHP

$php -v
PHP 5.4.39 (cli) (built: Mar 20 2015 08:10:43)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
    with Xdebug v2.3.2, Copyright (c) 2002-2015, by Derick Rethans

※ siegeの対象にlocalhostにsiegeがなぜか使えなかったので
 /etc/hostsに下記のように書いて対応

127.0.0.1	silex.local.com
127.0.0.1	slim.local.com
127.0.0.1	lumen.local.com

ちなみにこんなエラーが出ました。

$siege -g http://localhost:8000/
[error] socket: -928823552 connection refused.: Connection refused

3.siege実行

Transaction rateの値をとりあえず見ていこうかと思います。

Lumen

$siege -c 10 -t10s http://lumen.local.com:8000/

Transactions:		         181 hits
Availability:		      100.00 %
Elapsed time:		        9.20 secs
Data transferred:	        0.25 MB
Response time:		        0.04 secs
Transaction rate:	       19.67 trans/sec
Throughput:		        0.03 MB/sec
Concurrency:		        0.86
Successful transactions:         181
Failed transactions:	           0
Longest transaction:	        0.13
Shortest transaction:	        0.01

$siege -c 1000 -t10s http://lumen.local.com:8000/

Transactions:		         608 hits
Availability:		      100.00 %
Elapsed time:		        9.18 secs
Data transferred:	        0.82 MB
Response time:		        2.15 secs
Transaction rate:	       66.23 trans/sec
Throughput:		        0.09 MB/sec
Concurrency:		      142.45
Successful transactions:         608
Failed transactions:	           0
Longest transaction:	        8.99
Shortest transaction:	        0.00

なるほどなるほどー
1個目なのでよくわからないですね。

Silex

$siege -c 10 -t10s http://silex.local.com:8000/

Transactions:		         155 hits
Availability:		      100.00 %
Elapsed time:		        9.52 secs
Data transferred:	        0.00 MB
Response time:		        0.11 secs
Transaction rate:	       16.28 trans/sec
Throughput:		        0.00 MB/sec
Concurrency:		        1.79
Successful transactions:         155
Failed transactions:	           0
Longest transaction:	        0.42
Shortest transaction:	        0.03

$siege -c 1000 -t10s http://silex.local.com:8000/

Transactions:		         254 hits
Availability:		      100.00 %
Elapsed time:		        9.90 secs
Data transferred:	        0.00 MB
Response time:		        4.09 secs
Transaction rate:	       25.66 trans/sec
Throughput:		        0.00 MB/sec
Concurrency:		      104.92
Successful transactions:         254
Failed transactions:	           0
Longest transaction:	        9.32
Shortest transaction:	        0.00

お、Silexちょっと負けましたね。

Slim

$siege -c 10 -t10s http://slim.local.com:8000/

Transactions:		         182 hits
Availability:		      100.00 %
Elapsed time:		        9.63 secs
Data transferred:	        0.00 MB
Response time:		        0.04 secs
Transaction rate:	       18.90 trans/sec
Throughput:		        0.00 MB/sec
Concurrency:		        0.72
Successful transactions:         182
Failed transactions:	           0
Longest transaction:	        0.15
Shortest transaction:	        0.01

$siege -c 1000 -t10s http://slim.local.com:8000/
Transactions:		         585 hits
Availability:		      100.00 %
Elapsed time:		        9.42 secs
Data transferred:	        0.01 MB
Response time:		        2.18 secs
Transaction rate:	       62.10 trans/sec
Throughput:		        0.00 MB/sec
Concurrency:		      135.40
Successful transactions:         585
Failed transactions:	           0
Longest transaction:	        8.75
Shortest transaction:	        0.02

Slimはやはり特になにも入ってないので強いですね。

concurrency 10の時

Lumen 19.67 trans/sec
Silex 16.28 trans/sec
Slim 18.90 trans/sec

concurrency 1000の時

Lumen 66.23 trans/sec
Silex 25.66 trans/sec
Slim 62.10 trans/sec


Silexがダメなように見える・・・w
確かにLumen多くさばけそうですね。

4. 所感

なんとなく試してみたかったので、ベンチ取ってみました。
回数そんな多くやってないので、正しいものがとれたかどうか確実ではないですが、ほとんど何も入っていないSlimと近いスピードなので、Lumenのホームページ上で言ってることは本当なのかなと思います。
インストール時に色んな物が突っ込まれていたので、本当にマイクロフレームワークなのかちょっとわからないですが、なにかあるときはちょっと触ってみようかなと思います。

PHP製マイクロフレームワークSlimで404ページ500ページを表示させるだけ

タイトルの通りの事をしようとしていたら
404ページの内容を考え始め
気がついたらBLEACHを読んでいました。
いつからコードを書いていると錯覚していたのでしょう。


さて、Slimでエラーハンドリング試してみました。
ドキュメントが完璧だったので特に面白みもなかったです←
Slim Framework Documentation

Slimとは・・・

PHP製のマイクロフレームワークでとても簡単に使えます。
細かいことは書きません。
以下のリンク先にもあるようにわずか数行でHello Worldが完成します。
Slim Framework

<?php

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

// 初期化・設定

$app = new \Slim\Slim();

// ルーティング

$app->get('/', function () {
    echo "Hello world";
});

// 実行

$app->run();

はい。

何がしたかったか

404とか500とかのページを出したかったんです。
Slim エラーハンドリング
http://docs.slimframework.com/#Error-Handling-Overview

結果

<?php

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

// 初期化・設定

$app = new \Slim\Slim();

$app->config(array(
    'templates.path' => '../templates',
    'debug' => false,
));

// エラーハンドリング

$app->notFound(function () use ($app) {
    $app->render('404.html');
});

$app->error(function (\Exception $e) use ($app) {
    $app->render('500.html');
});

// ルーティング

$app->get('/', function () {
    echo "Hello world";
});

$app->get('/notfound', function() use ($app) {
    $app->notFound();
});

$app->get('/error', function() {
    throw new Exception();
});

$app->get('/error/wazato', function() use ($app) {
    try {
        throw new \Exception('わざとのえらー');
    } catch( \Exception $e ) {
        $app->error($e);
    }
    echo 'ここには来ない';
});

// 実行

$app->run();

これと404.htmlと500.htmlを追加するだけです。

$app->config(array(
    'templates.path' => '../templates',
    'debug' => false, // ここの設定
));

debugをfalseとすることによってエラーページを500ページにすることが出来ます。
これでもし仮に万が一Fatalエラーとか起きてたとしても500ページを出すことが出来ますね!

わざとnotfoundやerrorを実行したい時は以下のようにすればいいようです。

$app->get('/notfound', function() use ($app) {
    $app->notFound();
});

$app->get('/error/wazato', function() use ($app) {
    try {
        throw new \Exception('わざとのえらー');
    } catch( \Exception $e ) {
        $app->error($e);
    }
    echo 'ここには来ない';
});

やっぱフレームワークなら大体揃ってますね。

今回検証に使ったレポジトリ
tarata/slim-error-handling-try · GitHub

twigテンプレートエンジンでvue.jsを使うとデリミタがコンフリクトする。

silex+twigでvue.jsを使ってみようと思ったら
'{{ message }}' がtwigのdelimiterとして認識されてしまってエラーになった。

twigとは
Homepage - Twig - The flexible, fast, and secure PHP template engine

エラー内容

Twig_Error_Runtime: Variable "message" does not exist in "index.twig" at line 6

とりあえずソースコードを見てみると

index.twig
{% extends 'base.twig' %}
{% block body %}
<h1>messageを入力</h1>
<input type="text" v-model='message' />
<input type="button" value='送信' v-on='click: addMessage'>
{{message}} <!-- ここがtwigの文法として評価されてしまっている -->
<ul>
    <li v-repeat="messages" v-transition>
    {{content}}
    </li>
</ul>
{% endblock %}
base.twig
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/common.css">
    <script src='/components/vue/dist/vue.js'></script>
    <title>hoge</title>
</head>
<body>
    <div id="demo">
        {% block body %}
        {% endblock %}
    </div>
<script src='/js/common.js'></script>
</body>
</html>

どうしよう。。。って思って、
twig angularjs delimiter
とか
twig delimiter
とかでググったら以下のURLが見つかりました。

AngularJS-Twig conflict with double curly braces - Stack Overflow

How to escape Twig delimiters in a Twig template? - Stack Overflow

twig側に{% raw %} {% endraw %}とかがあるんですね!

index.twig (修正後)
{% extends 'base.twig' %}
{% block body %}
<h1>messageを入力</h1>
<input type="text" v-model='message' />
<input type="button" value='送信' v-on='click: addMessage'>
{% raw %} <!-- ここと -->
{{message}}
<ul>
    <li v-repeat="messages" v-transition>
    {{content}}
    </li>
</ul>
{% endraw %} <!-- ここ -->
{% endblock %}

既に用意されててよかった。。。