Home > CakePHP
CakePHP Archive
PHP5.4 ビルトインサーバで CakePHP を試食する(CakePHP Advent Calendar 2011 4日目)
PHP5.4のビルトインサーバで CakePHP を動かしてみました。
CakePHP Advent Calendar 2011 の 4 日目です。
いよいよ PHP5.4 の正式リリースが近づいてきました。5.3 からのマイナーバージョンアップとはいえ、Trait や Short syntax for arrays、Array Dereferencing など興味深い機能が追加されています。
そんな新機能の中から注目のビルトインサーバを使って、CakePHP を動かしてみました。今回は CakePHP1系と2系の両方を試しています。
PHP5.4RC2をインストール
まずは現時点での最新版 PHP5.4RC2 が動く環境を作ります。
手順は以前書いたエントリ「 現状のPHP環境はそのままで、PHP 5.4 を試す 」を参考にして下さい。
上記エントリではとりあえず 5.4 環境を作るという内容でしたが、実際にアプリケーションを動作させる場合は、./configure で利用するオプションを指定する必要があります。今回は下記のようにオプションを指定しました。
$ ./configure --with-mysql --with-pdo-mysql --enable-mbstring --with-pgsql=/opt/local/lib/postgresql90
ビルトインサーバを起動
ビルトインサーバは PHP5.4 で実装された新機能です。CLI 版 PHP のオプションとして実装されており、コマンドラインから Web サーバを起動して、その上で PHP を実行することができます。これを使えば Apache のような Web サーバが無くても CakePHP を実行することができます。
では先ほど構築した PHP5.4RC2 環境でビルトインサーバを起動してみましょう。
CLI 版 PHP に「-S」を付けるとビルトインサーバが起動します。「-S」の後ろは HTTP リクエストを待ち受ける IP とポートを指定します。下記例では local:8111 で待ち受けます。なお、ここでは既存の PHP の ini ファイルを利用したいので、「-c」で php.ini のパスを指定しています。
$ sapi/cli/php -S localhost:8111 -c /path/to/php.ini PHP 5.4.0RC2 Development Server started at Sun Dec 4 15:38:15 2011 Listening on localhost:8111 Document root is /path/to/docroot Press Ctrl-C to quit.
起動したらブラウザで「http://localhost:8111/」にアクセスするとページが表示されます。
Ctrl-C を押すと、ビルトインサーバが停止します。
-t で DocumentRoot を指定
デフォルトではコマンドを実行したカレントディレクトリが DocumentRoot になります。「-t」を付けると任意のディレクトリを DocumentRoot に変更できます。
$ sapi/cli/php -S localhost:8111 -t /path/to/docroot
.htaccess は無効
.htaccess はビルトインサーバ上ではただの1ファイルにしかすぎず、mod_rewrite などの制御は働きません。
しかし、ビルトインサーバではリクエストファイル名に「.」が含まれておらず、該当ファイルが存在しない場合は、index.php もしくは index.html を起動する仕様になっているようなので、CakePHP のように index.php からフレームワークが起動するアプリケーションでは問題無く実行することができます。
詳細はPHPマニュアルに
さすが PHP ということで、すでにビルトインサーバについては PHP にマニュアル解説があります。詳細はこちらをどうぞ。
PHP: ビルトインウェブサーバー – Manual
CakePHP2 を試す
では実際に CakePHP2 をビルトインサーバ上で動かしてみましょう。今回動かしたのは 2.0.4 です。
手順は簡単で、CakePHP2 の app/webroot を DocumentRoot に指定して、ビルトインサーバを起動するだけです。
$ sapi/cli/php -S localhost:8111 -c /path/to/php.ini -t /path/to/cakephp2/app/webroot
ブラウザでアクセスすると見事におなじみの画面が表示されます。

本格的なアプリケーションは実行していませんが、ざっと見た感じでは、PHPソースを変更することなく、そのまま実行することができました。
CakePHP1 を試す
次は CakePHP1 をビルトインサーバ上で動かしてみます。今回動かしたのは 1.3.13 です。
$ sapi/cli/php -S localhost:8111 -c /path/to/php.ini -t /path/to/cakephp1/app/webroot
ブラウザでアクセスするといつもの画面が表示されますが、いくつかエラーが出ています。

エラーメッセージは以下。
Strict Standards: Redefining already defined constructor for class Object in /path/to/cakephp1/cake/libs/object.php on line 54 Strict Standards: Non-static method Configure::getInstance() should not be called statically in /path/to/cakephp1/cake/bootstrap.php on line 38 Strict Standards: Non-static method CakeLog::getInstance() should not be called statically, assuming $this from incompatible context in /path/to/cakephp1/cake/libs/cake_log.php on line 290
E_STRICTエラー
PHP5.4 からエラー出力レベル E_ALL に E_STRICT が含まれたため、E_STRICT に引っかかった箇所がエラーとして表示されてます。
CakePHP ではフレームワーク自身が error_reporting() でエラー出力レベルを上書きしてしまうので、E_STRICT を除外するように変更する必要があります。(display_error=off でエラー表示を抑制する手もありますが、開発環境では表示しておいた方が便利です。)
この問題についてはビルトインサーバ上でなくとも、PHP5.4環境では同じく発生します。
修正箇所は github に commit してあるので、こちらをどうぞ。
Commit 9cbc30101c7d43221b88ea0d9fa66c2cfaa4f1d1 to shin1x1/cakephp – GitHub
URLパス生成の問題
「http://localhost:8111/a」など存在しないコントローラにアクセスするとお馴染みのエラー画面となるのですが、CSS が効かずにテキストのみが表示されます。画面下の画像も表示されていません。

