SymfonyはなぜTwigを採用したのか ~ Jinjaとの関係

Symfony Advent Calendar 2018 10日目の記事です。

SymfonyはテンプレートエンジンにTwigを採用しています。TwigはPython製のJinjaにシンタックスが似ていることで知られています。SymfonyがなぜTwigを採用したのか、なぜJinjaと似ているのかについて日本語の情報が少なそうなので書いてみようと思います。

ただ、内容としてはほとんどSymfonyの作者であるFabien Potencierのブログ記事で触れられているものです。

Templating Engines in PHP | Articles - Fabien Potencier

この記事は2009年10月にポストされたものです。この時のSymfony(当時はsymfony)のバージョンは1.3で11月に1系最後である1.4がリリースされています。彼がこの時期にどのようなテンプレートが望ましいか考え悩んだ結果が吐露されています。

記事の最初に「私と意見が合わなくて殺したい人がいるかも」という注意が書いてあることから当時のテンプレートエンジンに対する議論の白熱?具合が伺えます。

記事に書かれていること

PHP自体が良いテンプレートとは思えない

この記事の前半ではDjangoPythonのウェブフレームワーク)のテンプレートと比較して次のようなことが書かれています。

  • PHP自体がテンプレートだという主張があるが…
    • PHPは(言語としては進化してるが)テンプレートとして進化していない
      • 短く書けない
      • ありがちな処理をサポートしていない(例えばfor-else)
      • 再利用性がない(継承など)
        • 注: クラス継承ではなくテンプレート継承の意です
      • エスケープが大変(DjangoRailsは自動エスケープなのに)
      • サンドボックス化できない

PHPの既存テンプレートに良いものがない

中盤では既存のPHP製テンプレートエンジンを探してみたけどいいのがない、ということが書かれています。Smarty、PHPTAL、eZ Components Templates、Dwoo、Calypsoを取り上げています。

Twigとの出会い

後半ではTwigにたどり着いた経緯が書かれています。

  • Djangoのテンプレートっぽいものを探したところTwigを見つけた
  • TwigはJinja作者であるArmin Ronacherによって書かれたもの
    • 注: JinjaはPython製のテンプレートエンジンでDjangoのテンプレートと似ているけど別もの
  • 2008年にとあるサービス向けにTwigが作られたがArminはPythonで仕事してる
  • 彼にTwigを発展させてもいいか聞いたら乗り気になってくれた

その後

Symfony 2がリリースされたのは約2年後の2011年7月です。この時にデフォルトのテンプレートエンジンとしてTwigが採用されています。

Twigの最初のgitコミットを確認すると2009年10月7日となっています。そして先に紹介したFabienの記事の日付は同じく2009年10月7日です。この日のコミットは開発ではなくgit-svnを使ったSVNリポジトリ(おそらくArminが使っていたものと思われます)からのポーティングですが、これからやっていくぞという意気込みを感じるコミットです。

現在のTwigはSensioLabs(FabienがCEOの会社)やSymfony開発者によってメンテされ、9年間経った今でもSymfonyを支える重要なパーツとして生き続けています。

Symfony 4をDeployerでデプロイしてSlackに通知する

Symfony Advent Calendar 2018 9日目の記事です。

概要

PHP製デプロイツールのDeployerを使ってSymfonyをデプロイする方法について書きます。おまけでSlackにデプロイ結果を通知する方法についても書きます。

環境など

Amazon Linuxで作業しました。Deployerのバージョンは 6.3.0 です。gitにあるSymfonyプロジェクトをデプロイする想定なのでGitHubに次のような準備を行いました。

$ composer create-project symfony/website-skeleton symfony4-deployer
$ cd symfony4-deployer
$ git add .
$ git commit -m "first commit"
$ git remote add origin git@github.com:cloned/symfony4-deployer.git
$ git push -u origin master

Deployerのインストール

Deployerは dep というコマンドを使って操作します。インストール方法は Deployer — Installation を参照してください。pharをダウンロードして配置する方法でもcomposerでインストールする方法でも dep というコマンドが利用できればOKです。

設定ファイル deploy.php を配置

