Displaying posts filed under

プログラミング

CakePHPでトランザクション制御

環境: CakePHP 1.3

CakePHPのModelクラスには直接トランザクションを制御するメソッドがない。そこで例えば次のようにAppModelにメソッドを定義する。

class AppModel extends Model {
    function begin() {
        return $this->getDataSource()->begin($this);
    }
    function commit() {
        return $this->getDataSource()->commit($this);
    }
    function rollback() {
        return $this->getDataSource()->rollback($this);
    }
}

しかしこの方法ではテストがやりにくい。明示的にトランザクションを制御したい時というのは、複数のテーブルにまたがる複雑なロジックを組むことが多いので、commit/rollbackされたことを確認するのにいちいちDBの状態を確認していては手間がかかりすぎる。

そもそも「commitされたらそれまでの実行結果が反映される」「rollbackされたらトランザクション開始前の状態に戻る」というのはDBの機能なので、改めてテストし直す意味はない。アプリケーションのテストとしてはcommit/rollbackが発行されたことさえ分かればいい。

そこでAppModelで定義する代わりに、専用のモデルクラスを作ってそこでメソッドの定義を行うことにした。$useTable を false にしてもDataSource自体は取得できるというのがポイントである。

class TransactionManager extends AppModel {
    var $useTable = false;
 
    function begin() {
        return $this->getDataSource()->begin($this);
    }
    function commit() {
        return $this->getDataSource()->commit($this);
    }
    function rollback() {
        return $this->getDataSource()->rollback($this);
    }
}

これでMockを使ったテストができるようになった。トランザクションを使用する際は ClassRegistry 経由でこのモデルを取得する。

$tx = ClassRegistry::init('TransactionManager');
 
if($tx->begin()) {
  //...
 
  if(doSomething() && $tx->commit()) {
    return $values;
  }
}
$tx->rollback();
return false;

テスト時には ClassRegistry にMockのインスタンスを登録しておいて、begin/commit/rollbackの各メソッドが呼び出されたことだけを調べる。もちろんそれぞれの操作が失敗した場合のテストもできる。

function startTest() {
  $this->tx = new MockTransactionManager;
  ClassRegistry::addObject('TransactionManager', $this->tx);
}
 
function testA() {
  $this->tx->expectOnce('begin');
  $this->tx->setReturnValue('begin', true);
  $this->tx->expectOnce('commit');
  $this->tx->setReturnValue('commit', true);
  $this->tx->expectNever('rollback');
 
  //...
}

フィールドの値をシリアライズするSerializableビヘイビア

環境: CakePHP 1.3

特定のフィールドの値をシリアライズしてから保存する Serializable ビヘイビアを作りました。配列などの構造を持つデータを、ひとつのフィールドに保存することができます。

serializable.php – gist
(いずれ正式なリポジトリに移すかもしれない)

fields オプションに指定したフィールドがsave時にJSONでエンコードして保存され、find時には自動的にデコードされます(PHP標準の serialize ではなくJSONを使用するのは互換性を重視しているため)。

var $actsAs = array('Serializable'
  => array('fields' => array('field1', 'field2')));
 
// 保存する前に自動的にJSONエンコードされる
$Model->save(array(
  'field1' => array(1,2,3),
  'field2' => array('assoc' => 'data')
));
 
// find時に自動でデコードされる
$values = $Model->find('first');
// field1 == array(1,2,3)
// field2 == array('assoc' => 'data')

この種のフィールド・シリアライズには様々なバリエーションが考えられます:

  • JSON以外の形式でエンコードする
  • 複数のフィールドの値をひとつにまとめて保存する
  • シリアライズしたフィールドを別のモデルにマップして、hasOne関連のように扱えるようにする

このbehaviorは最も単純な作りになっているので、適当なメソッドを上書きすれば上のような動作も実現できるのではないかと思います。

動的にコールバックを設定するObservableビヘイビア

環境: CakePHP 1.3