これはフレームワークのURL生成に問題が発生しているためで、HTMLのソースを見ると CSS のパスが「/index.php/css/cake.generic.css」のように頭に余計な内容が入っています。
<link rel="stylesheet" type="text/css" href="/index.php/css/cake.generic.css" />
この対応として、app/webroot/index.php に以下の記述を追加します。
+ if (php_sapi_name() == 'cli-server') {
+ $_SERVER['PHP_SELF'] = '/'.basename(__FILE__);
+ }
if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
これで「http://localhost:8111/a」でアクセスすると CSS が効いて、いつもの表示になります。

他にも FormHelper が生成する URL など各所で同じ現象が発生しますが、上記修正で対応ができます。
ユニットテストもちゃんと通りました
ちなみに CakePHP1 系で作ったとあるアプリケーションの alltest を通すとビルトインサーバ上でもちゃんとテストが通りました。これで 5.4 上でも動作することが分かりました:D

ビルトインサーバで手軽に試す
ビルトインサーバの利点はなんといっても「手軽さ」です。
新たに環境構築が必要な場面で、いちいち Apache に VirtualHost を追加したりせずともビルトインサーバを使えば、すぐに任意のディレクトを DocumentRoot にして Web サーバを起動することができます。
ビルトインサーバは CLI 版 PHP ではデフォルトで組み込まれるので、拡張等無しで多くの環境で利用できるのが良いですね。
ぜひ PHP5.4 ビルトインサーバでお手軽に CakePHP を試してみて下さい。
明日の CakePHP Advent Calendar 2011 5 日目は、@kashioka さんです。お楽しみにー。
- コメント (Close): 0
- Trackbacks: 2
PHP Matsuri is Awesome!
PHP Matauri 2011 が終了しました。いやあ楽しかった。
大きな問題もなく無事にイベントを終えることができました。関西の方はもちろん、大阪開催にも関わらず、多くの方に関西外からお越し頂き、みなさん本当にありがとうございました。
個人的にも昨年参加できなかった PHP Matsuri にようやく参加できました。色々と感じたことなどをつらつらと書いていきます。
PHP Matsuri はどんなイベント?
参加前
- 昨年の PHP Matsuri は参加できず。
- まあ運営はいつものメンバーなので、後で聞いてみよう。
- みんな「とにかく楽しかった」しか言わない。。。
- 「カンファレンス」「ワークショップ」「ハッカソン」「開発合宿」「アンカンファレンス」が合わさったイベント?
- それぞれ参加したことあるし、魅力的なラインナップ、確かに楽しそうなのは分かる。
1日目終了
- カンファレンス、ワークショップは納得の内容。
- TDDワークショップでちょろっと講師。参加された方、ありがとうございました!(FizzBuzzでPHPUnitを体験)
- 翌日のライトニングトーク大会に向けてハッカソン開始するが、細々したことばかりやって今ひとつ手につかず。
- まあ雰囲気楽しいし、翌日の発表は今回はもういいかなーと思いつつ、3:00頃に就寝。
2日目午前終了
- チェックアウトぎりぎりの10:00前にようやく起床(同室の@itemanさんに起こしてもらったm(_ _)m)
- みんな頑張って作業してるのに触発されて、慌てて作業開始。
- そもそもテーマすら決めてなかったから、なかなか決まらず。
- ようやくあれこれやりだした頃にライトニングトーク大会締め切りが来て、エントリ出来ず。
- ただ頭のどこかでは「まあ間に合わなかったら別にいいか。」と思っていた。
2日目ライトニングトーク大会開始
- あ!!!そういうイベントか!!!
- ようやくイベントの核に気づく。
- 発表しない PHP Matsuri なんて、クリープの無いコーヒー、炭酸の無い RedBull やん。。。
PHP Matsuri はどんなイベント?
まず参加前に思っていた「「カンファレンス」「ワークショップ」「ハッカソン」「開発合宿」「アンカンファレンス」が合わさったイベント」は確かにそのとおり。それぞれが単体でイベントできる内容でかつ、わずか30時間の間に同じ場所で一気に繰り広げられるから、濃度が半端無く濃い。それだけに参加した人には強烈な印象(疲れも)が残ります。
ただそれだけじゃなく、参加して分かったのは、実は一番大事なコンテンツは2日目午後の「ライトニングトーク大会」だということ。そもそものコンセプトである「アウトプットする」を実践する「ライトニングトーク大会」こそがイベントで一番のコンテンツであり、@yandoさんが書かれているとおり、まさに「参加者の皆さん自身こそが目玉コンテンツ」ですね。
それを理解した頃には、まさに「後の祭り」なわけで、次回は発表するようにします!
PHP Matsuri を関西で開催したということ
多くの方に参加頂いたわけですが、関西以外からの参加者の方が多く、わざわざ関西に来てもらえて嬉しい気持ちでいっぱいでした。PHPカンファレンス関西の時もそうでしたが、イベントのために来阪してもらえるということはありがたいことですね。
一方で、関西からの参加者が少し少なく寂しい気持ちも若干ありました。
そもそも関西スタッフである自分のアピール不足が原因でもあるので、そこは反省するとして、ただこういった機会はそうは無いので迷っていた方には、できたら参加してもらいたかったなあという思いが残っています。
イベントに参加したからと言って、次の日から人生がバラ色になるとかそういうことはありません。イベントで高ぶった気持ちも翌日からの日常で徐々に落ち着いていき、いつもの日々に戻ります。
ただ確実に心のどこかでは何かが変わります。
上手く伝わるか分かりませんが、自分の中では、日常で徐々に積もっていく閉塞感というか、勝手に自分が創りだした心の壁のようなものを打ち砕くような効果があります。あと、なんとなくですが、スタッフ、スピーカー、参加者に関わらず、参加した人同士の結びつきも感じます。通常のイベントでもこういった感覚はあるのですが、一夜を共にした仲というのは(男女間に限らず)特別なものを感じます。
今後、関西でもイベントを開催していきたいので、次回こそはみなさん参加をお待ちしてます!
次回は?
会場でも次回開催を期待する声が多く、私自身もまた参加したいと思うのですが、いかんせん有料+宿泊イベントということで運営負荷も大きいので、まだどうなるかは未定です。
突然ですが、最後にイベント中のとあるシーンが写真に残っていましたので、ご紹介します。
左から、マイクを持って何かを言っている@shin1x1、真ん中で不敵に笑う@yando、何かを決意したような@k1low。
次回が楽しみですね。みなさん、おつかれさまでしたー。
one more thing…
イベントの翌々日にCakePHPコアデベロッパーの@predominantさんと夕食に行くことができました。
身振り手振りにGoogle翻訳を駆使し、Air に入れていた自社フレームワークのコードを見てもらったり、MotoGP の話をして、とても楽しい時間を過ごすことができました。こういった機会に巡り会えたもの PHP Matsuri のおかげですね。
ありがとうございましたm(_ _)m
- コメント (Close): 0
- Trackbacks: 0
5分でわかる最近のPHP – 2011夏
ここ最近のPHP界隈では、興味深いニュースが続いています。最近PHPを追っかけていない人へ少しづつご紹介。

