Home > PHP
PHP Archive
PHPベンチマーク CakePHPでモデルを使用しない
via: PHPベンチマーク: Zend Framework vs Symfony vs CakePHP vs CodeIgniter vs PHP on TRAX – 徒然なるままにBlog
エントリでPHPフレームワークのベンチが比較されており、とても興味深いものです。
ベンチ対象のソースが公開されているので、ざっと見てみると気になるところが一点ありました。
各フレームワークで行った処理はコントローラを呼び出しビューに遷移させて
“Hello World!”を表示させるだけのかなりシンプルな内容です。
DBへの接続やモデルの作成は行わず、自動レイアウト機能があるものはオフにするか全て削除しています。PHPベンチマーク: Zend Framework vs Symfony vs CakePHP vs CodeIgniter vs PHP on TRAX – 徒然なるままにBlog
モデル作成を行わないはずなのにCakePHPだけモデルが生成されるようになっていました。
そこでモデル生成あり・なしでどの程度差が出るかを計ってみます。計測方法は[ab -c 100 -n 100]を10セットしてその平均を出しています。
モデル生成ありのソースは元エントリのまま。モデル生成なしは元エントリのhello_controller.phpを下のように変更しています。
<?php class HelloNoModelController extends AppController{ var $layout=null; var $autoLayout=false; var $uses = array(); // モデルを使用しない function hello() { } } ?>
結果は以下のとおり。
モデル生成あり | 5.145 Request/sec |
---|---|
モデル生成なし | 6.381 Request/sec |
実行環境が元エントリとは異なるので絶対的な数値にはあまり意味が無いですが、両者を比べるとモデル生成なしの方が20%ほど速いようです。
できれば元エントリの環境でモデル生成なし版によるベンチが見てみたいところです;-)。
- コメント (Close): 0
- Trackbacks: 1
Web API実践リファレンスブック
本屋で何気に「Web API実践リファレンスブック」をパラパラと見ていると楽天ウェブサービスのところで拙作Services_Rakutenが紹介されていました!
全然知りませんでした。こういうのって連絡とか来ないんですね。。。
# まあServices_Rakutenは放置していたのでアレですが;-)。
使っている人がいるかどうか分かりませんが、楽天ウェブサービスのAPIもかなり増えているので近日中に更新したいと思います。
- コメント (Close): 0
- Trackbacks: 0
CakePHP 1.1.15.5144以降はHtmlHelper#tagValueに注意
確認画面等で入力値を表示するのにHtmlHelper#tagValueを使っています。
これは内部でHTMLタグをエスケープしてくれるので重宝していたのですが、1.1.15.5144から、このメソッドにエスケープするか否かを表すフラグが追加されました。
[cake/libs/view/helpers/html.php]
<?php function tagValue($fieldName, $escape = false) { ?>
困った事にデフォルトではfalseになっているので、従来の[$html->tagValue(‘Foo/bar’);]のような使い方をしているとHTMLタグがそのまま出力されてしまいます。1.1.15.5144以降(昨日リリースされた1.1.16.5421も含む)でHTMLタグをエスケープするには以下のように第2引数にtrueを指定しなければなりません。
// tagValueでHTMLタグをエスケープ <?php echo $html->tagValue('Foo/bar', true); // もしくはh()で囲む <?php echo h($html->tagValue('Foo/bar')) ?>;
1.1.14.4797以前から1.1.15.5144以降へバージョンアップする際はくれぐれもご用心を。
ちなみに1.2.0.5147ではtagValueがdeprecatedになっており、代わりにvalueメソッドを使うようになるようです。
- コメント (Close): 0
- Trackbacks: 1
CakePHP 比較演算子インジェクションに注意
@deprecated
この情報はCakePHP1.2betaまでのものです。1.2RC1についてはこの方法は有効ではありません。詳しくはCakePHP 1.2RC1からは比較演算子をキーに書くをどうぞ。
CakePHPのモデルで検索条件を指定する場合は比較演算子に注意が必要です。
検索条件では↓な感じで条件値の他にSQLの比較演算子を入れることができます。
<?php class UserController extends AppController { funtion index($id) { $id = is_numeric($id) ? $id : 0; // $id より大きなidを持つレコードを取得 $list = $this->findAll(array('id' => '> ' . $id)); $this->set('list', $list); } } ?>
これを見ただけで分かる人はピンと来ますね。そう外部から送られてきた値に比較演算子が含まれていてもそれがそのままSQL文として動作してしまうわけです。
これがマズそうな場面は色々あるでしょうけど、決定的なのがログイン画面です。例としてメールアドレスとパスワードで認証するアクションを見てみます。
<?php function login() { if (!empty($this->data['User'])) { $conds = array(); $conds['email'] = $this->data['User']['email']; $conds['password'] = $this->data['User']['password']; $user = $this->User->find($conds); if (!empty($user)) { // ログイン成功 } } } ?>
まあありがちな処理だと思いますが、このアクションに[email=!=hoge@example.com&password=!=a]をPOSTすると↓のようなSQL文が発行されてしまいます。(WHERE句以降)
WHERE "email" != 'hoge@example.com' AND "password" != 'a' LIMIT 1
これだとusers.emailにhoge@expample.com、users.passwordにaのどちらもが存在しなければログインする事が可能となります。つまりユーザ情報に登録されていなさそうな値を使えば誰でもログインが可能となります。
他にも登録されているメールアドレスを入力して、パスワードだけ[!=]を使うという手もあります。これならユーザのメールアドレスさえ分かればどのユーザとしてもログインできてしまいます。
この問題の対策は2つ+1つあります。
1. findBy/findAllByを使う
findBy/findAllByを使うとこの現象は発生しません。先程のloginアクションを書き換えてみます。
<?php // $user = $this->User->find($conds); $user = $this->User->findByEmailAndPassword($conds['email'] , $conds['password']);
これなら入力された比較演算子も文字列として処理されます。
WHERE "User"."email" = '!=hoge@example.com' AND "User"."password" = '!=a' LIMIT 1
2. 入力値の前に’= ‘を付ける
findやfindAllで条件を連想配列で渡したい場合は各値の前に’= ‘を付けます。実はfindBy/findAllByでは内部的にこの処理を行ってます。つまりそれを自分でやってしまうというわけです。
ここでのポイントは’=’の後ろにスペースを入れるという事です。これが無いと一部の演算子が有効となってしまいます。
<?php $conds = array(); $conds['email'] = '= ' . $this->data['User']['email']; $conds['password'] = '= ' . $this->data['User']['password']; $user = $this->User->find($conds); ?>
3. 入力値を厳密にチェックする
これももちろん大事です。検索に使用する値が数値のみや日付形式といった制限がある場合はそちらのチェックでこの問題を防ぐことができます。
ただ、実は比較演算子の他に[like/ilike/in/or/not/in/between/regexp/similar to]といったキーワードも同様の問題をはらんでいます。これらは文字列の中では通常の単語として出てくる可能性もある(「Like a child」とか)ので判別が難しいです。
ちなみにこの問題が発生するのは比較演算子・キーワードが文字列の先頭になっている場合のみです。それ以外の箇所にある場合は問題ありません。
find/findAllで検索条件に連想配列を使う場合は注意を
これは忘れるとインパクトがあるのでfind/findByfindBy/findAllByで検索する場合はくれぐれもご注意を。
@see: CakePHPの何か「CakePHPのModelを使う」
- コメント (Close): 2
- Trackbacks: 2
CakePHP テーブルのカラムサイズでvalidate
モデルで文字列の長さをvalidateする際にテーブルのカラムサイズを使う方法です。
CakePHPではモデルがテーブルのカラム情報を保持しているのですが、この情報にはカラム名やデータ型の他にカラムサイズ(varchar(N)等)も含まれています。
そこでモデルのvalidateでこの値を使えば最低限テーブルのカラムに収まるかどうかはチェックできます。
ここでは以下のようなテーブルでnameとemailの文字列長をチェックします。(DBはPostgreSQL)
create table users ( id serial primary key ,name varchar(50) not null ,email varchar(100) not null );
1. AppModelにカラムサイズ取得、文字列長チェックメソッドを追加する
AppModelにカラムサイズ取得用メソッド(getColumnLength)と文字列長チェックメソッド(validatesLength)を記述します。
<?php class AppModel extends Model { /** * Returns the column type of a column length in the model * * @param string $column The name of the model column * @return integer * @access public */ function getColumnLength($column) { $columns = $this->loadInfo(); $columns = $columns->value; $cols = array(); foreach($columns as $col) { if ($col['name'] == $column) { return $col['length']; } } return null; } /** * valid value length * * @param array $data * @param string $fields * @access public */ function validatesLength($data, $fields) { foreach ($fields as $v) { $len = $this->getColumnLength($v); if (!empty($data[$v]) && mb_strlen($data[$v]) > $len) { $this->invalidate($v . '_len_' . $len); } } } } ?>
2. モデルで文字列長をチェックする
モデルで文字列長チェックを呼びます。単に$dataとvalidatesLengthメソッドにチェックするカラムを配列で渡すだけでokです。
validatesLength内部ではテーブルのカラムサイズで文字列長チェックを行い、エラーならinvalidateメソッドを呼びます。このinvalidateメソッドに与える値[カラム名 + ‘_len_’ + カラムサイズ]がポイントです。これをモデルの$validationErrorsに持たせておくことにより、ビューで正規の文字列長を含んだエラーメッセージを動的に出力することができます。
<?php function validates($data = array()) { if (!parent::validates($data)) { return false; } if (empty($data)) { $data = $this->data; } $data = $data['User']; // 文字列長チェック $list = array('name', 'email'); $this->validatesLength($data, $list); } ?>
3. HtmlExtヘルパでエラーメッセージを出力
文字列長エラーメッセージを動的に出力するメソッド(tagLengthErrorMsg)をHtmlヘルパーを継承したHtmlExtに定義しておきます。
<?php class HtmlExtHelper extends HtmlHelper { /** * lengthエラーメッセージ表示 * * @param string $field * @param string $name * @return string */ function tagLengthErrorMsg($field, $name) { if (empty($this->validationErrors)) { return null; } $this->setFormTag($field); foreach ($this->validationErrors[$this->model] as $k => $v) { if (preg_match("/^" . $this->field . "_len_([0-9]+)$/", $k, $m)) { $message = sprintf("%sは%d文字以内で入力してください。", $name, $m[1]); return $this->tagErrorMsg($field . "_len_" . $m[1], $message); } } return null; } } ?>
4. ビューテンプレートでヘルパ呼び出し
最後にビューテンプレートで先程のtagLengthErrorMsgメソッドを呼び出します。これは引数としてカラム名とエラーメッセージ用項目名を与えます。
<!-- エラー時は「氏名は50文字以内で入力して下さい。」を表示 --> <?php echo $htmlExt->tagLengthErrorMsg('User/name', '氏名'); ?> <!-- エラー時は「メールアドレスは100文字以内で入力して下さい。」を表示 --> <?php echo $htmlExt->tagLengthErrorMsg('User/email', 'メールアドレス'); ?>
ソース内に文字列長を書かなくて良い
この方法では文字列長をテーブル定義から取得するので、ソースに文字列長を書く必要がありません。もしテーブルのカラムサイズが変わってもソースはそのままで新しいカラムサイズに対応することができます。
(ちなみにtext型などカラムサイズが無いものについてはテーブル定義情報(モデルの$_tableInfo)に直接文字列長を入れるという方法もあります。)
ここでは1.1系で記述していますが、1.2系ではvalidateが柔軟な形になっているので2.のモデルでのチェックはもうちょっとスマートに書けそうです。
- コメント (Close): 0
- Trackbacks: 0
PHPによる攻撃コードが出現–GIFファイルに隠される?
- 2007-06-21 (木)
- PHP
セキュリティ研究者らは米国時間6月19日、PHPによって記述され、GIFファイルに埋め込まれた攻撃コードを大手の画像ホスティングサイトで発見した。SANS Internet Storm Centerへの投稿によると、この攻撃コードはファイルの先頭に正規の画像を配置することで監視の目をすり抜けたという。
GIFファイルにPHPコードが隠されてたとしてもPHPとして実行されなきゃ問題無いような気が。
なんなんだろーと思って twitter でつぶやいてみると @koyhoge さんと@takesako さんからヒントを頂きました。
自分なりに理解すると、ようはGIFファイルにPHPコードが入ってるんじゃなくて、PHPファイルの先頭にGIFヘッダ+バイナリが入っていると考えれば良いんじゃかいかと。なのでアップロードするファイルは「hoge.gif」ではなく「hoge.gif.php」になるという事でしょう。
これならアップロードした画像(PHPファイル)が<img src=”hoge.gif.php” />で読み込まれた際にPHPコードが実行されるというストーリーが理解できます。
ただこれが成功するにはアップロードしたファイルの拡張子が「.php」になっている必要があるので、アップロード時にサーバ側で拡張子をチェックしておけば簡単に防げるように思います。実際に被害が出ているらしいのですが、被害があったサーバでは拡張子のチェックすらしていなかったということでしょうか。
うーん、何か思い違いをしているような気もする・・・。
[追記]
via: ITmedia エンタープライズ:攻撃コード隠した画像ファイル、大手ホスティングサイトで発見
PHPコード入りGIFファイルが実際に見つかっているようです。通常ならこれをアップロードしたところでどうという事はありませんが、もし万が一このファイルをinclude文やらrequire文等で読み込んでしまう or 拡張子を変える等で埋め込まれているPHPコードが実行されてしまいます。
このファイルは一見するとただの画像ファイルなのでライセンスフリーの画像として配布すれば、インターネット上に広がっていく可能性があります。いつ爆発するか分からない時限爆弾がばらまかれるようなものです。
今のところ爆発する危険性は低いとは思いますが、別のセキュリティホールと結びつくことによりPHPコードが実行される可能性も考えられます。
今後は画像ファイルにPHPコードが含まれているか否かを確かめる必要があるかもしれません。
うーん、どうなんだろ。。
- コメント (Close): 0
- Trackbacks: 0
CakePHP ライフサイクルチャート
via: cakebaker » What happens if you do a request?
CakePHPのライフサイクルがチャートになっています。
URLへのアクセスから出力まで(Controller#afterFilter()まで)書かれています。こうして図になっていると理解しやすくて良いですね。ソースを読む助けにもなります。
# ちょうどこのあたりを文章化していたので実はものすごく有り難かったり;-)
- コメント (Close): 0
- Trackbacks: 0
PHP4なスクリプトをPHP5へ移行する際に発生する7つの問題
- 2007-06-06 (水)
- PHP
2007/11/13: 3.と4.が同じ内容となっていましたので、4.の内容を変更しました。
ここ最近PHP4.1.2で稼働していたシステムをPHP5.1.6へ移行しています。
移行の際に発生した問題とその対応をメモしておきます。
ちなみに全ての内容がPHP4->PHP5で必要なものではないです。PHP4も4.1/4.2/4.3/4.4の間でちょこちょこ変わっているので、一部の内容はPHP4間の移行でも修正が必要になります。またPHP4.4に適応しているスクリプトをPHP5へ移行するなら修正内容は少なくなります。
1.Notice: Only variable references should be returned by reference
「function &method()」と定義しているメソッドでスカラー値を返すと発生します。
<?php class Hoge { function &method() { return false; } } Hoge::method(); [/php] <p>「function method()」にするか、本当に参照を返す必要がある場合ならスカラー値を一旦変数に代入してからreturnします。</p> <?php class Hoge { // 参照を返さなくて良いなら function method() { return false; } // 参照を返す必要があるなら function &method() { $ret = false; return $ret; } } [/php] <h3>2.get_class()がクラス名を小文字で返さない</h3> <p>get_class()でクラス名を判定している時にはまります。PHP4ではクラス名が小文字(hoge)で返されるのに対してPHP5ではクラス名(Hoge)がそのまま返ります。</p> <?php class Hoge {} $obj = new Hoge(); if (get_class($obj) == 'hoge') { // PHP4ならこちらを通る } else { // PHP5ならこちらを通る } [/php] <p>PHP5専用で良いなら比較するクラス名を実際のクラス名(Hoge)に変更します。PHP4との互換性が必要ならget_class()の返り値をstrtolower()で小文字にします。</p> <?php class Hoge {} $obj = new Hoge(); // クラス名を変える if (get_class($obj) == 'Hoge') { } // 小文字にする if (strtolower(get_class($obj)) == 'hoge') { } [/php] <h3>3.Notice: Trying to get property of non-object</h3> <p>PHP4では値がnullな変数のインスタンス変数もnullでした。($obj = nullなら$obj->idもnull)この仕様の是非はともかく、まあこういう仕様でした。PHP5では改善?され、nullな変数のインスタンス変数を参照しようとするとNoticeが発生するようになっています。</p> <?php $obj = null; if (is_null($obj->id)) { echo "foo\n"; } // PHP4.1.2 // foo // PHP5.1.6 // PHP Notice: Trying to get property of non-object in /home/shin/tmp/getter.php on line 3 // foo
$obj->idをempty()で判定するか、$objがnullか否かを判定します。
<?php $obj = null; if (empty($obj->id)) { echo "foo\n"; } if (!is_null($obj) && is_null($obj->id)) { echo "foo\n"; }
4.PHP Fatal error: Cannot redeclare
PHP4.1.2では同じ名前のメソッドが複数存在してもエラーにはなりませんでした。(単に後で定義したメソッドが呼ばれる)PHP5ではFatalエラーが発生します。
<?php class Hoge { function a() { echo "foo\n"; } function a() { echo "bar\n"; } } $obj = new Hoge(); $obj->a(); // PHP4.1.2 // bar // PHP5.1.6 // PHP Fatal error: Cannot redeclare Hoge::a() in /home/shin/tmp/same_method.php on line 7 ?>
5.short_open_tagがoff
ソースに含まれるphp.ini-recommendでは[short_open_tag=off]になっています。もし必要な場合はOnにしておきます。
6.Fatal error: Cannot redeclare method()
PHP4.1.2ではクラス内に同名のメソッドがあっても動作していました(後で定義した方が有効)。PHP5.1.6ではFatal errorが発生するようになっています。(確かPHP4.3.0あたりからこうなってたと思います。)
<?php class Hoge { function method() { echo "method1\n"; } function method() { echo "method2\n"; } } Hoge::method(); [/php] <p>先に定義されているメソッドはどこからも呼ばれていないはずなので削除するか、もし必要ならば別のメソッド名にします。</p> <h3>7.array_push()では参照を格納する</h3> <p>PHP5のオブジェクトは参照渡しが基本となっているので、array_push()でもオブジェクトの参照が連想配列に格納されます。ですのでループ内等で連続でarray_push()する際は注意する必要があります。</p> <p>下の例ではPHP4.1.2ではidが異なるオブジェクトが格納されていますが、PHP5.1.6ではどちらも同じidになっているのが分かります。</p> <?php $array = array(); $a = new stdClass; for ($i = 1; $i <= 2; $i++) { $a->id = $i; array_push($array, $a); } var_dump($array); // PHP4.1.2 // array(2) { // [0]=> // object(stdClass)(1) { // ["id"]=> // int(1) // } // [1]=> // object(stdClass)(1) { // ["id"]=> // int(2) // } // } // PHP5.1.6 // array(2) { // [0]=> // object(stdClass)#1 (1) { // ["id"]=> // int(2) // } // [1]=> // object(stdClass)#1 (1) { // ["id"]=> // int(2) // } // }
PHP5ではオブジェクトを毎回newするか__cloneする必要があります。
<?php // new $array = array(); for ($i = 1; $i <= 2; $i++) { $a = new stdClass; $a->id = $i; array_push($array, $a); } var_dump($array); // clone $array = array(); $a = new stdClass; for ($i = 1; $i <= 2; $i++) { $b = clone $a; $b->id = $i; array_push($array, $b); } var_dump($array);
意外と動きます
バージョン間の互換性が悪いと言われるPHPですが、PHP4.1.2からPHP5.1.6というロングジャンプ;-)でも、意外と動作しました。
専用サーバなどでよく採用されているCentOS(RHEL互換)の最新版(CentOS5)ではPHP5.1.6が採用されました。これにより今後はPHP5への移行が進むのではと期待しています。PHP4なシステムをメンテナンスされている方、一度PHP5環境を試してみてはいかがでしょうか。
- コメント (Close): 0
- Trackbacks: 3
CakePHP 任意のSQLを書く
CakePHPには強力なO/Rマッパーがあるので、普段はSQLを書かなくても良いのですが、DBの関数を呼ぶ時などやはりSQL文を直接書きたい場面が出てきます。
そこでSQL文を直接発行する方法です。
SQL文を直接発行するにはモデルのqueryメソッドを呼ぶ方法とdboのqueryメソッドを呼ぶ方法があります。モデルのqueryメソッドは結局内部でdboのqueryメソッドを呼んでいますので、どちらも取れる値は同じです。
<?php class HogeControlle extends AppController { $name = 'Hoge'; function index() { // PostgreSQL // モデル var_dump($this->Hoge->query('select version();')); // dbo $db =& ConnectionManager::getDataSource($this->Hoge->useDbConfig); var_dump($db->query('select version();')); } } ?>
出力値です。(同じ出力なので一つだけ)
array(1) { [0]=> array(1) { [0]=> array(1) { ["version"]=> string(88) "PostgreSQL 8.2.3 on i686-pc-mingw32, compiled by GCC gcc.exe (GCC) 3.4.4 (mingw special)" } } }
これが良いなあというのはSQLを発行した結果の値がメソッドの戻り値で取得できることです。
発行するだけなら何使っても簡単なんですが、結果を取得するのにfetchしたりするのがちょっと面倒だなと感じていました。(いやコード書いてもしれてるんですが、何となく;-))
[2007/06/07追記]
というかモデルに無いメソッドは__callでもれなくSQL文として発行されるみたいです。なので↓でいけます。(これはちょっとやり過ぎな感も)
<?php class HogeControlle extends AppController { $name = 'Hoge'; function index() { $sql = "select version()"; var_dump($this->Hoge->$sql()); } } ?>
- コメント (Close): 0
- Trackbacks: 1
Ruby on Rails vs PHP?
Hi, I’m Mac もとい I’m Ruby on Rails で始まるコマーシャル。
よくできたパロディです。
Mac vs Windows のパロディ Ruby on Rails vs PHP のコマーシャル : 僕は発展途上技術者
MacのCMっぽく作られてて中々面白いです(ほんと良くできてます)。;-)
ネタなのであまり真面目に取るのはあれですが、Ruby on Rails(フレークワーク)とPHP(言語)を比べるのはどうかと。Ruby vs PHP or Rails vs CakePHP(SymfonyでもEthnaでもZFでも可)なら分かりますが。
これを見てると何年か前にあったPerl vs PHPを思い出しました。
「PHPはPerlより速い」
当時そんな話しをあちらこちらで見聞きしていたのですが、実際に比較されている内容を見るとCGIのPerlとApacheモジュールのPHP(mod_php)を比べて「PHPが速い」とされていました。知っている人から見れば「おいおい」な話なんですが、(私の知る限りでは)わりとそんな話がまかり通っていました。*1
# あと「PHPはDBに強い」なんてのもありましたね。Perlでも似たような事できるでしょうに。:-p
私自身はその頃からPHPで開発をしていたので「Perl よりPHP」「これからはPHP」みたいな当時の風潮は有り難いものでした。それまでPHPを使おうとすると相手を説得する必要があったのが、逆に「PHPで」と指名されるようになったり。
開発に関わる人(クライアントも含む)には非技術者の人もたくさんいます。(というかそちらの方が多い)ですのでイメージは大事です。(「雑誌見ると最近○○がきてるらしいんだけど、次は○○で行こうか。」とか言われたりしません?)
もしその人が持っているイメージと現実が異なる時は「あいつは分かってない」で済ませるのではなく、分かっている人がちゃんと説明するということですね。
# あ、RoRがどうこうという話ではないのであしからず。
*1 Perl vs PHPをキチンと比較したベンチマーク PHPの方が軽くて速いは本当?
- コメント (Close): 5
- Trackbacks: 1
ホーム > PHP
- 検索
- フィード
- メタ情報