dep コマンドが使えるようになったら、今度はデプロイするための設定ファイル deploy.php が必要です。このファイルは次のコマンドで雛形を作成することができます。

$ dep init

LaravelやSymfonyなど色々な選択肢が用意されており便利そうなのですが、この選択肢の SymfonySymfony 2 を想定しているのと、この手の設定はまず動く最小限を用意してから必要に応じて増やす方が理解しやすいので、ここでは最小設定の deploy.php を用意しました。

<?php
namespace Deployer;

require 'recipe/symfony4.php';

set('application', 'symfony4-deployer');
set('repository', 'git@github.com:cloned/symfony4-deployer.git');
set('env', [
    'APP_ENV' => 'prod',
]);

host('localhost')
    ->set('deploy_path', '~/deploy-path/{{application}}');

要点としては次のようになります。

  • 既に定義済みのレシピ symfony4.phprequire する
  • application(アプリ名)とrepository(gitリポジトリ)を設定する
  • Symfonyのenvがprodになるように APP_ENV を設定する
  • デプロイ対象のホストとパスを設定する

それぞれ少し解説します。

レシピに symfony4.php を利用する

Deployerにはレシピ(recipe)というものが用意されています。と書くとそのような仕組みがあるかのようですが実際のところ「良くあるタスクをあらかじめ定義しておいたPHP」というだけで、利用するにあたっては require するだけです。

ここで利用している recipe/symfony4.php はDeployer公式のものなのでcomposerなどで追加する必要はなくそのまま参照して利用できます。内容は直接コードを見た方がわかりやすいです。

deployer/symfony4.php at master · deployphp/deployer

設定を変更していく際に symfony4.php の定義をみて既にある定義を使うのか、上書きするのか、追加するのか、など考えますので元のレシピ(設定)を把握しておくことは重要です。といっても symfony4.php に関していえば数十行しかなく簡単に把握できると思います。

envをprodにする

<?php
set('env', [
    'APP_ENV' => 'prod',
]);

このように設定するとDeployerはコマンドを実行する際に先頭に export APP_ENV='prod'; と付け加えます。Symfonyのenvをprodにするためこのような設定を入れてあります。

この設定をせずにデプロイすると次のようなエラーになってしまいます。

PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "WebProfilerBundle" from namespace "Symfony\Bundle\WebProfilerBundle".

これは composer install--no-dev で実行している(つまりrequire-devのものはインストールされない)にもかかわらず、SymfonyのenvはdevのためWebProfilerBundleクラスを読み込めずエラーになっています。

Symfonyのenvをprodにするには APP_ENV=prod と記述した .env ファイルを配置する方法もありますが、ここでは実行時の環境変数で設定を行いました。 APP_ENVについてはSymfonyの公式ドキュメントに記載がありますのでそちらも参照してください。

How to Deploy a Symfony Application C) Install/Update your Vendors

デプロイを実行する

これで準備が整ったのでデプロイを実行します。

$ dep deploy

うまくいけば次のように実行されたタスクが表示されます。

f:id:cloned:20181201212303p:plain:w290

デプロイ先は次のように設定していますので localhostSSH接続して deploy_path のパスに対してデプロイが行われます。

<?php
host('localhost')
    ->set('deploy_path', '~/deploy-path/{{application}}');

この例でのデプロイ後のアプリケーションパスは ~/deploy-path/symfony4-deployer/current になります。currentシンボリックリンクで実体は~/deploy-path/symfony4-deployer/releases/以下に存在します。releases/に配備したコードへのシンボリックリンクを張り替えるという方法になっています。この辺りはDeployerのコピー元であるRuby製デプロイツールのCapistranoと同じです。

デプロイを戻す

$ dep rollback

とすると直前のリリースにシンボリックリンクを張り替えます。これによりデプロイ前のコード(前回デプロイしたコード)にロールバックすることができます。ls -l current 等でシンボリックリンク先が変わっていることを確認すると理解しやすいと思います。

デプロイ設定を変更する

これで基本的なデプロイは完了です。ただ用途によって設定を変更したいことがあるかもしれません。例えば var/logvar/sessions はもともとデプロイごとに別ファイルになってしまわないようにcurrentと同じ場所にあるsharedディレクトリへのシンボリックリンクになるようになっていますが、var/files も同様にしたいとしましょう(var/filesは単にここでの例でSymfonyにはもともとありません)。

