Home > アーカイブ > 2008-06

2008-06

第34回PHP勉強会に参加してきました&発表資料

この記事の所要時間: 253

CakePHP勉強会の翌日に行われた第34回PHP勉強会にも参加してきました。

今回はLTで「動かしてコードの流れを追う」というタイトルで話してきました。PHP勉強会では初発表だったので秘かに感慨深いものがあったりしました。

発表時の資料をアップしておきます。

あとスライド中で紹介しているdebug_print_backtrace_source()はこれです。

以下、つらつらと。

maru_ccさん

  • 現場での貴重なお話 => なかなか外に出ない話なので興味深い
  • deployが悩みなのはどこも同じ:-D
  • 発表後にmaru_ccさんを囲んでのdeploy雑談がかなりアツかった!

kaz_29さん

  • PostgreSQLでの全文検索のお話
  • 8.3の全文検索は使える印象 => Ludiaとの比較も見たいなー:-D
  • デモがイイ! => やっぱり動いてるものは説得力が違います

sotarokさん

  • SimpleXML/XMLReaderのお話
  • そたろそたろっく」はNG(w
    => 「そたろっく」の間違いでした><。sotarokさんありがとうございますー:-D
  • sotarokさんがいかにみんなに愛されてるかが良く分かった;-)
  • 軽くSPLをdis => SPLは使える(使いたい)イメージなので意外だった
  • コードを使ったデモが多くてイイ!

shin1x1

  • 反省点は色々あったものの、会場で反応を頂けたので満足です!=> ありがとうございます!
  • IRCのログが見たい。。。(後半に何かツール?が紹介されてたような)
  • 自分の発表のustreamは怖くてまだ見れません><

gusagiさん

  • PHPセッションをDBで共有するお話
  • Webサーバをスケールすると必ず出てくる話なので実用的
  • セッションクラスにsetter/getterを持たして、$_SESSIONへのアクセスもラップしてしまうのもアリかも。

nemo_kazさん

  • IBMのProjectZeroなお話
  • 軽快なプレゼンで楽しかった:-D
  • 割り切りがスゴイ。
  • 割り切り1. 1コンテナで同時に動かせるアプリは1つ。
  • 割り切り2. リクエスト毎にJVMを再起動! => これはスゴイ。速度・負荷が気になる
  • 割り切り3. ブラウザで開発!(Eclipseでも開発可) => ガリガリ書くのは辛そう:-P
  • 家電並にマニュアルを読まない(w

懇親会

  • 端テーブルでまったりしてました
  • アツいユーザ会話 => 恩恵を受けているだけの自分としては感謝感謝です。
  • 例によって書けない話が多数:-D
  • CakePHP勉強会運営組は抜け殻のようでした。。。
  • 新幹線の都合で1次会で無念の離脱。maru_ccさんのレポでは3次会まであった模様。

また参加したい!

今回は発表があったのでちょっと落ち着かないところもあったのですが、かなり楽しかったです。やはりイベントごとは参加してなんぼだなと思います。

土曜日で昼開始だったのでノンビリした雰囲気なのも良いですね。土日参加はなかなか難しいですが、また参加したいです。

会場提供して下さったトライコーンさん(鈴木さん)、勉強会ではcocoitiさん、動画配信では安藤さん、懇親会ではゆどうふさん、ありがとうございました。そして参加された皆さんお疲れさまでしたー。

第3回CakePHP勉強会も盛り上がりました。

この記事の所要時間: 342

第3回CakePHP勉強会に参加してきました。

回を増すごとに熱気むんむんなのですが、今回はあのトライコーンさん(すずきさんいつもありがとうございます!)の会場がびっしり埋まる勢いでした。

特に今回は関東圏外の方が多いのにも驚きました。北海道の方や福岡の方が来てらしたので、大阪からの自分では遠方者扱いとはなりませんでした;-)。ありがたいことです。

~フェイス女学園~ CakePHP を使った効率的なPC・モバイルサイト構築について

  • スパイスボックスラボラトリ 神部さん
  • 受付でバタバタしていてあまり落ち着いて聞けませんでした><
  • 資料公開に期待!
  • サービスが伝播していく流れがリアル
    => 何だかんだいってTVは強力なのかな