モデル用のコールバック関数を動的に登録/解除できるようにする Observable Behaviorを作りました。コールバックを使用するためだけにサブクラスを作ったりビヘイビアを作ったりする必要がなくなります。

observable.php – gist
(いずれ正式なリポジトリに移すかもしれない)

callable なオブジェクトなら何でもコールバックとして使用できますが、PHP 5.3のクロージャと組み合わせると特に便利です。

$actsAs = array('Observable');
 
// addObserver メソッドで登録。戻り値は一意なid
$oid = $Model->addObserver('beforeSave', function($model) {
 
    // 第1引数はモデル、それ以後は通常のコールバック引数
    $model->data[$model->alias] = ....;
 
    // false を返すとコールバックの実行が停止する
    // 何も返さなかった場合(null)は true を返したのと同じことになる
    //return false;
});
 
// 不要になったら取り除く
$Model->removeObserver($oid);

コメント中にもある通り、明示的に引数を返さなかった場合は true と見なされるので、 beforeSave や beforeValidate で戻り値を忘れてその後の処理が実行されない……といったことはありません。

PHP 5.3で共通鍵暗号(openssl)を使用する

5.2以前でも使用できるmcryptについてはこちら

PHP 5.3でopensslモジュールの機能が拡張され、共通鍵暗号による暗号化が利用可能になった。同時に待望の疑似乱数生成器も用意された。これで今までmcryptが使用できなかったWindowsでも、暗号学的強度を持った乱数が生成できる(実際に試していないが、OpenSSLはWindowsのCryptAPIから乱数を得ている)。

まだマニュアルにも詳しい情報が載っていないので、ソースを読みながら使い方を探ってみた。以下はphp-5.3.3のソースに基づく。ただしコミットログを見る限り仕様も機能もまだ安定しているとは言いがたいので、実際に使用するにはもう少し様子を見た方が良いかもしれない。

暗号化する際に決めておくべきこと:

  • 鍵(以下 KEY)… マニュアルでは $password と表記されているが、いわゆる「パスワード」ではない。暗号化に使用する鍵であり、十分にランダムでなければならない。
  • 暗号アルゴリズム+モード(以下 CIPHER)… 利用可能な一覧は openssl_get_cipher_methods から得られる。

暗号化/復号を行う関数 encrypt/decrypt は次のようになる。

//実際の暗号化/復号を行う補助関数
function _callOpenSSL($func, $msg, $iv) {
    $opensslFunc = "openssl_{$func}";
    return $opensslFunc($msg, CIPHER, KEY, true, $iv);
};
 
/**
 * @param string  暗号化したいメッセージ
 * @return array  (暗号文, 初期化ベクトル) の配列
 */
function encrypt($msg)
{
    //初期化ベクトルを生成
    $ivSize = openssl_cipher_iv_length(CIPHER);
    $iv = openssl_random_pseudo_bytes($ivSize, $cryptStrong);
    if($iv === false || !$cryptStrong) {
        // 乱数生成失敗
        return false;
    }
 
    //ダミーの初期化ベクトル
    $dummyIV = str_repeat("x", $ivSize);
 
    //メッセージの暗号化
    $cryptMsg = _callOpenSSL('encrypt', $msg, $iv);
 
    //初期化ベクトルの暗号化
    $cryptIV = _callOpenSSL('encrypt', $iv, $dummyIV);
 
    return array($cryptMsg, $cryptIV);
}
 
/**
 * @param string  暗号文
 * @param string  初期化ベクトル
 * @return string  平文
 */
function decrypt($cryptMsg, $cryptIV)
{
    //ダミーの初期化ベクトルを生成
    $ivSize = openssl_cipher_iv_length(CIPHER);
    $dummyIV = str_repeat("x", $ivSize);
 
    //初期化ベクトルの復号
    $iv = _callOpenSSL('decrypt', $cryptIV, $dummyIV);
 
    //メッセージの復号
    $msg = _callOpenSSL('decrypt', $cryptMsg, $iv);
 
    return $msg;
}
実行:
define('CIPHER', 'aes-128-cbc');
define('KEY', '...暗号化のキー...');
 
