OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

本番環境でもSymfony2のデバッグツールバー(Profiler)が見たい

ども。フジロックでは battles がベストアクトだと思っている @kalibora です。小ネタです。

Symfony2の開発時に下に出てくるアレ。すごい便利ですよね。

クリックすると↓こんな感じの画面になって

f:id:kalibora:20160920214502p:plain

DBへのクエリも確認できますし、簡易的にボトルネックがどこかも調べたりできます。

そんな便利なツールバー、本番環境でも見たいなーって時たまにありませんか?

基本的には開発環境だけで事足りるはずですけど、本番と開発環境でのデータ量の差だったりが影響して 本番環境でDBへのクエリがどうなってるのか確認したいとかとか。

とはいえ、本番環境でツールバーを表示するわけにはいきません。(セキュリティの問題とパフォーマンスの問題)

じゃあどうするか?それ用の環境を作ってしまいましょう。

幸い公式ドキュメントにそのようなときにぴったりな解説ページがあります。

これ → How to Master and Create new Environments (current)

でもですね、この通りにやってもツールバーが出ないのでちょっとだけ足してやる必要があります。

diffにすると以下のようになります。

diff --git a/app/AppKernel.php b/app/AppKernel.php
index 823d7a2..082e6fb 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -26,6 +26,10 @@ class AppKernel extends Kernel
             $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
         }

+        if (in_array($this->getEnvironment(), ['benchmark'], true)) {
+            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+        }
+
         return $bundles;
     }

diff --git a/app/config/config_benchmark.yml b/app/config/config_benchmark.yml
new file mode 100644
index 0000000..3a6892d
--- /dev/null
+++ b/app/config/config_benchmark.yml
@@ -0,0 +1,10 @@
+imports:
+    - { resource: config_prod.yml }
+
+framework:
+    profiler: { only_exceptions: false }
+    router:
+        resource: "%kernel.root_dir%/config/routing_benchmark.yml"
+
+web_profiler:
+    toolbar: true
diff --git a/app/config/routing_benchmark.yml b/app/config/routing_benchmark.yml
new file mode 100644
index 0000000..172205b
--- /dev/null
+++ b/app/config/routing_benchmark.yml
@@ -0,0 +1,10 @@
+_wdt:
+    resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml"
+    prefix:   /_wdt
+
+_profiler:
+    resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml"
+    prefix:   /_profiler
+
+_main:
+    resource: routing.yml
diff --git a/web/app_benchmark.php b/web/app_benchmark.php
new file mode 100644
index 0000000..8852777
--- /dev/null
+++ b/web/app_benchmark.php
@@ -0,0 +1,20 @@
+<?php
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * @var Composer\Autoload\ClassLoader
+ */
+$loader = require __DIR__.'/../app/autoload.php';
+include_once __DIR__.'/../var/bootstrap.php.cache';
+
+$kernel = new AppKernel('benchmark', false);
+$kernel->loadClassCache();
+//$kernel = new AppCache($kernel);
+
+// When using the HttpCache, you need to call the method in your front controller instead of relying on the configuration parameter
+//Request::enableHttpMethodParameterOverride();
+$request = Request::createFromGlobals();
+$response = $kernel->handle($request);
+$response->send();
+$kernel->terminate($request, $response);

これで app_benchmark.php にアクセスすればツールバーが表示されます。

さぁ、っとここまでできたところでこれを実際に本番環境にデプロイして公開してしまってはセキュリティ的に問題なので、そこはお気をつけください。

きっと社内からしかアクセスできないようなステージング環境をみなさま用意していると思いますので、 そういうところだけにデプロイして使えば、 当初の目的だった本番環境(に限りなく近い環境)でデバッグツールバーが見れます。

それではみなさまごきげんよう。

オフィスで手巻き寿司&勝手丼♪開発チームで月次「締め会」はじめました!

こんにちは!デザイナーのぺろこです。

タイトル通りですが、開発チームで月次の締め会をはじめてみました!

OTOBANK開発チームでは、"イケてるリリースを自慢(どやぁ!)しあう会"という意味で、
締め会を「どや!会」という名前で呼んでいます。

「どや!会」とは

  • 月に1回、月末に開催
  • 開発メンバー全員でご飯を食べながら、今月のリリースをふりかえります
  • 1ヶ月の働きをねぎらうべく、毎回おいしいご飯を用意しています

「どや!会」のアジェンダ

  1. 乾杯
  2. 今月のリリース共有
  3. 雑談