CakePHPでの失敗談

  • ブルーオーシャン 岡田さん
  • 「CakePHPポケットリファレンス」出版おめでとうございます!
  • 次期C++を使って、C++版CakePHP? => CakeC++とか?:-D
  • 実際の失敗談は参考になります
  • アソシエーション周りの話はあるある
  • ビヘイビアのインスタンスは一つのみ
    モデルごとの設定は$this->__setting[%モデル名%]以下に書く
  • コンポーネントのstartup()でインスタンス変数がクリアされる話は、?
    => 手元のRC1では再現できなかった

ホッテントリメーカー@CakePHP

  • phaさん
  • 放浪生活は勝手に親近感が:-D
  • ゆるい感じがイイ!
  • 用語が分かりづらかった話はリアル
    => 確かにフレームワークを少しでも知っていないと難しい
    => 解説記事の用語が専門的過ぎる話は結構大事な指摘かも
  • phaさんもCake教に染まってしまったオチ:-D

CakePHPゆとり開発環境

  • k1LoWさん
  • 遠目に見ると知り合いに檄似で勝手に親近感が:-D
  • いきおいある良いLT!
  • Emacsを使ってCakePHP開発を最適化
  • デモが見たかった!
  • rails.vimをCakePHP用にカスタマイズしようとして放り投げたのを思い出した。。。
  • 会場でのエディッタ使用調査は、vim>eclipse>emacs>秀丸>textmateでした:-D

AuthComponentについて

  • 受付をご一緒した高木さん => 助かりました!
  • OpenIDが手軽に使えるのは嬉しい
  • AuthComponent自体を使ってない。。。事例増えてるみたいだし追っかけてみよう>自分

Cakeにテストがやってきた

  • きしださん
  • 自分にはど真ん中ストライクなCakePHPでのユニットテスト話
  • CakePHPでちゃんとしたユニットテスト事例は初めて聞いた気が。
  • やっぱControllerにロジック書いちゃダメ。
  • 1000あるテストケースの実装が見たかった:-D
  • => ユニットテスト勉強会に行きたいな。
  • チケットには「このテストが失敗する」の英語とそのテストケースがあればok:-D
  • CakePHP1.2本、企画中!

何が違う? CakePHP and symfony

  • 司会しつつ動画配信しつつの安藤さん
  • symfonyとCakePHPの違いを「使って」比べる
  • イメージだけで判断してはダメ!
  • モデルの戻り値は、CakePHPが連想配列、symfonyはオブジェクト
  • ヘルパは、CakePHPがオブジェクト、symfony:が関数
  • => モデルの戻り値はオブジェクトがいいなあ。あちこちで出る話題だからいずれはそうなると思うけど。
  • 1.2はsymfonyよりちょい遅い => PTはこれからかな

懇親会

  • 居酒屋の地下室をほぼ貸し切り状態!
  • あちこちのテーブルが盛り上がっていて楽しかったー!!
  • 勉強会を連日はしごする人が結構いてビックリ。みんなアツいなー。
  • ubuntuをレッツノートに入れたくなった;-D

次回はCakePHP本家の人が?

いやあ今回も盛り上がりました。この盛り上がりを見るとCakePHPのパワーを改めて感じました。

次回はいよいよgwoo氏を呼ぼうという流れになっているのでさらに盛り上がりそうですね。

参加された皆さんありがとうございました&お疲れさまでしたー。鈴木さん、高木さん、そして何から何まで安藤さんお疲れ様です!

CakePHP index.php以外からフレームワークを使う

この記事の所要時間: 121

CakePHPでは[app/webroot/index.php]がFront Controllerになっているのですが、これを他のファイル名に変える方法です。(DocumentRootがapp/webrootになっている場合)

1. index.phpを他のファイル名に変更

ここではgw.phpをFront Controllerにします。

$ mv index.php gw.php

2. .htaccessを変更

あとはapp/webroot/.htaccessのRewriteRuleを変更すればokです。

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
#    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
    RewriteRule ^(.*)$ gw.php?url=$1 [QSA,L]