$msg = "めっせーじ";
 
// 暗号化
$crypt = encrypt($msg);
var_dump(bin2hex($crypt[0]));
 
// 復号
$plain = decrypt($crypt[0], $crypt[1]);
var_dump($plain);

mcrytと違ってブロック長に合わせたパディング(PKCS#5パディング)はopensslが自動でやってくれるので、自前でbase64エンコードしたりする必要はない。

openssl_random_pseudo_bytes の戻り値は、乱数の生成に失敗した場合は真偽値の false になる。また十分なエントロピーが得られなかった場合は第二引数に指定した変数が false になる(ただ滅多に起こらないはずである:OpenSSL FAQ参照)。

ちなみに鍵の生成も openssl_random_pseudo_bytes で行えばいい。

#コマンドラインにて16バイト=128ビットの乱数を生成
$ php -r 'echo base64_encode(openssl_random_pseudo_bytes(16));
bONq0KiSNIO5ww1ggwdFdQ==

//定数KEYの定義
define('KEY', base64_decode('bONq0KiSNIO5ww1ggwdFdQ=='));

パスワードから鍵を生成する必要がある場合は、自分でPBKDFを実装する必要がある。『PHP PBKDF2』で検索するといくらか実装例が見つかるようだ。またRubyの pkcs5_keyivgen(openssl の EVP_BytesToKey)互換の機能が必要な場合はOpenSSL::Cipher::Cipher#pkcs5_keyivgen の中身などを参照のこと。

openssl_random_pseudo_bytes の中身

実体はOpenSSLの RAND_pseudo_bytes(rand_lib.c) -> ssleay_rand_pseudo_bytes(md_rand.c) -> ssleay_rand_bytes(md_rand.c) で、そこから先は『暗号乱数インフラの初期化処理』を参照のこと。

ssleay_rand_pseudo_bytes と ssleay_rand_bytes の違いは RAND_R_PRNG_NOT_SEEDED を見逃すか否か。エラーがあったこと自体は戻り値で判定できるので、$crypto_strong ではこれを利用している。

Searchプラグインによる検索条件をページングで引き継ぐ(Prgコンポーネントを置き換える)

環境: CakePHP 1.3

CakeDCのSearchプラグインは、全体としては非常に優れたプラグインなのですが、Prgコンポーネントの機能がどうしても私の使い方には合いませんでした。具体的に言うと

  • 検索パラメータの引き継ぎにnamedを使うのは問題が多い(参考)。代わりにQueryStringを使いたい。
  • Searchableビヘイビアと同じような内容の $filterArgs を書くのが手間。
  • そもそも値の検証や制限はモデルでやるべき、と考える。コンポーネント内でバリデーションを実行したりDBアクセスまでするのは仕事のし過ぎ。

これらの点を解消するため、昔作ったSearchPaginationプラグインを全面改訂して、Searchプラグインと連携できるようにしました。Prgコンポーネントの代わりに使うことができます。

http://github.com/tkyk/cakephp-search-pagination (旧版は 1.2 ブランチを参照)

使い方は非常に簡単で、コントローラの中で SearchPagination コンポーネントの setup メソッドを実行するだけです。

class UsersController extends AppController {
  var $components = array('SearchPagination.SearchPagination');
 
  function search() {
    $this->SearchPagination->setup('User');
    //...
  }
}

これによってQueryStringから検索パラメータが取得され、data[ModelName] に格納されるので、あとはそれを Searchable ビヘイビアの parseCriteria に渡すなどして、検索条件を組み立てます。

function search() {
  $this->SearchPagination->setup('User');
 
  // 常に $this->data['User'] に検索パラメータが入っている
 
  $this->paginate['conditions'] = $this->User->parseCriteria($this->data['User']);
  $this->set('users', $this->paginate('User'));
 
  $this->set('groups', $this->User->Group->find('list'));
}

ビューでは特に何もしなくても自動的に検索パラメータが引き継がれます。普通にFormヘルパーやPaginatorヘルパーを使ってください。

echo "<h2>Search Form<h2>";
echo $this->Form->create('User', array('type' => 'get'));
echo $this->Form->input('username');
echo $this->Form->input('created');
echo $this->Form->input('group_id');
echo $this->Form->end('Search');
 
echo $this->Paginator->numbers(array('modulus' => 10));

Formヘルパーのcreateメソッドには、typeとして ‘get’ を指定することをおすすめします。もし指定しなかった場合は(Prgコンポーネントに倣って)自動的にGET用URLを生成してリダイレクトされます。

CakeのURLパラメータに特殊文字を使ってはいけない

環境: CakePHP 1.3

CakePHPはURLパラメータのエンコードを一切行わないため、特殊な文字がパラメータに入り込むと容易にルーティングが破綻する。ここで言う「URLパラメータ」とはRoute中に埋め込まれたパラメータ、namedパラメータ、passパラメータのことを指す。

例えば以下のようなRouteがあったとする。

/controller/action/:keyword

ここでパラメータkeywordに特殊な文字を与えると…

keyword => '%'
URL: /controller/action/%
結果: URLとして不正な形式なので"400 Bad Request"になる
keyword => '?foo'
URL: /controller/action/?foo
結果: ?以降はQueryStringと見なされてルーティングから除外され、Routeにマッチしなくなる
keyword => 'a/b/c'
URL: /controller/action/a/b/c
結果: /がそのままパラメータ区切りになるのでRouteにマッチしなくなる

この問題は単純に rawurlencode/rawurldecode を使うだけでは回避できない。またCakeの設計上の問題なので、簡単な修正方法もない。よって任意の文字が含まれる可能性のあるパラメータをURLに埋め込んではいけない。最も簡単な回避策は、URLパラメータを諦めてQueryStringを使うことである。

Router::url(array('?' => array('keyword' => $any_characters)));

問題の原因と回避策の考察

なぜ rawurlencode で回避できないかというと、CakePHPが mod_rewrite 経由で $_GET['url'] から元のURLを取得しているからだ。

RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

RewriteRuleの後方参照($1の部分)では%エンコーディングのデコードが行われる。またQueryStringから $_GET に格納される際にもPHPによってデコードが行われる。つまりCakeのルーティングが実行される前に、既に2回のデコードが行われている。よってもしこの問題を rawurlencode で回避するなら、3重に rawurlencode した上で rawurldecode しなければならない、ということになる。

ちなみに RewriteRule の[B]オプションを指定することで後方参照のデコードを回避することができる。この場合は rawurlencode は2回で済む。しかしそれでもURLが汚くなることは変わりないし、検索エンジンのロボット等もURL中の文字列を認識してくれないだろう。

RewriteRule ^(.*)$ index.php?url=$1 [QSA,L,B]

別の考え方として、%エンコーディング以外の安全なエンコーディングを用いる方法もある。例えば16進数のバイナリ表現に変換すれば、[a-f0-9]しか現れないので確実に安全である。

$encoded = bin2hex($data);
 
//[0-9a-f]しか含まれないことを事前にチェックしておくこと
$decoded = pack('H*', $encoded);

ただしこの方法だと長さは元の文字列の2倍になるし、可読性も全く損なわれる。ここまでするくらいならやはりQueryStringを使った方が良いと思う。

第2回CakePHP勉強会@福岡 発表資料

7月3日に福岡で開催されたCakePHP勉強会で、『MarkupHelperの紹介および PHPによるDSL実装の可能性について』という題で発表を行いました。以下に資料をアップロードしておきます(右キーで進む、左キーで戻る、上キーでサムネイル表示切り替え)。

資料へのリンク

Studio ODIN – 高橋メソッド風 プレゼンスクリプトを使用しています。発表本番では改造版を使ったのですが、ライセンスが不明なので本家のスクリプトに直接リンクを貼っています。

githubのMarkupHelperリポジトリはこちら:
http://github.com/tkyk/cakephp-markup-helper

補足

質疑応答より。多少内容を変更しています。

Q. デザイナーに触ってもらうのは難しいのではないか?
A. このヘルパーはもともと「プログラマーがHTMLを書くのを楽にしよう」というコンセプトで作られているため、デザイナーとの連携が必要になる場面では使用できない・使用すべきでないと言えます。(デザインが比較的どうでもいい)内部向けの管理画面、ごく小さなelement、HTMLを生成する別のヘルパーの中、などで使用するのに向いています。
別の言い方をするとコードの分量が HTML > PHP の場合には使う意味は薄いです。コードの分量が PHP >= HTML の場合に威力を発揮します。
Q. XHTML以外のマークアップ言語を作ることはできるか?
A. MarkupHelperは「正しいXHTMLのタグか否か」をチェックしていないので、基本的にはどんなマークアップでも作ることができるはずです。ただし「そのタグが閉じタグを持つか否か」という情報を $emptyElements プロパティ(public)で管理しているので、必要に応じて再定義する必要があります。
Q. ループや条件分岐をメソッドチェーンに組み込めるか?
A.PHP言語の機能的な制約により、不可能です(PHP5.3のクロージャを使えば一応はできるかもしれませんが、書式が煩雑なのでDSLとしては実装できないでしょう)。
ループや条件分岐に相当する処理を入れつつメソッドチェーンを切りたくない場合は、その部分の処理を別のヘルパーのメソッドにまとめて連結することをおすすめします。
$markup->ul->MyHelper_loop_li($arr)->end;

……

勉強会で発表するのが初めてだったので緊張してしまい、発表中は聞いている人の反応を見ている余裕がありませんでした。早口に一気にまくしたててしまったような気がして、理解してもらえたかどうか不安だったのですが、あとで感想を聞くとそれなりに好評だったようでほっとしました。それから高橋メソッドによる大きな文字は、Ustream経由でも見やすかったようです。

勉強会の進行は(いくつか想定外の事態があったにも関わらず)大変スムーズで、気持ちよく発表できました。@k1LoWさんはじめ株式会社Fusicの皆さん、素晴らしい時間をありがとうございました。

福岡は魚もお酒も美味しくて、2次会以降ついつい飲み過ぎてしまいました。酔った勢いで、特に3次会ではずいぶん大口を叩いてしまったような記憶があります…。酔っぱらいの戯言が戯言でなくなるように、もっと精進していこうと思います。

グローバル関数のMockを作る

CakePHP 1.3 + SimpleTestでグローバル関数をMock化するためのラッパークラスを作ってみました。libs/ に置いて使用します。

global_function.php – gist

次のように任意の関数を呼び出せます。

//bootstrap.php 等で読み込んでおく
App::import('Lib', 'GlobalFunction');
 
GlobalFunction()->env('REMOTE_ADDR');
GlobalFunction()->gethostbyname('example.com');

テストケースでMockを使用したいとき、GlobalFunction::registerMock を呼び出します。戻り値はMockオブジェクトです。

function testRegisterMock() {
    //使用する関数名を引数に渡す
    $mock = GlobalFunction::registerMock('env', 'gethostbyname');
 
    //通常のmockと同様に使用できる
    $returnValue = '192.168.0.1';
    $mock->expectOnce('env', array('REMOTE_ADDR'));
    $mock->setReturnValue('env', $returnValue, array('REMOTE_ADDR'));
    $mock->expectNever('gethostbyname');
 
    $this->assertEqual($returnValue, GlobalFunction()->env('REMOTE_ADDR'));
 
    //ClassRegistry::flush を呼び出せば元に戻る
    ClassRegistry::flush();
 
    //本物の関数envが呼ばれる
    $addr = GlobalFunction()->env('REMOTE_ADDR');
}

注意:参照を引数に取る関数(array_push など)は使用できません。

mod_rewriteを使ってドキュメントルートを分割する

(特にCakePHPに限定される話題ではありませんが、CakePHPの.htaccessを例に説明します)

通常、Webアプリケーションを構成するファイルは全てソースコード管理システムの管理下に置き、何らかのデプロイツールを用いて本番環境に転送します。本番環境でファイルを直接編集することはしません。

しかし時には一部の画像ファイルやCSSファイルを管理から除外して、自由に編集できるようにしたい場合があります。そういう場合は mod_rewrite を活用して、本来のドキュメントルートの他にもう一つ別のディレクトリを実質的なドキュメントルートとして割り当てることで、高い柔軟性を持たせることができます。

前提

CakePHPで構築したアプリケーションが /var/www/cake にあり、ドキュメントルートは /var/www/cake/app/webroot である。CakePHPデフォルトの .htaccess は次の通り。

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

やりたいこと

/home/files/public に置いたファイルに対して、あたかもドキュメントルートに置いたかのようにアクセスさせたい。
例) /home/files/public/css/main.css に対して http://example.com/css/main.css でアクセスできる。