その場合は deploy.php に次の記述を追加します。

<?php
set('shared_dirs', ['var/log', 'var/sessions', 'var/files']);

元のレシピの symfony4.php に既に shared_dirs が定義されていますが var/files を追加して上書きしています。このように元のレシピを確認しながら必要に応じて設定を変更することができます。

他にも例えば symfony4.php には database:migrate タスクが定義されているので、デプロイ完了前にマイグレーションを実行するような設定を行うこともできます。

Slackに通知する

おまけですがデプロイが完了したらSlackに通知する設定も追加します。Slackに通知する実装はDeployer本体には含まれていませんが、別のリポジトリで色々なレシピが公開されておりそこにSlackのレシピが存在します。公式サイトにドキュメントも存在します。

Deployer — Slack recipe

まずはcomposerで各種レシピを追加します。

$ composer require deployer/recipes --dev

次に deploy.php にSlackに関する設定を追加します。成功時に通知したいだけであれば次の3行を追加するだけです。

<?php
require 'vendor/deployer/recipes/recipe/slack.php';
set('slack_webhook', 'https://hooks.slack.com/xxxxxxxx');
after('success', 'slack:notify:success');

slack_webhook のURLはSlack側で作成しておく必要があります。うまくいくと次のような通知が来るようになります。

f:id:cloned:20181201222525p:plain:w325

まとめ

Deployerを使ってSymfony 4をデプロイする方法を解説しました。PHPフレームワークのデプロイ設定があらかじめ用意されているのですぐに使い始められます。Symfony 4に限らず公式レシピをうまく使ってデプロイする際の参考になればと思います。

ドラゴンクエストXを支える技術を読んだ ~ 中の人しか知り得ない貴重な記録

発売されることを知って予約注文していた本。読み終えたので感想文。先に一言で結論を書くと素晴らしい本だった。

この本の良いところ

この本が素晴らしいのはゲーム開発の実録になっていること。一般的な技術解説本の場合、著者が経験してきたことの集大成になることが多い。つまり色々なプロジェクトを経験した上でこれが最も良かったと思う内容がピックアップされる。効率よく学習したい読者にとっても普通はそのほうが良い。

しかし、この本では「ドラゴンクエストXで何をしたのか」しか書いていない。もちろんお手本になるものが中心ではあるけれど、失敗したこと、そしてそれがどのような大規模障害に繋がったのかが解説されている。

これはすごいことで、著者は自分(達)の失敗を明らかにするような本を書きたくないし、失敗する方法よりも成功する方法を伝えたいものだから。でも、この本には失敗談がたくさん盛り込まれている。失敗談をたくさん記述してくださった著者に敬意を表して失敗談の有用性について少しだけ書きたい。

失敗経験がないと良い方法を蔑ろにする

本(に限らないけれど)に書いてあった良い方法を学んで実践する。通常はこれで良い。けれども、悪いものを知らないとそれがどれだけ良いのかわからない。良さの程度がわからないと良い方法を蔑ろにする。

「こうした方が良いのだろうけど今回はその場凌ぎの対応をしておこう」というのは現場で頻繁にある。時間が限られているので仕方がないところもあるけれど、悪いものを知らないと「その場凌ぎであっても選択してはならない重大な欠陥がある方法」をあっさりやってしまったりする。

悪い方法について知らないからというのもあるけれど、何より失敗経験がないとその悪さを重大なものとして認識しにくい。

実際に失敗した人は違う。この悪い方法を選択したばかりに開発/運営チームは数日対応に追われお客様から大量の叱咤を受けたという経験から、時間がなくてもその方法を選択してはいけない、時間がないならリリースを延期してでも違う方法にしないとダメ、と言えるようになる。

こういった判断力を培うには普通は自分で失敗するしかないのだけど、この本はなんとその失敗経験をたったの2680円+税で提供しているのだから値千金ということ。しかも実際にドラゴンクエストXにどのような影響を与えたのかが常に記載されており社外の人間が読んでも臨場感のある内容になっている。

広範囲な内容

