自分の部下には「パーフェクトPHP」を読んでおいてもらいたい理由5つ
自分の部下には、「これ読んどけ!」と必ず渡しておきたい1冊です。
- PHPの開発現場で実際に必要な知識を幅広くカバー
- さすがにこれで「全部」というわけにはいきませんが、書かれている内容はどれも「必要」
- MVCフレームワークの用語や知識
- オブジェクト指向でPHPを書く場合の必須知識を網羅
- 「5章 クラスとオブジェクト」が50ページ以上、「11章 実践オブジェクト指向」が40ページ
- PHPでWebアプリケーションを作る際に必須のセキュリティの知識も網羅
- must readとしか言いようがない
- PHPの内部構造の知識も適度にカバー
- 掘り下げ具合のさじ加減がちょうどいい
- 作者: 小川雄大,柄沢聡太郎,橋口誠
- 出版社/メーカー: 技術評論社
- 発売日: 2010/11/12
- メディア: 大型本
- 購入: 32人 クリック: 1,065回
- この商品を含むブログ (59件) を見る
そんな私には、今は部下は1人もいませんけどね!
それに、なにより私自身のPHP知識の「弱点」を、パーフェクトPHPを読むことによって浮き彫りにされた気がします。
あと、この本の「はじめに」のページで、「謝辞」に私の名前が並んでいてビックリです。id:Fivestar が書いているフレームワークの章で、(Symfonyつながりということで)ちょっとだけ査読的なことをしただけなんです。
(将来私の部下ができたら)この本を必ず読ませます!
KernelとHttpKernel、その辺の起動順序
Symfony2で名前的に混乱しやすそうなのがKernelとHttpKernelで、
- Kernelはアプリケーション全体のベースとなる
- HttpKernelは1つのHTTPリクエストのベースとなる
という感じで、Kernelの方がベースよりです。これは説明するまでもないかもしれませんが、フロントコントローラーで一番最初に作られるAppKernelがKernelの方を継承しています。
で、ポイントはHttpKernelの初期化のあたりですが、これはDIコンテナ経由で行われます。
(※DIコンテナ自身はKernelが作ります)
Kernel::handle()内部で、
<?php return $this->container->getHttpKernelService()->handle($request, $type, $raw);
とコンテナ経由でHttpKernelサービスにアクセスしているので、この時点でHttpKernelが初期化されます。
DIコンテナの面白い(すごい)のはここからなんですが、このHttpKernelを初期化する際に、同時にいくつかの他のサービスも初期化されます。なぜかというと、DIの定義で、HttpKernelのコンストラクタにいくつかのパラメーターが渡されていて、このパラメーターはDIが渡すのですが、そのパラメーターがまだ初期化されていなければ、それもまたその時点でインスタンス化されるからです。
具体的には、HttpKernelのコンストラクタには次の3つの引数があります。
- ContainerInterface $container
- EventDispatcher $dispatcher
- ControllerResolverInterface $resolver
対応するDIの定義は、FrameworkBundleのservices.xmlにあります。
<service id="http_kernel" class="%http_kernel.class%"> <argument type="service" id="service_container" /> <argument type="service" id="event_dispatcher" /> <argument type="service" id="controller_resolver" /> </service>
なので、HttpKernelがインスタンス化されると同時に、DIコンテナによってEventDispatcherがインスタンス化されます。
symfony 1的に「new EventDispatcher」でgrepしたりしても見つかりませんw
このあたりに慣れるまでは、ちょっとソースを追うのが大変かもしれません。
Symfony2のconfigファイルにおけるimportsの挙動(DIのconfig)
Symfony2のconfigファイルでは、別のconfigファイルをインポートすることができます。
たとえばconfig_dev.ymlでは、共通設定であるconfig.ymlを以下のようにインポートしています。
imports: - { resource: config.yml }
また、config_dev.ymlをよく見ると、ルーティングの設定についても似たような記述があることが分かります。
app.config: router: { resource: "%kernel.root_dir%/config/routing_dev.yml" }
前者のimportsで記述したresourceは、このconfigファイルの処理時点で同時にインポートされ、マージされて1つのconfigとして扱われます。
このimportsキーは、YAMLのルート階層にのみ記述できます。
そして、YAMLファイルがパースされるとき、まず最初にimportsの処理が行われ、インポートしているファイルから先に読み込まれて行きます。
この処理は、DependencyInjectionのYamlFileLoaderにおける共通処理となっています。
後者のrouterキーでのresourceは、FrameworkExtensionでの処理になっています。FrameworkExtensionのconfigLoadで、単にresourceのパスなどがコンテナの設定値として保存されます。(コンテナ・エクステンションの読み込み段階ではこれ以上の処理は行われない)
実際にrouterキーのresource(つまりrouting.yml)が読み込まれるのは、内部の処理が先に進み、RouterからRouteCollectionのloadが呼び出される時点です。ここでは、Routingコンポーネントが持つローダー(YamlFileLoader)で処理が行われるので、DIコンテナのconfigとは若干ルールが異なることに注意が必要です。
Symfony2のデバッグモードのエラー画面で、ファイルのパスを物理ファイルにリンクする
app/config/config_dev.ymlに次のように記述すると、リンクになります。
app.config: ide: macvim
「macvim」だと「mvim://open〜」というURLでリンクされ、「textmate」だと「txmt://open〜」というURLになります。他の値を指定した場合、その値がファイルリンク用のパターンになります。
<?php if (isset($config['ide'])) { switch ($config['ide']) { case 'textmate': $pattern = 'txmt://open?url=file://%%f&line=%%l'; break; case 'macvim': $pattern = 'mvim://open?url=file://%%f&line=%%l'; break; default: // should be the link pattern then $pattern = $config['ide']; } }
config_dev.ymlではなくて、config.ymlにideの設定を記述するとうまく動かないのは、いまいち理由が分かっていませんが・・・。
PearのServices_TwitterでOAuth認証している場合のfriendships/show
OAuth認証してAPIリクエストを送信している場合は、friendships/showでsource_idまたはsource_screen_nameのいずれも指定しなくてもいけるはずなんだけど、PearのServices_Twitterだとパラメーターの数が足りないと言われエラーになります。
このエラー、twitter側で出しているんじゃなくて、Services_Twitter側で出してるんですね。
で、こういった引数を定義しているのがdata/api.xmlファイルで、そこに引数の最小値が「2」と書かれていたのが原因でした。
303 <endpoint name="show" method="GET" auth_required="false" min_args="2"> 304 <formats>xml,json</formats> 305 <param name="source_id" type="id_or_screenname" required="false"/> 306 <param name="source_screen_name" type="id_or_screenname" required="false"/> 307 <param name="target_id" type="id_or_screenname" required="false"/> 308 <param name="target_screen_name" type="id_or_screenname" required="false"/> 309 </endpoint>
OAuth認証している場合とそうでない場合とで必須かどうかが変わるので、これをバグと言うべきかどうかは判断できませんが・・・。
とりあえず手元で直接1に変更して対処。
Doctrine2を使った開発のワークフローで悩み中
symfony 1では、ORM Designerを使ってスキーマ設計→schema.yml書き出し→モデルやDBを生成(必要に応じてマイグレーション)というワークフローでした。
こういったワークフローをDoctrine2を使った開発でもやりたいと思い、Doctrine2のコマンド等とあれこれ格闘していますが、今のところまだ「これだ」と思える方法が見つかっていません。
現状、ぶつかった問題点
- ORM DesignerはDoctrine2をサポートしているが完全ではなく、リレーションの情報など、一部書き出されたYAMLが不完全
- YAMLでマッピング設定を書き、doctrine:generate:entitiesでエンティティを生成すると、getter/setterなども一挙に生成できて楽、しかし問題も
- エンティティにアノテーションでマッピングを記述するのが現状もっともスムーズに思える
- マッピング情報はYAML、エンティティにはマッピング情報を記述しないというやり方
- 2重管理的になるので避けたい(エンティティ側にもメンバー変数などの宣言が必要なため)
- リレーション(Doctrine2ではアソシエーションと呼んでいる)関連
現時点での結論としては、
というやり方がもっとも無難っぽいです。(あくまで個人的な結論)