シンボリックリンクの作成とmod_rewriteの設定

まずドキュメントルート内に /home/files/public に対するシンボリックリンクを適当な名前で作成します。当該ディレクトリに対して Options FollowSymlinks の権限が必要です。

ln -s /home/files/public /var/www/cake/app/webroot/__files

次に mod_rewrite の設定を追加します。

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/__files/%{REQUEST_URI} -d [OR]
RewriteCond %{DOCUMENT_ROOT}/__files/%{REQUEST_URI} -f
RewriteRule ^(.+)$  __files/$1 [QSA,L]
 
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

この方法ではシンボリックリンクを使っていますが、mod_rewrite の設定を httpd.conf に書く場合は Alias を使って実現することもできます。

いずれにせよまず /home/files/public の検索が行われ、次に /var/www/cake/app/webroot の検索が行われます。パスの衝突には十分な注意が必要です。あらかじめ管理外とするパスを固定しておけるのであれば、それがベストなのは言うまでもありません。

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

「CakePHP TOKYO 2010.5.29」ロゴ入りのマシュマロ

会場で配られたマシュマロ

5月29日の『第5回CakePHP勉強会』に参加してきました。

京都から東京まで移動するため、今回初めて夜行バスを使ったのですが……失敗でした。想像以上に揺れがひどくて椅子が堅くて、ほとんど眠れなかったのです。おまけに東京はあまり天気がよくなくて、汗だくになったかと思うと寒くて震えるというような気温の変化が連続して、勉強会会場にたどり着いたときには既に満身創痍という有様でした。