目次を見るとものすごい広範囲な内容になっていて、これだけ読めばオンラインゲームの全貌が掴めそうな感じ。とはいえ実際のところは次の点が中心だったように感じる。

  • ゲームサーバーのプロセス設計やDBの使い方
  • 制約に対するアプローチ(メモリが足りない、アクセスが多くて高負荷など)

ページ数だけでみると他の章も結構紙面を割いているのだけど、開発体制やプログラミング言語に関するところ、グラフィックに関するところは基本的な内容が多いように感じた。

あまり公開されないチート対策についても記述されているので必見。

読み手を選ばない配慮の行き届いた文章

この本はドラゴンクエストXについてしか書いていないので当然ゲームとしてのドラゴンクエストXを知らないと理解しにくい部分があるのだけど心配無用でゲーム仕様どころか次のような解説からスタートしている。

RPGは、役割(Role)を演じる(Playing)ゲーム(Game)のことです。

p3より

また専門用語は使われているものの専門用語の使い始めに説明を記していることが多い。「フラグ」のような開発者でなくても知っていそうな言葉でも解説が入っている。

期待外れにならないように

この本では大体のことが方法論までしか触れておらず実装説明はあまりないので各部門の専門の人が読んだ際には物足りない感じはあるかもしれない。企業秘密的な側面もあるだろうし幅広い読者層を意識してのことだと思う。

プログラミング視点だとウルティマ開発者のこの本が具体的でかつ実践的だった。

ゲームコーディング・コンプリート 一流になるためのゲームプログラミング (Professional game programming)

ゲームコーディング・コンプリート 一流になるためのゲームプログラミング (Professional game programming)

オンラインゲーム設計視点だとやはりこの本を外せない(「ドラゴンクエストXを支える技術」の中でも言及がある)。

オンラインゲームを支える技術  ??壮大なプレイ空間の舞台裏 (WEB+DB PRESS plus)

オンラインゲームを支える技術  ??壮大なプレイ空間の舞台裏 (WEB+DB PRESS plus)

とはいっても各専門の人が読んでも期待外れに思うことはないと思う。なぜならこの著者ほどオンラインゲームの全体を把握し責任を持つ立場になっている人は日本にそれほどいないだろうから。

読み終えて思ったこと

買いましょう。読みましょう。良い本です。好きな呪文はベホマラーです。嫌いな呪文はマホトラです。

はてなブログに移行したついでに振り返りや近況など

はてなダイアリーからはてなブログに移行した。はてなダイアリーの最初の投稿は2005年11月22日のようなので昨日で13周年だったみたい。

Javaについて書くところから始めて、インターネット全般、読んだ本、写真のこと、いろいろ書いた。今では年に数回しか書いてない。

ブログを書かなくなったのはTwitterFacebookでパッと書きたいことを書けるようになったからというのもあるし、以前よりもインターネットに情報が充実していて自分が書くまでもないと感じて書かないことも多かった。

最近のこと

今でもプログラマを続けているし作るものもインターネット関係だから仕事は昔と変わっていない。内容が同じということはないけれど今までの延長線。

でも、生活は大きく変わった。子供が二人いるので自分だけのために使う時間はほとんどなくなった。こうしてブログ書いている時間なんてほとんどなくて、実際、今現在も下の子を朝寝させている隙に書いている。

ただ、この時間がない、というのは自分のための時間がない設定で良いという自分の判断なので、子供のせいで人生が辛いという意味ではない。優先順位の問題。

インターネットに思うこと

最近は穏やかではないこともよく目にするけれど、自分が思ったことを検閲されずに広く公開できる場所は今でもやはりインターネットしかないと思う。検閲は国によるし日本でも最近話題に新しいけれど、なんの背景も持たない小さな個人が平等に扱われる場所としてインターネットはあり続けて欲しいと思う。

子供に思うこと

子供ができて感じるのはとにかく子供の能力を甘く見ていたなということ。5歳というとまだ幼児、つまり言葉は話せるけどまだまだ幼いというイメージだったけれど、本当に謝罪の上で撤回。論理的な思考ができるし社会性もある。身体能力も跳び箱9段跳べるとかびっくり。

今日のこと