近頃、PHP界隈でホットなニュースを5つ、つまみぐいして見てみましょう。
1. PHP5.4.0-alphaリリース!
PHPの次期バージョン、5.4.0-alphaがリリースされました。
まだ alpha1 ですが、すでに Traits や Array dereferencing support など様々な機能追加が行われています。
特に Traits は面白い機能で、いずれはこれを利用したフレームワークの登場が考えられます。ぜひ使ってみたい機能ですね。
今後、正式リリースに向けてさらなる新機能が追加されていくようなので、目が離せません。
2. Short syntax for arrays を PHP5.4 で採用!
PHPでおなじみの連想配列を、array()ではなく、JavaScriptのように [] で定義できるシンタックスが採用されました。
$array = [1, 2, 3]; $array = ['foo' => 1, 'bar' => 2];
これは、そもそも2008年に @rsky さんが提案されていたもので、議論に上がる->リジェクトされる or 放置されるを繰り返して、3年越しでようやく実現となりました。@rsky さんの行動がPHPを動かしたわけです。おめでとうございます!
もう array() と書くのは過去の遺産になっていくでしょうね。(次は配列がオブジェクトになって、あの関数群がメソッドにまとまってくれたら。)
3. Built-in web server を PHP5.4 で採用!
PHP5.4 から CLI 版に簡易 Web サーバーが組み込まれるようになりました。
この機能はmoriyoshitさんが作られたものです。すばらしい!
PHPは Apache に組み込んで(mod_php)で利用されることが多いので、そもそもこういった機能は不要では無いか、という議論もあったようなのですが、httpd.conf 等の設定を変更することなく、コマンドラインからすぐに動作確認ができるサーバを起動できるのは非常に便利です。
実は昔、CakePHPにこういった開発サーバを組み込もうと思って、さわりだけ作ったりしたのですが、それが PHP でサポートされるようになりました。
普段の開発もそうですが、新たなライブラリやフレームワークを試す時にも便利ですね。
4. CakePHP2.0 Betaリリース!
CakePHPの次期バージョン、2.0のBeta版がリリースされました。
こちらはPHP5.2.6以降が対象バージョンとなっています。5.3以降ではなく、あえて5.2を範囲に入れているあたりが、1系でPHP4 & PHP5両対応を行っていたcakeらしいです。(個人的には5.3以降を対象にして、namespaceに対応して欲しかった感はありますが。)
alpha版を少し触ってみましたが、フレームワークのクラス構造が一新しており、ソースも前より洗練されている印象です。1系で懸念点であったパフォーマンスについてもbeta版の時点で改善されているようです。
今後の正式リリースに向けて期待が膨らみますね。
- CakePHP 1.3.11 and 2.0.0-beta released | The Bakery, Everything CakePHP
- CakePHP 1.3.11 と 2.0.0-beta のリリース(訳) – 24時間CakePHP
5. Symfony 2.0.0 リリース!
Symfonyの次期バージョン、Symfony2がついに正式リリースされました。
個人的にはPHP5.3フレームワークでは本命だと思っており、これを機に5.3の本格利用がようやく進むのではないでしょうか。日本のSymfonyコミュニティも盛り上がってるので、今後Symfonyがぐいぐい来そうですね。
PHPは止まらない
PHPは、独自の歩みながらどんどん前に進んでいっています。昔の知識のままで語らず、進化を続ける今のPHPに触れてみて下さい。
- コメント (Close): 0
- Trackbacks: 1
FizzBuzzではじめるテスト – 第1回関西PHP勉強会
7/22に大阪市内で第1回関西PHP勉強会を開催しました。

