CakePHP1と2のパフォーマンスを比較

CakePHP2.2になったので、1.3, 2.0, 2.1, 2.2で比較しました。
比較したコードは、postsテーブルをbakeしただけの単純なものです。
https://github.com/ichikaway/CakePHP-PerformanceCheckSample


この画面のように、ページング処理が入ったindexアクションを対象に計測しました。
DBには25,000件のレコードが入れてあり、その中から同じ20件を抽出して表示するアプリで比較しています。

環境

サーバ構成

  • サーバ: Dell SC440
    • Pentium Dual CPU E2180 @ 2.00GHz
    • 2G Memory
    • SATA HDD 7200rpm
  • OS: Ubuntu11 64bit
  • PHP 5.3.5 with APC
  • Apache 2.2.17
  • MySQL5.1

計測

Siegeというabのような計測ツールを利用しました。

下記のコマンドを実行。

siege -b -c 10 -t 3S

同時接続10クライアントで、3秒間の間にアクセスを発生し続けるという意味です。
これによって、1秒間に何アクセスさばけたのかというtrans/secが出ます。

計測は、同一サーバ内から行いました。本当は別のマシンから実行したほうが良いのですが、準備も大変ですし、単純に同じ環境で比較するのであれば、パフォーマンスの差を測るには大きな影響がないと思ってやっていません。


結果

このような結果になりました。値が大きいほどパフォーマンスが良いという意味です。


  • CakePHP1.3.11
    • 28trans/sec
  • CakePHP2.0.4
    • 40trans/sec
  • CakePHP2.1.0
    • 38trans/sec
  • CakePHP2.2.1
    • 34trans/sec


1.3からのパフォーマンスは最大40%アップ(cake2.0)でした。CakePHP2もバージョンがあがるごとに機能が増えているので、速度の低下がありました。
今後は2.2.1でボトルネックになっている箇所を見ていきたいと思います。

PHPカンファレンス関西で発表してきました

発表資料です

聞く人がCake知らない人もいるしという前提で作ってしまって、概要が多くなってしまいました。会場にいるほぼ99%の人がCake触ったことあって、8割ぐらいの人が業務で使ったことある状態だったので、もっと個別に注目している点を深く掘り下げていけばよかったと反省。

PHPカンファレンス関西で@suzukiさん、@msngさん、@shin1x1さんとCake2のドキュメントの翻訳が足りてないねという話をしてて、じゃあ福岡でやりましょう!という流れに。7月ごろに翻訳合宿をやるので興味がある人は @cakephper まで連絡ください。

CakePHP2.1.2と、2.2.0-Betaがリリースされました

もう2.2の足音が聞こえてきてますね。
CakePHP 2.1.2 & 2.2.0-beta released」
http://bakery.cakephp.org/articles/markstory/2012/04/30/cakephp_2_1_2_2_2_0-beta_released

CakePHP2.2では、2.0, 2.1とAPI互換のためそのままスムーズに移行できそうです。
以下、CakePHP2.2の主要な追加機能のリスト

  • CakeTimeクラスでタイムゾーンも考慮するようになる
  • ページネーションの件数カウントで、カスタムFindが利用可能に(findCountの代わりに)
  • Setクラスの置き換えとしてHashクラスを利用
  • ヘルパーのLazyLoading
  • Redis cache engine
  • キャッシュに対してグループ設定が可能に。一括削除とかできるように
  • Nested transaction のサポート
  • Dispatcher filtersで独自Dispatcherが差し込み可能に
  • CakeEmailクラスにcharset() / headerCharset()を追加

CakePHPのshellでHtmlヘルパーを利用しURLを生成する

CakePHPのシェルでHtmlヘルパを使ってHtmlHelper::url()を呼び出して、URLを取得する方法です。
CakePHP1.3をベースに書いています(CakePHP2の場合はApp::uses()を使うなどすればいけると思います)。

<?php
App::import('Helper', 'Html');
App::import('Core', 'Router');

class HelloShell extends Shell {
  public function startup() {
    include CONFIGS . 'routes.php';  //(1)
    $this->Html  = new HtmlHelper();  //(2)
  }

  public function main() {
    echo $this->Html->url(array('controller'=>'posts', 'action'=>'index'));
  }
}

Htmlヘルパのurl()メソッド中ではRouterクラスが利用されているため、最初にApp::importでHtmlヘルパとRouterクラスを読み込みます。
次に、シェルの場合はconfig/routes.phpで定義したものが自動で読み込まれないため、これをソースコードの(1)のようにして読み込みます。これを忘れるとうまくURLが生成できないので注意!
最後に、(2)のようにしてHtmlHelperクラスのインスタンスを生成します。


Htmlヘルパを使った独自のMyHtmlヘルパみたいなのを使いたい場合は、下記のようにすればOK
<?php
App::import('Helper', 'MyHtml');
App::import('Helper', 'Html');
App::import('Core', 'Router');

class HelloShell extends Shell {
  public function startup() {
    include CONFIGS . 'routes.php'; 
    $this->MyHtml  = new MyHtmlHelper();
    $this->MyHtml->Html  = new HtmlHelper();
  }