</IfModule>

index.phpはフレームワークを避けるとか

これは以前index.phpだけはデザイナーさんが自由に使いたいという要望があったため考えました。(もちろんPagesControllerを使っても良いのですが)

index.phpはサイトのトップページになることが多いので、アクセスによる負荷を考えて、あえてフレームワークを避けるという手にも使えますね。

第3回CakePHP勉強会&第34回PHP勉強会に参加します

この記事の所要時間: 014

今週末の第3回CakePHP勉強会第34回PHP勉強会に参加してきます。

CakePHP勉強会では運営(手伝い)、PHP勉強会ではLTで話す予定です。

外の勉強会は久しぶりなので楽しみです。

当日参加される方々よろしくお願いします!

debug_backtraceを見やすくしたdebug_print_backtrace_source

  • 2008-06-16 (月)
  • PHP
この記事の所要時間: 041

debug_bracktrace()は手軽にバックトレースが取れるので便利です。

メソッド(関数)の呼び出し順だけではなく、引数の内容や現在動作しているオブジェクトの状態なども取得することができます。

そこでこれらを利用してバックトレースの内容を見やすくしたdebug_print_backtrace_source()を作ってみました。

ダウンロード

debug_print_backtrace_source.php

dBugを内包しているのでライセンスはGPLで。

使用方法

debug_print_backtrace_source.phpを読み込んで、debug_print_backtrace_source()もしくは、dpbs()を実行するだけです。

引数は2つでどちらもbooleanです。第1引数でバックトレース表示後に処理をexitするか否かを、第2引数でバックトレースを逆順で表示するか否かを指定します。指定しない場合はどちらもfalseが有効となります。

表示の見方

a. ファイルパス、行番号、クラス名、メソッド名
b. 現在のインスタンス情報(クリックすれば展開)
c. バックトレース行
d. 引数(クリックすれば展開)

実装した機能

実装した機能は以下です。

  1. ソースコードを表示(前後10行)
  2. バックトレースの内容を並べた
  3. 現在のクラス・メソッド(関数)・オブジェクトの内容では次のトレース内容を表示

1. ソースコードを表示

バックトレースで指し示している箇所前後10行のソースを表示します。これが一番実装したかった機能です。実際にトレースで指定されている行は背景色を変えています。

2. バックトレースの内容を並べた

debug_backtraceでは多くの情報が取れるのでそれらを視認しやすいように並べました。

現在のオブジェクトとメソッド(関数)引数の表示では、dBugを一部変更して使用しています。

3. 現在のクラス・メソッド(関数)・オブジェクトの内容では次のトレース内容を表示

バックトレースの内容では、ファイルパス・行番号とクラス名・メソッド名・現在のオブジェクトがずれているので若干把握しづらくなっています。ですので見やすいように後者については次のトレースの値を表示しています。

ちなみに「ずれている」というのは、バックトレースとしては問題ありません。単に見やすいようにした、というだけです:-D

フレームワークで使うと良いかも

フレームワークのようにクラス構造が複雑で、メソッドの呼び出しが深くなりがちなシステムでは、メソッドの呼び出し履歴が見やすくて便利だと思います。フレームワークのソースを読む時のお供にどうぞ。

フレームワークで使われているPHP関数を数えてみた

この記事の所要時間: 418

CakePHPを使ってからempty()を使うようになった、なんて話が以前社内でもあったので、各フレームワークで使われているPHP関数を調べてみました*1

調べたのはCakePHP/symfony/ZendFrameworkで、それぞれ最新版を使用しています。

あと関数全てを載せると長いので上位20件のみ記載しています。

CakePHP-1.2RC1

array()が圧倒的ですね。2位以下もin_array()から5位のis_array()まではarrayに関係する関数となってます。

さらにarray()自体の数がスゴイです。ソースコードは3つの中で一番少ないのですが(symfonyの約2/3、ZFの約1/3)ですが、array()はsymfonyの約5倍、ZFの約2倍となっています。

いかにCakeがarrayを多用しているかが、良く分かります。