そんなこんなで、プレゼンのたび電気が消されるたびに意識が飛びそうになるのを懸命に堪えつつ、きっちり懇親会まで参加してきました。その感想です。

技術ネタ

全部は書ききれないので、私がコメントできそうなものを選んで感想を書いています。プログラムの全体はEvent Entry::第5回CakePHP勉強会@TokyoおよびMASA-Pさんのブログ記事をご覧ください。

CakePHP1.3の概要 市川さん(@cakephper)

betaのころから1.3を使っていたので、発表中いくつかの機能については「そういえばこれは1.3の新機能だったな」と妙な感慨を持って聞いていました。それだけ1.3の新機能は利便性が高く、一度使えば当たり前になるということです。

一方で、それら新機能の実践的なノウハウについてはまだまだこれから発見・共有されていく段階だな、とも感じました。例えば当日 #cakephpstudy のTLでは「libsとverndorsの使い分けはどうするの?」という疑問が流れていましたし、スライド中routeClassの例として『Using custom Route classes in CakePHP』から転載されたコードは、直接Modelをnewしているためにテスタビリティに難があります。そうしたノウハウはいずれこのブログなどでもまとめていければと思います。

Ktai Library on CakePHP1.3 滝下さん(@ecworks_masap)

幸か不幸かまだ本格的な携帯サイトを作った事はないのですが、将来作るとなったらほぼ間違いなくお世話になるであろうライブラリなので、興味深く聞くことができました。最新版ではktai-devという形でライブラリ統合済のCakeコアも配布しているとのことで、もはや一ライブラリというよりも「CakePHPをベースとした用途特化型フレームワーク」に近づきつつあると言えそうです。それすなわち、CakePHP 1.xの成熟度が一定のレベルに達した証左だと言えるかもしれません。