「どや!会」は乾杯からはじまりますw
リリースの共有では、GithubのcloseされたPullRequest一覧と事前資料をベースに行います。
事前資料には自主的に書き込む項目があり、「これはイケてるっしょ!」というものを各自書き込み&発表(どやぁ)してもらいます。
発表のあとは、もぐもぐ・・・うまぁ・・・( ´ ▽ ` )

9月の「どや!会」ごはん

そろそろイクラが美味しい季節・・・ということで、CTOの@riafさんが大量のイクラを漬けてきてくれました!
北海道では自宅でイクラを漬けるのが当たり前だそうです。すごい。

f:id:peroko_tokyo:20161004005019j:plain みんなで手巻き寿司、楽しい!

f:id:peroko_tokyo:20161003193208j:plain どんなにかけてもイクラがなくならない・・・・しらすとの相性も抜群!!

f:id:peroko_tokyo:20161003193216j:plain 釧路のお酒もおいしい〜!

f:id:peroko_tokyo:20161003193245j:plain 釧路のご当地グルメ「スパカツ」は@symmt9302の手作り!

どや会のねらい

  1. 今月も頑張ったよね!と自分たちを労って、来月の活力にすること
  2. 良い仕事を共有して高め合うこと

オトバンクの場合は、自社サービスの開発がほとんど。
自社サービスの開発は、受託開発の仕事と比べると、

  • 「改善」という大きなタスクが消えることはない
  • 受注金額がないので、開発したものの価値が見えにくい

という点で、「達成感」や「区切り」みたいなものが作り出しにくいなーと感じます。

人間は機械じゃないので、モチベーションを維持するには何らかの仕組みが必要ですよね。
そこで、締め会という仕組みを取り入れてみた次第でした。

6月からはじめた試みなので、実は9月で4回目。明確に「ooがxxになった!」とは書きづらいのですが、さっそく雰囲気が変わったというか、"チーム感"のようなものが生まれてきている感じがしています。

いいことだな〜😸
来月の「どや!会」は、どんなメニューにしようかな。

Symfony2での処理の流れについてまとめた

こんにちは!@mrtryです。 最近、スモークチーズのオイル漬けを作りました! チーズを燻製して、オリーブオイルにつけるだけでですが、とても美味しく、ハイボールが進みます。 燻製してみたいなぁ〜と、考えている人には、おすすめです!

さて、「Symfony2入門」2回目の投稿です。 前回は、全体としての基本動作をざっくり説明しました。 今回は、RequestオブジェクトからResponseオブジェクトを生成するまでの処理をもう少し掘り下げて行きたいと思います。

全体の処理の流れ

公式ドキュメントのフローチャートを参考に全体の処理の流れを追ってみましょう。 全体の処理の流れ

Symfonyでは、すべてのリクエストの処理が以下の流れに従っています。

  1. URLへのアクセスをフロントコントローラで受ける
  2. フロントコントローラ(app.php)が受けたリクエストからRequestオブジェクトを生成する
  3. カーネルがリクエストがあったURLをルータに知らせる
  4. ルータがURLにマッチするコントローラなどをまとめた「情報」をカーネルに返す
  5. カーネルがルータから受け取った情報を元にコントローラを実行する
  6. コントローラがResponseオブジェクトを返す

データを処理をするのはコントローラ、どのコントローラかを選択するのはルータ、コントローラとルータに依頼をするのがカーネル、と役割が分かれています。 それぞれについて、役割を確認していきます。

カーネル

カーネルは、クライアントからのRequestオブジェクトを受け取り、コントローラにそれを渡し、Response オブジェクトに変換します。 コードでは、フロントコントローラ(app.php(app_dev.php))にて呼びだされています。 気になった方は、前回の記事の終わりの方を見てみてください

ルータ

「このURLにアクセスがあったら、このコントローラを呼び出す」というURLのパスとコントローラを関連付け(マッピング)を行っています。 マッピングの設定は、設定ファイルかコントローラのPHPファイルに書くことで設定できます。

例として、前回も参考にしたsymfonyのデモアプリケーションの設定ファイルを見てみます。 設定ファイルは、app/config/routing.ymlにあります。 http://127.0.0.1:8000/にアクセスがあった時、 default/homepage.html.twigを引数に、FrameworkBundleTemplateControllerにあるtemplateActionを実行するという設定が書かれています。

homepage:
    path: /{_locale}
    requirements:
        _locale: '%app_locales%'
    defaults:
        _controller: FrameworkBundle:Template:template
        template:    default/homepage.html.twig
        _locale:     '%locale%'

コントローラ

コントローラは、Requestオブジェクトから情報を取得し、Responseオブジェクトを生成する関数群です。 ページのレンダリングやリダイレクト、画像などwebにアクセスして取得できる情報を任意にResponseオブジェクトとして返すことができます。

こちらも、実際にコントローラを見てみましょう。 見るコントローラは、先ほどルータで設定されていた、FrameworkBundleにあるTemplateControllerです。 vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/TemplateController.phpに実物があります。 このコントローラでは、引数で受け取ったファイルをレンダリングして、Responseとして返すという処理をしています。

<?php
namespace Symfony\Bundle\FrameworkBundle\Controller;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Response;

/**
 * TemplateController.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TemplateController implements ContainerAwareInterface
{
    use ContainerAwareTrait;

    /**
     * Renders a template.
     *
     * @param string    $template  The template name
     * @param int|null  $maxAge    Max age for client caching
     * @param int|null  $sharedAge Max age for shared (proxy) caching
     * @param bool|null $private   Whether or not caching should apply for client caches only
     *
     * @return Response A Response instance
     */
    public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null)
    {
        /** @var $response \Symfony\Component\HttpFoundation\Response */
        $response = $this->container->get('templating')->renderResponse($template);

        if ($maxAge) {
            $response->setMaxAge($maxAge);
        }

        if ($sharedAge) {
            $response->setSharedMaxAge($sharedAge);
        }

        if ($private) {
            $response->setPrivate();
        } elseif ($private === false || (null === $private && ($maxAge || $sharedAge))) {
            $response->setPublic();
        }

        return $response;
    }

あわせて、実行時にレンダリングされるファイルを見てみましょう。 ルータの設定で、引数には、default/homepage.html.twigが設定されていました。 実際のファイルパスとしては、app/Resources/views/default/homepage.html.twigにあります。 http://127.0.0.1:8000/にアクセスされた際は、このファイルがレンダリングされて表示されます。

{% extends 'base.html.twig' %}

{% block body_id 'homepage' %}

{#
    the homepage is a special page which displays neither a header nor a footer.
    this is done with the 'trick' of defining empty Twig blocks without any content
#}
{% block header %}{% endblock %}
{% block footer %}{% endblock %}

{% block body %}
    <div class="page-header">
        <h1>{{ 'title.homepage'|trans|raw }}</h1>
    </div>

    <div class="row">
        <div class="col-sm-6">
            <div class="jumbotron">
                <p>
                    {{ 'help.browse_app'|trans|raw }}
                </p>
                <p>
                    <a class="btn btn-primary btn-lg" href="{{ path('blog_index') }}">
                        <i class="fa fa-users"></i> {{ 'action.browse_app'|trans }}
                    </a>
                </p>
            </div>
        </div>

        <div class="col-sm-6">
            <div class="jumbotron">
                <p>
                    {{ 'help.browse_admin'|trans|raw }}
                </p>
                <p>
                    <a class="btn btn-primary btn-lg" href="{{ path('admin_index') }}">
                        <i class="fa fa-lock"></i> {{ 'action.browse_admin'|trans }}
                    </a>
                </p>
            </div>
        </div>
    </div>
{% endblock %}

例: トップページが表示されるまでの処理の流れ

実際に、ビルドインサーバを立ち上げて、http://127.0.0.1:8000/にアクセスすると、homepage.html.twigがレンダリングされたものが表示されます。

f:id:symmt9302:20160816104405p:plain

アクセスしてから、ページが表示するまでの流れを追うと以下のようになります。

  1. ユーザがhttp://127.0.0.1:8000/にアクセスする
  2. リクエストフロントコントローラ(app.php)が受け、カーネルにRequestオブジェクトを渡す
  3. カーネル/にアクセスがあったと、ルータに知らせる
  4. ルータが、設定ファイルを元にTemplateController.phpのtemplateActionを実行すれば良いと情報をカーネルに知らせる
  5. カーネルが情報にあったコントローラのアクションを実行する
  6. アクションした結果、ページがレンダリングされ、その情報はResponseオブジェクトとして生成される
  7. Responseオブジェクトを返す
  8. ユーザがページを見る

おわりに

今回は、Symfonyの処理流れについて、書きました。 次回は、ルータの設定方法について書きたいと思います。

参考