ソース行数:126,469行

16645 array
1519 in_array
1151 isset
889 empty
486 is_array
454 str_replace
378 unset
314 strpos
302 array_merge
270 count
241 sprintf
235 preg_quote
169 define
165 date
148 file_exists
148 preg_match
146 strtolower
144 explode
134 extract
134 substr

symfony-1.1.0RC2

やはりarray()が一位ですね。Cakeと違うのところでは、sprintf()が3位に来ています。さらに4位にsubstr()、5位にstrlen()と文字列関数が上位に来ています。

ソース行数:179,517行

3534 array
1083 isset
729 sprintf
515 count
372 substr
256 strlen
230 is_null
229 is_array
218 dirname
213 empty
204 strpos
201 unset
198 array_merge
164 in_array
156 str_replace
155 strtolower
150 preg_match
134 date
133 implode
124 preg_replace

ZendFramework-1.5.1

ソースコード行数が最も長いZFです。これもやはりarray()が1位となっています。4位にdirname()が入っています。Cakeでは50位(53件)、symfonyで218件(9位)なので、多いように感じます。あとord()が8位に、definedが12位などが上2つとは違う点でしょう。

ord()でソースをgrepしてみるとPDFやLucene、NTP関連のソースが引っかかりました。このへんはライブラリ的要素が強いZFの特徴ですね。

ソース行数:372,103行

8686 array
1528 isset
1407 count
999 dirname
789 is_array
650 empty
542 unset
508 ord
334 strlen
326 substr
295 array_key_exists
286 defined
243 explode
233 is_string
215 strpos
205 preg_match
202 date
201 strtolower
189 define
180 list

DB関数に着目

DB関連の処理は3フレームワーク共にフレームワーク自体に機能があるので、通常はPHPのDB関数を直接実行する必要はありません。これはフレームワーク内の処理も同様で、SQLを直接発行する箇所は1つにまとめておいて、各箇所からはそこを呼び出すイメージです。

よってpg_queryやmysql_queryは1つしか登場しないと思っていました。

Cakeは想像どおりそれぞれ1つしかありませんでした。ZFもPDOを使用しているのでこれらの関数自体が無いのですが、Oracleのoci_executeは2つ(1つはテスト)のみでした。

さて残るsymfonyですが、なぜかmysql_queryが27、pg_queyが20となっていました。あちこちに書いているのには、何か理由があるのでしょうか(歴史的な理由?)。

最後に関数の種類

最後に関数の種類ですが、3フレームワーク共に400前後でした。

もう少しじっくり見ると面白いところがあるかもしれませんが、今回はこのあたりで。

*1 array()やempty()など厳密には関数でないもののありますが、ここでは本質では無いのでスルーしてください;-)

PHP 意図を伝えるコーディング

  • 2008-06-11 (水)
  • PHP
この記事の所要時間: 039

PHPに限らずですが、読む人に意図が伝わるようなコードを書きたいという話です。

なお以下は私の感覚での話しですので、それ違う!という突っ込みがあればお願いします:-D

業務にしろオープンソースにしろ人のソースを見て気になるのがif文です。

if文で真偽値を判定する

メソッド・関数の戻り値が真偽値の場合、true/falseを判定するなら等号(不等号)は無くても良いのではないでしょうか。

つまり(hoge()はtrue/falseを返す関数)

[1]

if (hoge() === true) {
}
if (hoge() !== true) {
}

と書くのではなく

[2]

<?php
if (hoge()) {
}
if (!hoge()) {
}
&#91;/php&#93;
<p>と書けば良いのでは、という事です。</p>
<p>特にPHPでは厳密にtrue/falseを判別するなら[===]で比較する<a href="#1" name="p1">*1</a>ということで、[1]のようなコードを見るのですが、ここで判別したいのは、true/falseの2値のみなので、型を厳密に見る必要はありません。</p>
<p>[2]であればhoge()が2値のみを返す(他の値を返すにしても2値のどちらにみなされるか、だけを判定している)意図が明確になります。</p>
<p>私の感覚では[1]のように書くとhoge()がtrue/false以外の値(integer等)を返すので、あえて[===]で判別しているようにも取れます。</p>
<p>コードの解釈できる範囲を狭めてやることにより、コードの意図がはっきりします。</p>
<p></p>
<p>PHP5で強化されたオブジェクト指向機能を使うことにより、コードの意図を伝えることができます。</p>
<h3>例外</h3>
<p>try/catchで例外を捕捉しているコードなら、その中で例外が飛んでくることが分かります。さらにcatchで捕捉する例外を絞り込めば、飛んでくる例外の種類も分かります。</p>