4月にPHPカンファレンス関西を開催して以来、3ヶ月ぶりにPHPの勉強会を開催しました。
参加頂いたみなさんありがとうございました。また発表を快く引き受けていただいた皆さん、本当にありがとうございました。
イベント名に「第1回」と付けたのは、今後も継続して開催していきたいという気持ちの表れです。PHPを軸に色々なテーマで開催していきたいと思うので今後もよろしくお願いします。
スイーツタイム
勉強会をやるときは、休憩時間にみんなで食べるおやつを用意するのですが、今回は @msng さんに色とりどりのマカロンを用意してもらいました。見た目も鮮やかですし、適度な甘さで好評でしたね。(たしかに美味しかったです!)
昨今「スイーツタイム」が話題になっていますが、会場で飲食が可能なら実施することに賛成です。
単純に勉強会で発表を聞くだけでも疲れますし、特に平日夜の勉強会だとお腹が空く時間帯でもあります。そんな時に甘いモノをみんなで食べると自然と笑顔になります。
運営側としてもおやつを配るという行為を介して、参加している方と軽く言葉を交わしたり交流ができます。参加している方同士も「おいしいですねー」と隣の方と一言二言だけでも話ができるきっかけになります。
この効果は意外に侮れません。何度かこのおやつタイムをやっていますが、おやつタイム後のセッションは明らかに空気が変わります。それまで見知らぬ者同士で張り詰めていたような空気が和らぎ、みんなで一緒に聞こう空気に変わります。(もちろん、その固い空気をほぐすのが司会者の役割でもあるのですが、未熟なだけにおやつの力は絶大です)
このおやつタイムは、まっちゃだいふくさんが勉強会でいつも美味しいスイーツを出されているという話を聞いてから始めました。まっちゃだいふくメソッドとも呼ばれるこの手法はかなり有効なので、まだご存知無い方は、まっちゃだいふんさんの発表を見てみてください。
FizzBuzzではじめるテスト
今回はテーマを「テスト」にしたのですが、まずはテストの導入として、FizzBuzzを題材にテストを行う内容で発表しました。
テストの基本の考え方から、Selenium IDEとSimpleTest+CakePHPで、FizzBuzzをテストする内容になっています。
サンプルソースと資料は以下になりますので、これからテストをやってみようかなーという方は参考して下さい。
テストを動かす時の補足です。
P.13 / Selenium IDE デモ
サンプルソースの app/tests/selenium/account.html がデモ用ファイルなので、 Selenium IDE で開いて実行して下さい。
FizzBuzz の実行
基本は CakePHP の設置方法と同じです。一例としては、サンプルソースをダウンロード+展開して、app/webroot を document_root に設定して下さい。
P.27 / Selenium IDE で FizzBuzz をテスト
サンプルソースの app/tests/selenium/fizzbuzz.html がテストケースなので、 Selenium IDE で開いて実行して下さい。
P.29 / SimpleTest(CakePHP) で FizzBuzz をテスト
http://localhost/test.php にアクセスして、model/Fizzbuzz のテストケースを実行して下さい。テストケースは、app/tests/cases/models/fizzbuzz.test.php 、テスト対象の Fizzbuzz モデルは、app/models/fizzbuzz.php です。
参加された方からのフィードバック
- コメント (Close): 0
- Trackbacks: 1
コードカバレッジ測定ツールPHP_CodeCoverageをCakePHPで使ってみた
PHP_CodeCoverageで、CakePHPのユニットテストのコードカバレッジを表示してみました。
CakePHP標準のテストランナー(test.php)でも単一のテストケースについてはコードカバレッジが表示できるのですが、All tests の時はコードカバレッジが表示されません(All testsでも表示されることもあるようです。hiromi さん、ありがとうございます)。
そこでPHP_CodeCoverageを使って、All testsのコードカバレッジを表示してみました。
1. PHP_CodeCoverage インストール
PHP_CodeCoverageは、PHPUnitでおなじみのSebastian Bergmannが開発した、コードカバレッジやCRAPを計測、表示するツールです。
PEARパッケージで提供されているので、pear コマンドでインストールします。
2011/06/15現在の最新版1.0.4では、PHP5.2.7以上とxDebugが必要となります。
sudo pear channel-discover pear.phpunit.de sudo pear channel-discover components.ez.no sudo pear install phpunit/PHP_CodeCoverage
ソースコードはgithubでも公開されており、README.markdownで詳しいインストール方法なども記載されているので、気になる人は見てみて下さい。
sebastianbergmann/php-code-coverage – GitHub
2. test.php のコードカバレッジを計測する
test.phpで実行する All tests のコードカバレッジを計測します。
test.php を直接改修して PHP_CodeCoverage を実行しても良いのですが、test.php を改修しない方法として、auto_prepend_file / auto_append_file を使う方法で実装します。
auto_prepend_file / auto_append_file を使うと test.php の実行前後に、自動実行するPHPスクリプトを指定することができます。そこで、auto_prepend_file ではコードカバレッジ計測を開始するPHPスクリプトファイルを、auto_append_file ではコードカバレッジ計測終了と結果を書きだすPHPスクリプトを指定します。
2-1. auto_prepend_file / auto_append_file を設定
httpd.confでauto_prepend_file / auto_append_file を設定します。
<VirtualHost *:80>
ServerName example.com
DocumentRooot /path/to/cakephp/app/webroot
<Location /test.php>
php_value auto_prepend_file /path/to/prepend.php
php_value auto_append_file /path/to/append.php
</Location>
</VirtualHost>
2-2. prepend.php
auto_prepend_file で指定したコードカバレッジ計測を開始するPHPスクリプトを実装します。
[/path/to/prepend.php]
ポイントは 7 行目から 14 行目で、PHP_CodeCoverage_Filterを使って、フレームワークやプラグイン、テストケース、 prepend.php、append.php を対象外にしています。
このあたりが考慮されているのがさすがですね。
2-3. append.php
auto_append_fileで指定したコードカバレッジ計測を終了して、結果ファイルを出力するPHPスクリプトを実装します。
[/path/to/append.php]
2-4. 結果出力ディレクトリ作成
append.phpでは app/webroot/coverage ディレクトリに結果ファイルを出力するので、あらかじめディレクトリを作成して apache ユーザに書き込み権限を付与しておきます。
$ mkdir app/webroot/coverage $ chmod a+w app/webroot/coverage
3. test.php で All tests を実行
あとは普通にtest.phpで All tests を実行します。
テストが完了して /coverage/ にアクセスすると、下記のようなコードカバレッジ計測のレポートが表示されます。
内容は CakePHP 標準のコードカバレッジレポートよりも詳細で、全体、ディレクトリ単位、ファイル単位で、行数・メソッド数・クラス数でのコードカバレッジを確認することができます。
左のディレクトリ、ファイルはリンクになっており、クリックするとディクレトリ内のファイルを見ることができます。またファイルをクリックするとPHPソースが表示され、背景色によってその行が実行されたか否かが判別できるようになっています。
4. メモリーエラーが出たら
コードカバレッジ計測では多くのメモリを消費するのでテスト対象のソースファイルが多いと「Fatal error: Allowed memory size of 0 bytes exhausted」といったメモリ不足によるPHPエラーが出る場合があります。
エラーが発生した時は memory_limit にてPHPが確保できるメモリを増やします。
なお古いバージョンの test.php では memory_limit 設定がハードコーディングされている場合があるので、その際は test.php を直接書き換えて下さい。
[app/webroot/test.php]
-ini_set('memory_limit','128M');
+ini_set('memory_limit','512M');
手軽にコードカバレッジを表示
コードカバレッジを計測すること自体はxdebugを使うと簡単なのですが、それを集計して、見やすい形式にまとめてくれるという点で、PHP_CodeCoverage はかなり便利なツールです。
auto_prepend_file / auto_append_file を使う方法を応用すれば Selenium のテストでもコードカバレッジを計測することができそうです。これはぜひ試したいですね。
ここではCakePHPを対象としましたが、もちろん他のフレームワークやアプリケーションでも利用できるので、一度試してみてください。
- コメント (Close): 2
- Trackbacks: 0
CakePHP Modelとの付き合い方(CakePHP Advent Calendar 2010 3日目)
- 2010-12-03 (金)
- CakePHP
CakePHPのModelに悩む人が多いようなので、自分なりの付き合い方(考え方)をご紹介します。