  public function main() {
    echo $this->MyHtml->myUrl(array('controller'=>'posts', 'action'=>'index'));
  }
}

CakePHP勉強会 2012春 @東京で発表できませんでした!

CakePHP勉強会 2012春 @東京 〜ヤザマキ春のCake祭り〜
主催者&メインセッションの発表者だったのに、インフルエンザで欠席しました。
当日は@mon_satさんと@hiromi2424さんが私の発表資料をつかって即興プレゼンしてくれました。ありがとうございました!

発表資料を置いておきます(当日やってもらった資料にいくつか情報が足してあります)


しばらく福岡に住むことになったので、その前にCakePHP勉強会やっておきたいなと思って主催したのに・・・
とはいえ、月に1回ペースで東京出張するので、勉強会参加や飲み会参加には影響がありません! #cake_beer

フォーム入力画面と確認画面で共通で使えるXFormヘルパーをCakePHP2.0対応しました

Cake1用に作っていたXformヘルパーをCake2.0対応しました。(CakePHP2.1.1でも問題なく動きました)
https://github.com/ichikaway/xformHelper/tree/2.0

※現在、PHP5.2ではエラーがでますので、PHP5.3以上が動作条件になります(2012/4/18)


Xformヘルパーは、まったく同じ記述で、フォーム入力画面ではinputタグを出力、確認画面ではPostデータを表示するヘルパーです。これにより、確認画面のViewファイルが入力画面と同じものが使えるようになり工数削減できます。

Cake2に対応した際にプラグイン化しています。

インストール方法

git cloneか、zipファイルをダウンロードし、
app/Plugin/Xformに設置。(Xform以下にはViewフォルダが入るはずです)

app/Config/bootstrap.phpに下記を記述

CakePlugin::load('Xform');

関連するコントローラに下記を記述

public $helpers = array('Xform.Xformjp');

使い方

Viewファイルでは、下記のように呼び出し。

echo $this->Xformjp->input('title');

基本的には、Cake1用の使い方と同じで、使い方は下記に載っています。
フォーム入力画面と確認画面で共通で使えるXformHelper

cakeのエレメントをDB管理する(変数に入ったhtml/phpコードをincludeする)

いくつかのエレメントファイルがview/elements/foo/以下にあって、それをDBで更新することになりました。
ctpの内容をDBの1フィールドに入れて、表示は $this->element(foo/bar)の代わりに DBから取ってきたデータをecho($foo['Model']['bar']);すれば良いかなって思ってたら、なんとbar.ctpの中に

<?php $this->element(baz/menu);?>

みたいな記述があるじゃありませんか!!
このままだと、単純にechoすると、phpのコードがそのままhtml出力されてしまうので対策しました。

結論からいうと、viewテンプレートの中でincludeで読み込めばOK。ただしincludeでファイルを読み込むわけじゃなく、変数に入ってるデータを読み込む必要があります。

ストリームラッパー

Twitterで変数に入れたデータをincludeしたいとつぶやいたら反応があり、ストリームラッパーを使えば変数に入れたコンテンツをincludeできるんじゃないかと、@iakioさんから教えてもらいました。ありがとうございました!

stream wrapperを使うと、includeやfopenでファイル以外にも色々な形式のものが開けて、http://から始まるURLや、ftp://, zlib://なんてものが透過的にread/writeできます。
http://www.php.net/manual/en/wrappers.php

標準のストリームラッパーを試す

data://というストリームがあったので下記のようにしたところうまくいきました。

<?php
$data = 'hello <?php echo "world"; ?>';
include('data://text/plain,'.$data);

ただし、php.iniにallow_url_fopen, allow_url_includeがONじゃないと動きません。この設定はOFFにしておきたかったので、しょうがなくストリームラッパーを作ることにしました。

ストリームラッパーを自作

下記URLに変数に入れたものをストリームラッパーで取り出すサンプルがあり、基本的にはこれで問題ありません。
http://php.net/manual/ja/stream.streamwrapper.example-1.php

使い方は下記のように。

<?php
stream_wrapper_register("var", "VariableStream");
$myvar = 'hello <?php echo "world"; ?>';
include("var://myvar");

$myvarの変数の中身がstream_wrapper_register()によって$GLOBAL['mybar']の中に格納され、VariableStreamクラスではそのGlobal変数を参照してread()メソッドの中でそれを返しているだけです。


ただ、cakephpのviewテンプレートの中でこれを使うと、GLOBAL変数に$myvarの値が格納されず、期待した動作になりませんでした。そこで、色々と改良し、下記のようにしました。

このファイルを、vendors/variable_stream.phpとして保存し、下記のようにすると動作します。

<?php
App::import('Vendor', 'VariableStream');

$val = 'Hello <?php echo "World"; ?>';
stream_wrapper_register("var", "VariableStream");
include("var://".urlencode($val));

includeで渡す値はurlencode必須になってます($valの中にphpタグが入っているとうまく動かないため)。内部ではurldecodeしてデータを扱ってます。

これで、elementのデータをDBから取ってきてviewの中で変数をincludeするだけでOKとなりました。