<?php
try {
  // foo()内でPDOExceptionが発生する
  foo();
} catch (PDOException $e) {
  echo $e;
} 
&#91;/php&#93;
<h3>アクセス権(visibility)</h3>
<p>インスタンス変数にprotected/privateを設定しておけば、その変数が外部からは参照・変更して貰いたくないことが分かります。メソッドについても同様です。</p>
<h3>タイプヒンティング(TypeHinting)</h3>
<p>関数・メソッドの引数にタイプヒンティングでクラス(インターフェイス)名を指定しておけば、与えるべき引数が明らかになります。さらにPHPではタイプヒンティングで指定した引数へはnullも指定できない<a title="引数のデフォルトパラメータにnullを指定すれば、nullも渡すことができます。" href="#2" name="p2">*2</a>ので、メソッド内部では確実に指定したクラスとして扱えます。</p>

<?php
class Foo {}
class Bar {
  public function execute(Foo $foo) {
  }
}

$bar = new Bar();
$bar->execute(new Foo()); // Fooクラスのインスタンスのみ

クラスを継承/インターフェイスを実装

当たり前に使っている機能ですが、これを行えば親クラスやインターフェイスで定義されているメソッドならば(publicなら)呼べることが分かります。またそのクラスがどんな責務を負っているか、何に使うべきかを知ることができます。

意図を伝えられるコーディングを

例をいくつか書いてますが、大事なのはコードを読む人(一ヶ月後の自分も含む!)に意図を伝えられるコードを書きたい、書こうということです。

# コメントも大事ですが、実際に動くのはコードなので、まずコードがあってこそですからね。

追記[2008/06/12]:
if文の話には別の視点があるようです。人間って疲れる生き物なのですよ – よくきたはてダ

*1 PHPの==がキモい件 – hnwの日記
*2 引数のデフォルトパラメータにnullを指定すれば、nullも渡すことができます。

CakePHP 1.2RC1からは比較演算子をキーに書く

この記事の所要時間: 155

via: “1.2RC1でのSQLインジェクション対策” フォーラム – CakePHP Users in Japan

リンク先にあるとおり、1.2RC1ではfind()の条件を指定する際、比較演算子を配列のキー側に書くように変更されています。

CakePHPでは値の先頭にある比較演算子をSQLとして実行してしまう特性があったので、1.2betaまでは以下のようなコードを記述していました。

    // $id = 1が外部から来るとする
    $user = $this->User->find(array('id' => '= ' . $id)); 

しかし1.2RC1でこのコードを実行すると[‘= ‘ . $id]が値として認識されてしまいます。

