Hatena::ブログ(Diary)

モバイル系ニートエンジニア`chobi_e`の日記 Twitter

2010-11-05

Symfony2を理解する為にシンプルなアプリケーションを書く

ニートになってはや一週間。仕事をしてなくても色々勉強等を励んでいると一日って早いものだなーと実感しているchobi_eです。


Symfony2とにらめっこしてきてだいぶ理解できてきたと思うので、Symfony2を理解する為にHello chobi_eを出力するシンプルなアプリケーションを作成しましょう。

apache2の設定自体は前回のエントリのままで進めるので初めての方はかるーくapache2の設定だけ目を通しておいてくださいね。


因みに今日のエントリーはlinuxならshellにガンガンコード部分をコピペしていけば終わりますよ!shellのエスケープの問題でコードの$の前にバックスラッシュがはいっていますがエディタでちまちまやる場合はお手数ですが消してくださいね(11/6コピペで動かない部分があったので修正しました。)


環境を構築する
cd ~/servers
# 前の環境はとりあえず横においとく
mv symfony2.chobie.air symfony2.bak

# githubから開発版をひろってくる
git clone https://github.com/fabpot/symfony.git symfony2.chobie.air
mkdir -p symfony2.chobie.air/{web,logs,app/config}
cat > symfony2.chobie.air/web/index.php <<EOF
<?php
echo "Hello chobi_e";
EOF

では、ブラウザでhttp://symfony2.chobie.air/(ローカルの開発環境)を開いてみましょう。


f:id:chobi_e:20101106031210p:image


Hello chobi_eという事で今日は最終的にこの出力をだす為にSymfony2でゴニョゴニョと書いていきます。


autoloaderを書く

\Symfony\Component\HttpFoundation\UniversalClassLoader.phpを使ってnamespaceやクラス名を元にファイルの読み込みが行えるようにしましょう。今回は理解するのが目的なのでvendorディレクトリは作らなかったのですが他のBundleやクラスを使う場合はvendorディレクトリにまとめるのがSymfony流のようです。


cd symfony2.chobie.air
cat > src/autoloader.php <<EOF
<?php
require_once __DIR__ . "/Symfony/Component/HttpFoundation/UniversalClassLoader.php";

use Symfony\Component\HttpFoundation\UniversalClassLoader;

\$loader = new UniversalClassLoader();
\$loader->registerNamespaces(array(
        'Symfony'       =>      __DIR__,
        'Application'   =>      __DIR__,
        'Bundle'        =>      __DIR__
));
\$loader->register(); 
EOF

アプリケーション用のKernelを書く

Symfony2ではマイクロカーネルという構築の仕方をしており、WebApplicationを作成する場合は\Syfony\Component\HttpKernel\Kernelを継承したクラスを作成する必要があるみたいですねー。


cat > app/SampleKernel.php <<EOF
<?php
require __DIR__ . "/../src/autoloader.php";

class SampleKernel extends Symfony\Component\HttpKernel\Kernel
{
  public function registerRootDir()
  {
    return __DIR__;
  }
  
  public function registerBundles()
  {
    \$bundles = array(
      new Application\SampleBundle\SampleBundle(),
      new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
    );
    return \$bundles;
  }
  
  public function registerBundleDirs()
  {
    return array(
      'Application'   => __DIR__ ."/../src/Application",
      'Symfony\\Bundle'=>__DIR__ ."/../src/Symfony/Bundle"
      );
  }
  
  public function registerContainerConfiguration(Symfony\Component\DependencyInjection\Loader\LoaderInterface \$loader)
  {
    \$loader->load(__DIR__ ."/config/config.php");
  }
}
EOF

Kernelでは次の4つのメソッドを定義しなければなりません

  • registerRootDir
  • registerBundles
  • registerBundleDirs
  • registerContainerConfiguration

registerRootDirはKernelがあるディレクトリ。registerBundlesは利用するBundleを登録。registerBundleDirsはautoloaderの名前解決とは別にBundleの場所を書いて解決できるようにします。registerContainerConfigurationはDIコンテナの設定ファイルをどうやってロードするかを書いていきます。


Bundle登録関係は見ての通りなんで特に説明する必要はありませんが、DIコンテナの設定ファイルの登録については次で見ていきましょう。


DIコンテナの設定を作る


今回はSymfony2を理解する為のエントリなのでまずは基本のPHPの設定ファイルから。


そうそう、以前設定ファイルはPHP,YAML,XMLどれがいいかという話題があがっていましたが個人的にはXMLかYAMLでいいんじゃないかなーと思います。XMLであればSchemaValidationが扱えるのでミスコンフィグが減らせますし、YAMLであればちょっとした開発で見通しが良いので便利かと思います