CakePHP Advent Calendar 2010の3日目です。
前日の k1LoW さんのエントリ、参考になりますね。GETのフォームをdata[]でやるとURLがすんごいことになるので、ウチでは別途対応できるライブラリを作ったりしてます。
さて、3日目の今日は書きたいネタは幾つかあったのですが、「君の当たり前に僕らは感嘆させられるんだ」の精神に従って、自分なりのModelの使い方、考え方を書いてみます。
1. 適正なインターフェイスを用意して処理をカプセル化
まず基本的な考え方。
Model(に限らずですが)では、処理単位でインターフェイス(メソッド)を用意して、実装はカプセル化しておきます。
こうすることによって、Modelを利用する側(Controllerや他のModel等)はそのインターフェイス越しに処理を呼び出せば、求める処理が実行され、その結果を得ることできます。
呼び出し側では、求める処理さえが実行されれば良いわけで、その処理自体がどのような実装になっているのかは知る必要がありません。極端な話、求める要件さえ満たせば、処理はどのように行われていても良いわけです。
この処理のカプセル化というのはオブジェクト指向では一般的な考え方ですが、あらためてしっかりとイメージしておくとフレームワークと上手く付き合う方法が見えてくるのかなと思います。
もちろん、これはベースの考え方であって、実際は当然実装も気にしますし、それに応じた呼び出し方も考えます。ただ根底ではこの考え方を意識しています。
2. SQLを直接書いて良いか?
CakePHPが用意しているインターフェイスで簡単に全てのクエリが表現できればそれに超したことは無いのですが、実際のところそうもいかないこともあります。
発行したいSQLはイメージできているのに、それをどうCake風に書けば良いかを悩む、という場面に遭遇した人も多いのではないでしょうか。
もちろんフレームワーク流の書き方を習得する方法も1つなのですが、場合によっては、その処理をメソッド内に閉じ込めておけば、SQLを書いてしまっても良いと思います。
イメージとしては以下。このメソッドを呼び出す側からはregisterという処理を実行してくれれば良いわけで、中でsaveメソッドを呼ぶのか、SQL直書きなのかは関係ありません。
public function register() {
$this->query('複雑なSQL書く');
}
もちろん、これも程度の問題で、基本はModelのメソッドをそのまま使います。ただ複雑なSQL、パフォーマンスが要求されるような箇所では、SQL直書きも許容するということです。
3. belongsTo以外のアソシエーションは使わない
単純なbelongsTo以外のアソシエーションは原則使いません。
複数テーブルのJOINが必要な場合は、DBにViewTable(CREATE VIEW)を作成して、それに対応するModelを作ります。
例えばでは、usersテーブルとuser_classesテーブルをJOINしたViewTableを作ります。
CREATE VIEW v_users AS SELECT u.*, uc.name AS user_class_name FROM users AS u JOIN user_classes AS uc ON (u.user_class_id=uc.id) ;
そして、v_usersを利用するModelを作ります。
class VUser extends AppModel {
public $useTable = 'v_users';
}
VUserモデルでfind()等を実行する場合は、あくまでv_usersという一つのViewTableに対する操作となるので、とても単純です。
さらにViewTableは自分でSQLを書いて作るので、意図したとおりのSQLが発行できます。もしクエリをチューニングするときはSQLレベルで調整できます。
複雑なアソシエーションを覚える、制御する必要が無いので、個人的にはこの方法が気にいっています。
4. アクション毎にModelを作る
Modelには多くの責務があるので、アプリケーションが大きくなってくると1つのModelに処理が増えてきて肥大化する傾向があります。
特にバリデーションなどは画面によって内容が異なる場合もあるので、1つのModelにあらゆる場面での処理を書いていくと複雑になり、メンテナンス性が落ちます。
そこで、処理を分割する、1つのModelの責任を小さくするためにControllerのアクション毎にModelを作成しています。
例えば、UserController#index()ならActionUserIndexというModelを、UserController#edit()ならActionUserEditというModelを作成します。
画面毎の処理はそのModelに書き、共通で利用するような処理はUserモデルに書くというように使い分けています。
もちろん中にはUserモデルをfindするだけで済むような画面もあるので、そういう場合は作らないときもありますが、基本的には作成するようにしています。
これにより画面毎の処理を局所化できるので、特にチームでの開発では効率が上がりました。
5. 例外を積極的に使う
わりと敬遠されがちな例外ですが、積極的に使ってます。例外を使えば、処理の考え方がシンプルになります。
例えば、Modelで何かエラーがあったら例外を投げるようにしています。投げる例外は場面に応じたものを選択しますが、とりあえずエラーなら例外を投げるというルールにします。
また、呼び出し側では、try句の中には正常系だけ書いて、異常系はcatchに書きます。try句の中には正常系処理しか無いので、本来実行すべき処理を追いやすくなります。
もちろん例外を使わず戻り値の規約を作れば同じようなことはできますが、try/catch句のようにPHPの構文で実装の意図を明確にすることができます。
try {
正常系処理
} catch (NotFoundException $e) {
異常系処理1
} catch (AppException $e) {
異常系処理2
}
バランスも大事
フレームワークを使う上で、どこまでフレームワークの仕様に合わせるか、どこからは独自の方法で実装するかというのは悩むポイントですね。
このエントリで紹介した内容はCakePHPの基本的な使い方からは少し外れるものもあります。
ただ、フレームワークを使う目的は、フレームワークを使うことではなく、フレームワークを使って何かを作ることだと思っているので、バランスを取りながらうまく付き合っていきたいですね。
CakePHP Advent Calendar 2010 4日目は、remoreさんです。どんなエントリになるか楽しみですね!
- コメント (Close): 3
- Trackbacks: 0
PHP unserialize()が__destruct()を実行する?
CakePHPのセキュリティホール(まだの方はご対応を!)から、unserialize()が話題になっています。
このセキュリティホールは、外部から送信された値をチェックせずにunserialize()したことが引き金になっており、安全でない値をunserialize()することの危険性が指摘されています。
下記エントリでは、コードを交えてunserialize()から__destruct()が実行される過程が解説されています。
PHP5 __destruct() and unserialize() function – TokuLog 改メ tokuhirom’s blog
念のための補足なのですが、unserialize()から__destruct()が呼ばれるわけではありません。
下記コードは、PHP5.3.3で実行しています。
unserialize()から実行される関数
unserialize()から実行される関数(メソッド)は以下です。
__autoload、spl_autoload*などのオートローダー
デシリアライズするインスタンスのクラス定義を探す時に実行される可能性があります。
unserialize_callback_funcで設定した関数
上記オートローダー実行してもデシリアライズするインスタンスのクラス定義が存在しない場合に実行されます。ini_set()等で設定していなければ実行されません。
__wakeup
デシリアライズしたインスタンスに__wakeupメソッドが定義されていれば実行されます。
unserialize()から間接的に実行される関数
デシリアライズしたインスタンスの操作によって、結果実行される可能性があるのは以下です。
__destruct()
デシリアライズしたインスタンスの参照が0になったタイミングで実行されます。通常必ず実行されます。
__toString() / __set() / __get() / __call() / __invoke()などのマジックメソッド
デシリアライズしたインスタンスへの操作によって実行される可能性があります。
この中で実行されそうなのは__toString()です。unserialize()した値が文字列だと想定して以下のようなコードを書いていると__toString()が実行されます。
func start Foo::__toString func end
unserialize()で__destruct()が実行される?
上記でも触れたように__destruct()は、unserialize()で呼ばれるわけではなく、デシリアライズしたインスタンスの参照が0になるタイミングで実行されます。
これは通常のクラスインスタンスと同じ挙動です。
下記コードを実行してみるとその挙動が確認できます。
実行結果
% php unserialize.php func start func end Foo::__destruct <--- "func end"の後
ただ以下のようにunserialize()の戻り値を変数に格納しない場合は、デシリアライズしたインスタンスへの参照がありませんので、即時に__destruct()が実行されます。
func start Foo::__destruct // unserialize()して、即__destruct()が呼ばれる func end
[おまけ] protected/privateな変数もデシリアライズ可能
ちなみにunserialize()では、protected/privateなインスタンス変数もデシリアライズ可能です。インスタンス変数をprotected/privateにしたところで、unserialize()で汚染される可能性があることには変わりありません。
object(Foo)#2 (3) {
["var1"]=>
string(6) "public"
["var2":protected]=>
string(9) "protected"
["var3":"Foo":private]=>
string(7) "private"
}
でも結果としては実行される
unserialize()が__destruct()を直接実行するわけではないですが、デシリアライズしたインスタンスはどこかで破棄されるので、結果として__destruct()が実行されることには変わりありません。
ただやみくもに「unserialize()が__destruct()を実行する」と考えると問題を見誤る可能性があるのでご注意を。
参考リンク
- コメント (Close): 0
- Trackbacks: 0
CakePHPのSecurityComponentに深刻なセキュリティホールが見つかりました
- 2010-11-17 (水)
- CakePHP
すでにご存知の方も多いと思うのですが、CakePHPに深刻なセキュリティホールが見つかりました。
SecurityComponentの実装に問題があり、結果、外部から任意のコードを実行させることができるという深刻な内容です。
セキュリティホールの概要や攻撃手順については以下のエントリが詳しいですので、ご一読を。
CakePHP の PHP コード実行の脆弱性を使って CakePHP を焦がす
なお、今回の問題はSecurityComponentを利用していない場合は発生しません。
もしSecurityComponentを利用している場合は、以下のいずれかの方法で早急に対策してください。
1. CakePHP1.2.9 or 1.3.6にアップグレードする。
この脆弱性を受けて修正バージョンが出ています。
CakePHP 1.3.6 and 1.2.9 released | The Bakery, Everything CakePHP
2. SecurityComponentを自分で修正する
バージョン変更が容易で無い場合は、SecurityComponent を修正します。
以下に修正差分があるので、このコードをSecurityComponentに適用してください。
https://github.com/cakephp/cakephp/commit/e431e86
3. SecurityComponentの利用をやめる
SecurityComponentを CSRF 対策で利用している人も多いと思いますが、現在のSecurityComponent は必要以上に複雑な実装となっていて内容が掴みづらいという印象です。
いっそのこと自分に必要なシンプルなものを自作してしまうのもひとつの方法です。(私も社内で開発したものを使っています。)
まずは確認を
CakePHPを使っている方は、まずはSecurityComponentを利用しているか確認を。
簡易的ですが、SecurityComponent を使っているかどうかは以下のコマンドでも探せます。
% find app/ -name "*.php" | xargs grep "'Security'" % find plugin/ -name "*.php" | xargs grep "'Security'"
まだ実際のサイトでの攻撃例は聞かないですが、今後発生する可能性があります。お気をつけを。
- コメント (Close): 0
- Trackbacks: 0
PHPで認証して、mod_xsendfileでファイルを出力する
ApacheでX-Sendfileが利用できるmod_xsendfileをPHPと連携して使ってみました。
PHPで認証してから、許可したユーザのみにファイルを出力する、という処理を実装する場合、ファイルはdocument_root外に配置しておいて、readfile()やfpassthru()でファイルを出力するという手法を良く使います。
この方法でも問題無い場合が多いのですが、容量の大きいファイルを出力する際は思ったようなスピードが出ない時があります。
そのような時はmod_xsendfileを使って、ファイル出力の部分をApacheに任せてしまう方法が有効です。
ここでは2010/11/12時点の最新版であるmod_xsendfile 0.12を対象としています。またインストール環境はRHEL、CentOSを想定しています。
mod_xsendfileのインストール
mod_xsendfileはApache2/2.2で動作するモジュールです。
インストールにあたって、apxsコマンドが必要となるので、もしインストールしていない場合はyum等でインストールします。(apxsは、RPMならhttpd-develパッケージに含まれています)
% sudo yum install httpd-devel
次にmod_xsendfileをダウンロードします。mod_xsendfileは、mod_xsendfile.cという1ファイルだけなので、それをダウンロードしてインストールします。
ブラウザからダウンロードする際は問題無いのですが、wgetでダウンロードしようとすると証明書エラーが発生します。–no-check-certificateオプションを付けるとこのエラーを避けることができます。
% wget --no-check-certificate https://tn123.org/mod_xsendfile/mod_xsendfile.c
apxsでmod_xsendfileをインストールします。
% sudo /usr/sbin/apxs -cia mod_xsendfile.c
インストールと共にhttpd.confにLoadModuleが追加されます。
[/etc/httpd/conf/httpd.conf]
LoadModule xsendfile_module /usr/lib/httpd/modules/mod_xsendfile.so
mod_xsendfileを設定する
mod_xsendfileを利用するために設定を行います。
httpd.confでXSendFilePathを指定する
まず、XSendFilePathで出力を許可するパスを指定します。mod_xsenfileではdocument_root外のファイルを出力することができるので、誤って意図しないファイルの出力を避けるために出力対象のパスを指定する必要があります。
例えば、出力するファイルは「/path/to/images」以下にあるなら以下のように指定します。
[/etc/httpd/conf/httpd.conf]
XSendFilePath /path/to/images
XSendFilePathは、VirtualHostやDirecortyディレクティブでも指定が可能です。Directoryディレクティブでは実行するPHPファイルのパスを指定できます。例えば、CakePHPで指定するなら以下のようになります。(出力ファイルがapp/data以下にあるとします。)
[/etc/httpd/conf/httpd.conf]
XSendFilePath /path/to/app/data
XSendFileをonにする
次にXSendFileをonに設定します。これによりX-Sendfileが有効となります。
/etc/httpd/conf*な設定ファイルでも記載できるのですが、アプリ側ファイルの方が都合が良いので、.htaccessに設定を書いておきます。
[/path/to/app/webroot/.htaccess]
XSendFile on
これで設定は完了です。あとはPHPから出力するファイルを指定します。
XSendFileAllowAboveは削除
なおmod_xsendfile0.9ではXSendFileAllowAboveという設定項目があったのですが、これは0.10で削除されたようです。記載しているとエラーになるのでご注意を。
PHPから出力するファイルを指定
mod_xsendfileと使って、PHPからファイルを出力するにはX-Sendfileヘッダで出力ファイルパスを指定します。
下記のようにreadfile()を記載していた箇所(下から2行目)を、X-Sendfileを出力するheader()に書き換えます。
これでmod_xsendfileからファイルが出力されます。
ちょっと気になったこと
X-Sendfileヘッダは外部に出力される?
X-Sendfileヘッダには出力ファイルのパスを指定するので、外部に出力されるのはあまり好ましいことではありません。mod_xsendfileの処理が有効になっていれば、このヘッダは除去され外部には出力されないようになっています。
0バイトのファイルが出力される
X-Sendfileヘッダで指定したファイルパスが、XSendFilePathで指定したパスにマッチしないと0バイトになるようです。必要であれば、XSendFilePathにファイルパスを追加しましょう。
404が発生する
X-Sendfileヘッダで指定したファイルが存在しない場合は404になります。
PHP以外でも使える?
X-Sendfileヘッダをレスポンスヘッダに出力できれば良いので、PHP以外でも利用できるようです。
Apacheで使えるX-Sendfile
数年前にこんなエントリも書いたりしていたのですが、たしか当時はApacheでの実装は無かったと思います。
なんとなくそのままのイメージでいたので、Lighttpd(nginxにも似た機能があったような。X-Accel-Redirectかな。)いいなあと思ってたら、実は結構前からApacheでもできるようになったんですね。
この認証ロジックはPHP、出力はmod_xsendfileと、それぞれ得意な分野に分けるのは、Unixっぽくて好きです。
- コメント (Close): 0
- Trackbacks: 1
PHP5.3+古いCakePHPのDeprecated表示をPHPコードを書き換えずに抑制する
PHP5.3+古いCakePHPで表示される大量のDeprecatedを抑制する方法です。
![]()
これはPHP5.3からE_ALLにE_DEPRECATEDが含まれたためで、非推奨な関数や文法を使っているとDeprecatedエラーが表示されます。
Deprecated表示を抑制する一番単純なのは、error_reporting設定でE_ALLからE_DEPRECATEDを外す方法です。
error_reporting = E_ALL & ~E_DEPRECATED
CakePHPでもデバッグレベルが0であれば、この方法で回避可能です。
しかし開発環境でデバッグレベルを1以上にしていると、フレームワークがerror_reportingをE_ALLに上書きしてしまうため、Deprecatedが表示されてしまいます。
E_ALLを設定している箇所を書き換える方法もありなのですが、開発環境にある複数のプロジェクトについて書き換えていくのも中々骨の折れる作業です。
ということで、CakePHP側のソースは書き換えずにDeprecated表示を抑制する方法を考えてみました。
E_ALLからE_DEPRECATEDを外す
方法は単純。PHP自体のソースを変更してE_ALLからE_DEPRECATEDを外すだけです。
変更箇所は1箇所だけ。
[Zend/zend_errors.h]
-
--- zend_errors.h.org 2010-09-23 18:10:00.000000000 +0900
-
+++ zend_errors.h 2010-09-23 17:05:30.000000000 +0900
-
@@ -38,7 +38,7 @@
-
#define E_DEPRECATED (1<<13L)
-
#define E_USER_DEPRECATED (1<<14L)
-
-
-#define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED)
-
+#define E_ALL (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_USER_DEPRECATED)
-
#define E_CORE (E_CORE_ERROR | E_CORE_WARNING)
-
-
#endif /* ZEND_ERRORS_H */
あとは通常どおりPHPをビルドしてインストールすればokです。
変更前後でE_ALLの値は以下のように変わります。
変更前(オリジナル) % php -r 'var_dump(E_ALL);' int(30719) 変更後 % php -r 'var_dump(E_ALL);' int(22527)
これでerror_reportingにE_ALLが設定されても、Deprecatedは表示されません。
とりあえずですが
本来はDeprecatedな原因を取り除くことが一番なのですが、本番環境はPHP5.2や5.1で、開発環境はPHP5.3にしたいということがあったので、この方法で対処しました。
アプリケーション側のソースは一切変更する必要が無いので楽ですね。まあ開発環境限定ということで。
- コメント (Close): 0
- Trackbacks: 1
ホーム > CakePHP
- 検索
- フィード
- メタ情報