ただそうして「ライブラリ」がカバーする範囲が大きくなればなるほど、ソフトウェアとしての Responsibility も大きくなります。その点については懇親会で直接お話しさせていただく機会があったのですが、やはり既存のライブラリ・サービスとの兼ね合いや、本来キャリアが提供すべき情報をどこまでサポートすべきかといった点について苦労されているようでした。

コアライブラリのエレガントなハック 清水紘己さん(@hiromi2424)

「Cakeコアの構成要素をいかにエレガントに置き換えるか」という、なかなか聞く人を選ぶ話題でした。個人的には大好物です。特にコンポーネント初期化処理をハックする苦労を味わった同士として、懇親会では固い握手を交わさせていただきました。

CakePHP2.0の概要を見たときに「エイリアス」というのがイマイチ何なのかピンと来なかったのですが、この発表を聞いていてよく理解できました。静的型付言語でいうと置き換えられる「名前」がインターフェイスを規定し、置き換えるクラスが実装クラスという事になります。依存関係が生ずる対象がクラスではなく「名前」なので、コア内部の依存関係であっても後から置き換える事が可能になります。

「CakePHPでjQueryを使ってみた(仮)」 – @nano_eightさん

CakePHPでjQueryを使うなら下手にHelperなど使うよりも直接JavaScriptを書いた方が早い……と考えていたので、flashとelementの組み合わせ+SCRIPTタグべた書きという手法は目から鱗でした。Cake/PHP, JavaScriptそれぞれを分けて考えるのではなく、トータルで最も効率の良い方法をこそ考えていくべきなんだな、と認識を改めました。