閑話休題


cat > app/config/config.php <<EOF
<?php
\$app_config = array();
\$app_config["charset"] = "UTF-8";
\$app_config["router"] = array(); 
\$app_config["router"]["resource"] = '%kernel.root_dir%/config/routing.php'; 
\$app_config["templating"] = array();
\$app_config["templatting"]["escaping"] = "htmlspecialchars";

\$container->loadFromExtension('app', 'config', \$app_config);

return \$container;
EOF

$containerは\Symfony\Component\DependencyInjection\Loader\PhpFileLoaderから渡されており、様々な設定を書いて最終的にreturn $container;することでアプリケーションの設定をDIコンテナで管理することができます。


で、注意して欲しいのは$container->loadFromExtension。


重要なので\Symfony\Component\DependecyInjection\ContainerBuilder.phpから該当部分を引っ張ってきましょう。


<?php
/**
 * Loads the configuration for an extension.
 *
 * @param string $extension The extension alias or namespace
 * @param string $tag       The extension tag to load (without the namespace - namespace.tag)
 * @param array  $values    An array of values that customizes the extension
 *
 * @return ContainerBuilder The current instance
 */
public function loadFromExtension($extension, $tag, array $values = array())

という事なので先程書いた設定は、appというaliasが設定されたExtension(この場合FrameWorkBundle\DependencyInjection\FrameworkExtension)にconfigという名前の設定を配列で渡しますよ。という意味ですね。


$tagの名称は特に重要で、Extensionクラス内に{$tag}Loadというメソッドを定義しておく必要があり、コンフィグのロード時に該当Loadメソッドで処理されDIコンテナに登録されます。これは設定ファイルがPHPでもYAMLでもXMLでも同様なのでよく理解しておきましょう。


まぁ、実際にDIコンテナ周りをいじるのはBundleをきちんと作成する段階になってからだと思うので、今日はとりあえず次に進みましょうか。


Routingを作成する
cat > app/config/routing.php <<EOF
<?php

use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

\$collection = new RouteCollection();
\$collection->addRoute('homepage',new Route('/',array(
  '_controller' => 'SampleBundle:Hello:index'
  )));

return \$collection;
EOF

_controlloer => 'SampleBundle:Hello:index'でSampleBundleのHelloControllerのindexActionを実行する、と指定してますね。なんとなーくわかりますよね!ということで割愛。


\Application\SampleBundle\Controller\HelloControllerの作成

BundleとControllerとViewも作成します。


mkdir -p src/Application/SampleBundle/{Controller,Resources/views/Hello}
cat > src/Application/SampleBundle/SampleBundle.php <<EOF
<?php
namespace Application\SampleBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class SampleBundle extends Bundle
{
}
EOF

cat > src/Application/SampleBundle/Controller/HelloController.php <<EOF
<?php
namespace Application\SampleBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class HelloController extends Controller
{
        public function indexAction()
        {
                return \$this->render('SampleBundle:Hello:index.php');
        }
}
EOF

Viewも同様に作成します


cat > src/Application/SampleBundle/Resources/views/Hello/index.php <<EOF
<?php
echo "Hello chobi_e";
EOF

最後にindex.phpもKernelを実行するように上書きしましょう


cat > web/index.php <<EOF
<?php
require_once __DIR__ . "/../app/SampleKernel.php";

\$kernel = new SampleKernel('dev', true);
\$kernel->handle()->send();
EOF

これで準備は整ったので、再度http://symfony2.chobie.air/にアクセスしてみましょう。


f:id:chobi_e:20101106031210p:image



きちんと意図したとおりに表示されていますね!


あとがき

いやはや、たった数バイトの文字を出力するまでにこれだけ労力がかかるとは大変です。しかしSymfony2はとことん最適化されたキャッシュやDIコンテナを利用することで実際の実行速度は想像以上に速いですし、今後リリースに向けてその他便利ツールも充実するみたいなので面倒くささは少なくなるでしょう。


もう少し深くBundleの作成方法を学ぶと一度書いたBundleは楽に他のアプリケーションに持っていけるようになるのでアプリケーションの再利用性が高いのもポイントですね。


次回はPDOBundleを作成してBundleの理解を深めつつ実際に使う所までをやってみましょうか。


DB周りのBundleとしては既にDoctrineBundleがあるので車輪の再発明となってしまいますがBundleを理解するには一番わかり易いサンプルかと思います。


そいでは、マタネー!