SELECT * FROM users WHERE id = '''= '' 1';

1.2RC1では条件配列のキーの部分に比較演算子を書くようになっています。

     // 比較演算子の前に半角スペース[0x20]を入れる
     $user = $this->User->find(array('id =' => $id)); 

キーに演算子が無ければ[=]とみなされるので、等号で比較するなら[‘ =’]は省略できます。

     $user = $this->User->find(array('id' => $id)); 

LIKE句なども同様に配列キーに記述します。

     $list = $this->User->find('all', array('conditions' => array('name LIKE' => '%foo%')));

通常、条件配列のキーはコードに直接記述するため、外部からは変更できません。セキュリティを高める意味でこれは良い変更だと思います。

# これでいちいち[‘= ‘ . $value]と書くのから解放されます。:-D

ただ1.2betaまでに合わせて書いたシステムでは、1.2RC1へ移行する際は修正が必要になります。

PHP NULLかどうかはis_null()を使う

  • 2008-06-07 (土)
  • PHP
この記事の所要時間: 021

PHPの==がキモい件 – hnwの日記

同い年(学年は違うけど)なhnwさんの[==]なエントリです。
# この話はPHPを使う上では大事なことなので、知らない方はリンク先を確認してください。

型まで厳密に見るときは[===]を使う必要があるのはエントリのとおりなのですが、NULLかどうかを調べるにはis_null()を使う方法もあります。

SQLのIS NULLと同じノリですね。

[==]と[===]を使い分けずとも、明示的にNULLを判別しているのが分かるので良いかと。

CakePHP PostgreSQLではgetInsertID()に注意

この記事の所要時間: 325

@deprecated

この情報はCakePHP1.2RC1までのものです。2008/06/26現在のリポジトリではチケットが反映され修正が完了しています。

CakePHP+PostgreSQLでModel#getInsertID()を使う場合、別セッションのシーケンス値が取得される問題があります。

問題点

ユーザ情報とその付加情報や、注文情報と明細情報などINSERT後にそのレコードに関連する情報としてid値を活用する場合、本来とは異なるレコードに結びつく可能性があります。

ex) 別の注文情報に明細が登録される等

ただ、これはほぼ同時にINSERT文が発行された際に起こる現象ですので、それほど登録処理が行われないサイトではあまり遭遇するものではありません。(ですので、これまであまり表面化しなかったかと。)

CakePHP1.2RC1/1.2-beta/1.1.19でこの問題があります。

解決策

フレームワークを修正して貰えるようにチケットは投げています。

https://trac.cakephp.org/ticket/4832

現状のソースのままで対応するなら、INSERT前にLOCK TABLEで該当テーブルをロックしておくことで対応できます。(nextval()で直にシーケンス値を変えられるとNGですが。)

<?php
  // Order は Model
  $this->Order->begin();
  $this->Order->query('LOCK TABLE orders IN EXCLUSIVE MODE');
  if (!$this->Order->save($data)) {
    $this->Order->rollback();
  } else {
    $this->Order->commit();
  }
?>

概要

PostgreSQLでは、自分が発行したINSERT文に対するシーケンス値を取得する際はcurrval()というPostgreSQLの関数を使用します。この関数は自セッションで発行された最新のシーケンス値が返ってくるので、別セッションでINSERT文が発行されても値は変わりません。これにより自分がINSERT文で追加したレコードを特定できるわけです。

SELECT currval('hoge_id_seq');

以前はCakeでもこのcurrval()を使って実装されていたわけですが、いつの頃か実装が変わって、現在のlast_valueを取得する形に変わっています。

SELECT last_value FROM hoge_id_seq;

last_valueはシーケン自体の最新の値になるので、上記のように別セッションにてINSERT文が実行されると、そちらで割り当てられたシーケン値が返ってきます。

流れとしては下のようになります。(初期シーケンス値が0、A/Bが同時に登録したと仮定)

[SQL]
CREATE TABLE hoge(
id serial primary key
,name text
);
[/SQL]
[SQL]
A: INSERT INTO hoge(name) value(‘foo’); // id=1
B: INSERT INTO hoge(name) value(‘bar’); // id=2
A: SELECT last_value FROM hoge_id_seq; // 2!
B: SELECT last_value FROM hoge_id_seq; // 2
[/SQL]

この場合、どちらも2がModel#getInsertID()の値として返ってきます。

当然この値をhoge_idとして別のテーブルに登録すれば、本来hoge_id=1に登録されるべきであったレコードがhoge_id=2として登録されてしまうわけです。

頻度は少ないが、発生すると問題

それほど発生することは無いにせよ、発生した際の影響が大きいです。(注文情報とカード番号が別テーブルになっていて、別の注文情報にカード番号が登録されたら。。。)

フレームワークが修正されるのも大事ですが、LOCK TABLEを活用してトランザクション内で別のINSERTが発生しない(シーケンスが進まない)方がより安全に登録処理を行うことができます。状況が許すなら活用してみて下さい。

Home > アーカイブ > 2008-06

検索
フィード
メタ情報

Return to page top