下の子を朝寝から起こしてしばらくしたら離乳食をあげる。その頃には奥さんが起きてくるので(奥さんは夜中対応してくれているので午前中長めに寝る方針)、そしたら上の娘を連れてスケートに行ってくる。またスケートができるシーズンになって喜ばしい。

冬は大好きなので寒さも乾燥も大目に見るけれど静電気だけは消滅して欲しい。痛いの嫌い。ぺんてるの静電気除去グッズが手放せない毎日。

目的のために「手段」認定すると楽しくない

手段であって目的ではない、とよく言われる。現代の格言と言っても良い。英語を使って何をするかが重要なのだから英語を覚えることは手段でしかない。いかにももっともらしい。

ここでいう手段は目的を達成するための方法という意味だから、目的を何にするかによって何が手段になるのか異なる。目的によって変動する定義なのだから一般論として「○○は手段だ」と断じるとおかしい。宇宙全体の幸せが目的の人にとってはそれ以外の行為全てが手段でしょう。

一人の人間に対して目的が1つ、という決まりもない。同じ人間でも目的はいくつもある。ゲーム開発者がゲームプレイすると遊んで楽しい(プレイヤーとしての目的そのもの)と知見が増えた(開発者としての手段)と両方共存する。

手段であって目的ではない論法を目にした時、近視眼的で今まで見えていなかった目的が見えてよかったと思うなら良いけれど、自分のやっていることは手段でしかないのかと卑下するなら良くない。結局のところ、何をしたいの?どうしたいの?というのを自覚しましょうということでしかない。

これは経験的にだけど「手段」認定するとその行為が楽しくなくなるので基本的には目的であり手段でもある方が良い。よくプログラミングも手段であって目的ではないと言われる。確かにアプリケーションを作るためのプログラミングは手段だけど、より美しく保守性の高いプログラミングを考えてるときはそれ自体が達成したい目的だったりする。そう感じて頭を使う方が楽しいし創造的になる。もちろんそれを客観的に俯瞰してみれば「手段」認定できるけれど、そんなに手段手段言わなくても良いのではと思う。

noteでプログラミング超超超入門を書いてみる

プログラムが魔法でないことを知ればあなたもプログラマになれる

noteデビューしてみた。プログラミングの入門書って入門の割に初心者ブレイカーなことが多いとずっと思っていて、今日ふと思って書き始めてみた。プログラミング超超超入門として時間を見つけて書いてみたい。

家事分担における不平等感の解決

家事を分担すると、不平等に感じることがよくある。この不平等感の原因に「隣の芝生は青い」問題と「隣の芝生は本当に青い」問題を挙げたい。

「隣の芝生は青い」問題

Aはお風呂掃除を担当し、Bはトイレ掃除を担当するような場合。Aはトイレの方が掃除する面積が狭いから自分より楽だろうと思い、Bはお風呂は汚くないから良いなと思う。

このように自分が担当していないものの方が楽だと思ってしまいがち。とはいえ、気をつけて直せるようなものでもない(そう思ってしまうのは仕方がない)。

「隣の芝生は本当に青い」問題

Aは育児を担当し、Bはお風呂掃除とトイレ掃除と他にもたくさんの掃除を担当するような場合。仮にBが時間的に育児に匹敵するほど掃除をしていたとしても、実際のところ、精神的な負荷は圧倒的にAの方が大きい。そのため作業時間が同じでも不平等。Bは(時間的に)自分もたくさんやっていると感じているので、すれ違いになりやすい。仕事担当と育児担当で相殺しようとするとこれになりやすい。

※育児の方が圧倒的に大変かどうかは個人差があるので決め付けられないけれど、わかりやすそうな例として。

解決方法

私はXをするので、あなたはYをする、といった異なる作業で分担するのをやめる。私はXを1日10回する、あなたは仕事があるので回数は少なくて良いけれど同じXを1日2回する、のように時間や回数の比率で分担をすると良い。

時間/回数が異なっても作業内容が同じなので、隣の芝生にならず自分の芝生になる。自分の方が大変だ、という見解になりにくい。何より相手の大変さが理解できるようになるので誤解が少なくなり、必然と不平等感も少なくなる。