「WordPressの管理画面のプラグインにCakePHPを使う」- 原さん(@kara_d)

「あのCMS eZ publishをCakePHPのModelにしちゃう」- @leebennyさん

他のPHP製ソフトウェアとCakePHPを組み合わせて使用する、というアイデアは多くの人が検討したことがあるだろうと思います(私もあります)。@kara_dさんのWordPressプラグインではDispatcherを無効にしたindex.phpを読み込むという大胆な手法で、@leebennyさんのeZ publishではDataSourceを実装するという正攻法で、これを実現されていました。いずれも他のソフトウェアに応用可能な方法だろうと思います。またCakeとは直接関係ありませんが、eZ publishの概略について学べたのも良かったです。

会場・コミュニティの雰囲気など

発表の様子は常時Ustreamで配信され、全国各地のサテライト会場に中継がつながっており、海外からの発表がSkype経由で行われ、会場では記念ロゴ入りのマシュマロが配られ……「コミュニティーベースの勉強会でここまでのことができるようになったのか!」と感嘆せずにはいられませんでした。

この種の勉強会に参加するのが初めてということもあり、正直なところ、ちょっと距離感を掴むのに戸惑ったというのも事実です。ただ懇親会で近くで飲んでいた@nano_eightさんのところに「プレゼン良かったですよ!」と多くの人が集まるのを見て「うらやましいなー」と思ったのもまた事実で、次の機会にはぜひ発表者として参加しよう!と思いました(実は今回もLTのお誘いは受けていたのですが、忙しさと不慣れを理由に断っていたのです)。


……

東京散策ではiPhoneが超役に立ちました。慣れない都市で迷わずに動き回るコツは「自分の勘を一切信用しないこと」なのです。午前中は国立新美術館で開催中の「オルセー美術館展」を堪能しました。モネの『ロンドン国会議事堂、霧の中に差す陽光』とゴッホの『星降る夜』には魂が震えました。もう一回見に行きたいくらいです。久しぶりに歩き回った東京は広々としていました。京都と比べればほとんどどんな土地も広々と感じるのですが、東京も例に漏れず、ずいぶんと空間に余裕があるなあ、と思いました。