【PHP/CakePHP】phpadvent2010 Day21「ピュアPHPでも便利なCakePHPを使おう」

PHP Advent Calendar jp 2010 21日目担当のMASA-Pです。
CakePHP界隈ではいろいろとやらせていただいていますが、PHPコミュニティの方はなかなか日程等があわない関係もあって不参加状態ですので、もしかしたら初めましての方もいらっしゃるかもしれません。一応こんなものなんかを公開させていただいております。またこんな本とかこんな本なんかを執筆させていただだいておりますので、もし興味がありましたら是非よろしくお願いします。できれば買って!(笑)

さて、今回はPHP Tipsという事なのですが、非常にお恥ずかしい話ですが当方は既にCakePHPがないと生きていけない体になっております(笑)。当方としましてもそういう道連れ的な方を一人でも多く製造したい次第ですので(笑)、布教活動がてら「CakePHPに存在している超便利な機能をライブラリとして使ってしまおう!」ということで記事を書いてみたいと思います。実例としては前日のomoonさんの記事とネタが被っているかもしれませんが、まあ続編と思ってもらっても全然構いませんです(^^;。

さて、CakePHPのフレームワークの機能についてここで多くを語るのはやめるとして、CakePHP内には「便利クラス」なるものがいくつか存在します。
特に一押しなのが、今回取り上げさせていただく「Setクラス」です。このクラスは、配列の操作をより便利にする機能を持っています。PHPは標準でも他の言語と比べて配列を自在に操れる方だとは思いますが、かゆいところに手が届かないものもいくつかあります。しかしこのSetクラスを使うと、その辺が簡単に解決できたりする場合があります。
ちなみにどんな機能を持っているのかといいますと…

  • Set::merge()
    配列を再帰的に結合する
  • Set::filter()
    空の要素を取り除く
  • Set::pushDiff()
    配列の差分を付加する
  • Set::map()
    配列をオブジェクトにマッピングする
  • Set::numeric()
    配列内の要素が全て数字かをチェックする
  • Set::enum()
    指定するキーの値を見つける
  • Set::extract()
    配列から指定キーの値を抽出
  • Set::format()
    フォーマットに従って値を抽出・生成する
  • Set::matches()
    配列内の値が条件にマッチしているかを調べる
  • Set::insert()
    配列内に値を挿入する
  • Set::remove()
    配列から指定要素を削除
  • Set::check()
    指定パスの存在をチェック
  • Set::diff()
    配列の差分を抽出する
  • Set::isEqual()
    2つの配列の内容が一致しているかをチェックする
  • Set::contains()
    配列の値が内包されているかをチェックする
  • Set::countDim()
    配列の次元数を調べる
  • Set::normalize()
    配列内の値を正規化する
  • Set::combine()
    2つ以上のパスから新たな連想配列を生成する
  • Set::reverse()
    オブジェクトを連想配列に戻す
  • Set::flatten()
    連想配列の構造をフラット化する
  • Set::sort()
    パスで示した要素を対象にソートする
  • Set::apply()
    抽出結果をコールバック処理

上記メソッドについての詳細はここでは説明しきれませんので、こちらをご覧いただくか「ポケット詳解 CakePHP辞典」を参照してください。できれば買って!(笑) 今回はこの中のSet::extract()とSet::combine()を動作サンプルとして掲載します。

さて前置きはこのくらいにして、さっそくピュアPHPコード内でCakePHPのコードを使用する方法をご紹介しましょう。

1:CakePHPのコードを配置する

先でも後でも良いのですが、とりあえずCakePHPコードを配置してしまう事にします。
今回は、ドキュメントルート内に利用元のピュアPHPコードを展開し、「includes/」内に利用したいCakePHPコードのファイルのみをを入れます。ドキュメントルートとは別の一にCakeのパッケージを丸ごとインストールして、そこを参照したりしても良いかもしれません。
CakePHPはこちらからアーカイブを入手し、その中の「cake/libs/set.php」をincludesにコピーします。また、Setクラス内では同じくCakePHPライブラリである「Stringクラス」も呼び出していますので、「cake/libs/string.php」もコピーします。その他にもクラスを呼び出しているものがありますがそれは次のステップで。

2:ダミークラスを作成する

CakePHPのコードを使うための、超最低限の機能(?)を有するコードを作成します。
includes内に「cake.php」ファイルを作成します。内容は次のようにします。


<?php
define('DS', DIRECTORY_SEPARATOR);

//CakePHP内での基底クラスのダミー
//
class Object {
}

//CakePHPのAppクラスの代替メソッド
//
class App extends Object {

	//ファイル読み込み(App::import()の代替メソッド)
	//
	function import($type, $class){
		include(dirname(__FILE__).DS.strtolower($class).'.php');
	}
}

Objectクラスは、CakePHPのほとんどのクラスが継承している基底クラスで、ライブラリでも使用している場合があります。ダミークラスを作成して問題を起きにくくしておきます。
App::import()はCakePHP内で自在にincludeするための便利メソッドで、これもライブラリ内で用いられています。SetクラスでもStringクラスを呼び出すために使用しています。本来は$typeでクラスの種類(ControllerとかModelとか)を指定するのですが、今回の実装ではincludesディレクトリに全て突っ込んでしまうため、無視します。$classはupperキャメルケースで表現されていますのでアンダースコア形式に変換する必要がありますが、今回はインチキしてstrtolower()してしまっています。厳密にやるのなら「cake/libs/inflector.php」内のメソッドを用いるとできると思います。

3:目的のピュアPHPコードの作成

最後に目的のコードを記述します。今回は次のようなコードで実験する事にします。


<?php
//Includes
//
require_once('includes/cake.php');
require_once('includes/set.php');

//Example : CakePHPのSetクラスをピュアPHPでも便利に使ってみる!
//

//データ
//
$results = array(
	array(
		'User' => array(
			'id' => '1',
			'loginname' => 'saito',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '101',
			'name' => '斉藤',
			'type' => 'baseball',
		),
	),
	array(
		'User' => array(
			'id' => '5',
			'loginname' => 'honda',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '102',
			'name' => '本田△',
			'type' => 'soccer',
		),
	),
	array(
		'User' => array(
			'id' => '10',
			'loginname' => 'ishikawa',
			'password' => '0123',
		),
		'UserProfile' => array(
			'id' => '103',
			'name' => '石川',
			'type' => 'golf',
		),
	),
);

//処理1:名前だけを抜き出す
//
$arr = Set::extract('/UserProfile/name', $results);
var_dump($arr);
echo"<br />\n";

//処理2:User.id = 5の名前だけを抜き出す
//
$arr = Set::extract('/User[id=5]/../UserProfile/name', $results);
var_dump($arr);
echo"<br />\n";

//処理3:(User.id) => (UserProfile.name)の連想配列を入手する(select文生成などで利用)
//
$arr = Set::combine($results, '{n}.User.id', '{n}.UserProfile.name');
var_dump($arr);

実行結果は次のようになります。

array(3) {   [0]=>   string(6) "斉藤"   [1]=>   string(6) "本田△"   [2]=>   string(6) "石川" }
array(1) {   [0]=>   string(6) "本田△" }
array(3) {   [1]=>   string(6) "斉藤"   [5]=>   string(6) "本田△"   [10]=>   string(6) "石川" }

SetクラスはCakePHPユーザでもあまり使われていないかもしれないのですが、この便利さは魔力を秘めているというのか使い出したら止まりません(^^;。上記2つはデータベーステーブルから特定の値を抜き出したりする場合に威力を発揮すると思います。また、今回触れてはいませんが、Set::merge()やSet::sort()なんかもかなり便利です。array_merge()は1次元目の配列要素に対してのみマージが行われますが、これは多階層の配列に対して再帰的にマージが可能です。またsortについても、特定のパスの値に対してソートをかける事ができるので、例えばとりあえず取ってきた複数レコードのデータをid順に並べ直したりといったことが簡単にできます。
しかしSetクラスは少々CPU%を使う仕組みだと思うので、peclかなんかでモジュールを利用する形で作ったりすると良いのかもしれませんね。いや、できればPHPで標準化されて欲しいものばかり!(笑)

ぱっと見なのですが、Setクラスだけでなく次のクラスライブラリもたぶん同様の方法で使えると思います。

  • Inflectorクラス
    アルファベッド表記の名前を単数形←→複数形変換したりキャメルケース←→アンダースコアに変換したりできるクラス
  • Stringクラス
    UUIDを生成したり、テンプレート文字列に要素を挿入したりできる機能のあるクラス
  • NumberHelperクラス
    通貨など数値関連のフォーマッティングをしてくれるクラス。なおAppHelperクラスを上記のObjectクラスのようにダミー化する必要があります
  • TImeHelperクラス
    時間に関するフォーマッティングをしてくれるクラス。strtotime()等の機能をより便利に拡張します

他のクラスにも有用なものがありますが、ちょっと大がかりな工夫をする必要がありそうなので、興味があったらチャレンジしてみてください(Validationクラスくらいなら比較的簡単にけるかもしれない)。

アマゾンのサーバでエラーが起こっているかもしれません。一度ページを再読み込みしてみてください。

明日は kashioka さんの担当です。よろしくお願いします!
それではMerry Xmas!!

【CakePHP】「Media Plugin」の概要と構成

cake-logo先の記事で「Media Plugin for CakePHP」のセッションスライドを紹介させていただいたのですが、実際にソースを調べたりして分かったことをまとめてみることにしました。

まずは序章ということで、概要とプラグインの構成を紹介させていただきます。
そして、実際に利用する手順などを後の記事で紹介させていただく予定です。

概要

「Media Plugin for CakePHP」は、画像や動画などのいわゆる「メディアファイル」を始めとした、ファイルアップロード及びそれらの管理を簡単かつ安全にに行うためのプログラム群です。プラグイン形式で提供されているため、プログラム管理がしやすく、またこれらを部品として利用することが出来るため、カスタマイズ性に優れています。

構成

Media Pluginは、大きく分けて次のパートから構成されています。

■Transfer Behavior
ファイルを「外部から持ってくる」ためのビヘイビアで、「ファイルバリデーション」と指定ディレクトリへの「ファイル転送」を担当します。
「ファイルバリデーション」は、一般的な「容量」「ファイル形式」といったチェックだけでなく、不正ファイルを検査するための強力なチェック機能を提供します。MIME Typeと拡張子、ファイル内容を相互的にチェックすることで、そのファイルが信頼性あるものかを調べます。また、画像サイズや送信元の場所(ディレクトリやURL)、パーミッションなども調べます。
「ファイル転送」は、実際にファイルを読み込んで指定ディレクトリへ保存します。転送はには、PHPのアップロード関数ではなく、Cake標準のソケット機能を使用しているようです。

■Media Behavior
Transfer Behaviorはファイルそのものを「持ってくる」動作を担当していますが、Media Behaviorではそれ以降の「持ってきたファイルの取り扱い」を担当します。
中でも興味深いのは「バージョン」という概念です。一つのアップロードファイルからいろいろな加工を行い、それを一元管理する機能を有しています。例えば、画像ファイルを1つアップロードしただけで大中小の画像を作り、それを実際のサイトで扱うことが出来ます。画像だけでなく、他のファイルでも面白い機能を持っています。例えば、JavaScriptやCSSでは、ソースの中身を圧縮して難読化することも出来たり、ビデオ映像からサムネイル画像を作りだすことも出来るようです(未確認)。

■Polymorphic Behavior(2009/07/18追記)
このビヘイビアは、メディア関連に限らず、いろいろなモデルに適用できる大変に便利なものです。
取り付けたモデルのフィールドに、関連づけたいモデルとそのIDを用意すると、自動でその記述モデルのアソシエーションを設定し、データを引っ張ってきて検索結果に取り付けます。
唯一残念な点は、このビヘイビアは「belongsTo」として他のモデルを取ってくる点です。つまり、1つのデータしか関連づけできません。このビヘイビアは、拡張してhasManyでも適用できるようにすると、更に便利なビヘイビアになりそうです。
一応、このビヘイビアを使う問題点もあります。データレベルで付け替えが出来てしまうため、セキュリティ的に不安な点があります(ぶっちゃけ、Userモデルを引っ張ってくる悪用が出来てしまう)。柔軟に対処するために、このビヘイビアで入手したデータを丸ごと表示するような利用は危険ですが、データを限定して利用するような方法なら便利に活用できることでしょう。

[200/907/19追記]
このビヘイビアが「belongsTo」である理由は、純粋に「Attachmentモデル(もしくはそれに相当するモデル)から動的に親モデルをたどるため」に作ったようで、まあつまり汎用的な設計ではないのでしょう。

■Attachment Model
上記3つのビヘイビアの導入をテンプレート化したモデルです。
特にこだわりが無いのであれば、このモデルを利用すれば簡単にファイル管理が行えます。
もしくは、このモデルを加工して自分のモデルを作成するのも良いかもしれません。

■Medium Helper
管理しているファイルを実際にサイト上で表示したりリンクを生成したりするヘルパーです。
例えばフラッシュやムービー、オーディオ、PDF等の埋め込みHTMLの生成を行ったり、リンクをつけたりすることが出来ます。
また、「バージョン」管理をしている場合でも適切な画像を呼び出します。

■Medium
メディアファイル管理の実体要素、オブジェクトクラスです。実際には、各ファイルタイプ別にオブジェクトクラスがあります。

■Adapter
各ファイルの固有の操作を統一化して扱うためのカプセルで、datasourceのような働きをします。例えばgdを用いて物理的に画像情報を入手したり、画像のフィッティングを行ったりするのはアダプター(gdアダプター)で行います。

■その他
その他、ファイルの格納場所を生成・管理するするコンソールがあります。

【Ktai】重要度の高いバグのお知らせと対処方法

icon_ktaiバージョンアップ版をなかなか更新できなくて申し訳ございません。仕事の方が緊急で忙しくなってしまい、現在また徹夜です(大汗)。本日がとりあえずの山場になりそうなので、もう少しお待ちください。

お待ちいただく間に、一つ重要度の高いバグを見つけました。ご報告と共に、対処方法をお知らせいたします。

【現象】
リダイレクトを行うと、予定していた場所に飛ばない、もしくはループする。

【理由】
Ktaiコンポーネントのextendsが「Object」ではなく「Component」になっていたため

【対処方法】
1:extendsを修正
class KtaiComponent extends Component {

↓↓↓

class KtaiComponent extends Object {

2:initialize()とshutdown()内に含まれている「parent::~」を削除

以上で正常動作をすると思います。
完全に私のポカミスです。
申し訳ございません。

kenji0302さんが指摘されていましたが、上記の対処が正式だと思います。
いつもありがとうございます。

なお、かる~い進捗ですが、「UTF-8絵文字の報告終了後の文字化け」は、どうやらサイト側の問題でした。文字エンコーディングの設定がされていない部分を見つけましたので、おそらくSJISコードをUTF-8内で表示しているだけだと思います。一応念のために対策をしたコードが用意できましたが、まだサイト等には上げていません。
そしてmailtoの不具合ですが、こちらはまだ未解決です。仕事が忙しくて実際に検証が出来ていません。
他には特に問題が出ていなそうなので、こちらの方を解決した後にとりあえず公開しようかと考えています。一つ厄介な問題を見つけたのですが、こちらは根が深くて考え中が、実害はないとは思います。簡単に書くと「直接emoji()で作った絵文字をさらに全体変換してしまう…つまり二重変換の可能性が出ているのですが、docomoとAUはかぶっているコードがないので、とりあえずは大丈夫ではないか…という感じですね。

げーもう4時過ぎか…
とりあえず仕事に戻ります。

【Ktai】Ktai Libraryが海外サイトでも捕捉されたみたいです…が

icon_ktaiKtai Libraryですが、なんか知らない間に海外サイトに取り上げられたようです。

▼Japanese Mobile Support for Cake PHP
http://blog.mobalean.com/2009/03/16/japanese-mobile-support-for-cake-php

何かいろいろと書かれているみたいですが…(大汗)
「オブジェクト指向に従っていない」「ヘルパー・コンポーネントで同じ関数を複製している(Railsで言うFowardable moduleのようになっていない)」みたいなことが書かれていますが、言い分は何となく分かるのですが、それをあえてやっていない理由をくみ取っていただけなかったのは残念だなぁ…
「キャリアコード」云々と言っている部分は意味が良く分からなかったんですが、要は「もっとシンプルに書ける」って事でしょうか。まあ「キャリアコード」というのは後から書いた部分なので、無駄があるのは承知してますので、今後の対応ですかね。

ちょっと大人げなく反論してみました(^^;;;
というか、海外でダウンロードされることはまったく予期していなくて、しかも国内よりも早く、具体的にソースの感想が述べられていたのは驚きでした。そういった意味で感謝。

【Ktai】「Ktai Library」のちょっとしたTipsとか

icon_ktaiとりあえず版ということで、Ktai Library を公開したわけですが、思った以上に反響があり嬉しく思います。特に ktai.org さんに取り上げていただいたことでかなりのアクセス数があります(現在進行形です)。本当にありがとうございます。CakePHPフォーラムの方は、後で自作自演しようかと思いましたが、newsに先を越されてしまいました(笑)。こちらもありがとうございます。

とにかく急いで上げたためにいろいろと落ち度がありますが、そのフォローも含めていくつか情報を書きたいと思います。

■UTF-8で製作したページを無理矢理利用する方法

Ktai Libraryでは、今のところUTF-8での利用を推奨しておりませんが、(たぶん)使えないわけではないと思いますので、一応やり方を記載しておきます。試していないので、出来ない場合はご容赦を。

CakePHP1.1等古いバージョンでは、afterRender()内でob_get_clean()でアウトプットをゲットしてコンバートする手法が一般的ですが、最近のバージョンはこのやり方では出来ません。何故かというと、バッファにはまだ書かれていなくて、コントローラ内の$this->output内に格納されているからです。なので、afterRender()内で、$this->outputを、mb_convert_encoding()でコンバートします。

しかし、これだけでは「絵文字が化けてしまう」問題が発生する場合があります。化けるのは「文字コードを直接指定した場合」です。つまり数値指定(&#xxxxx;)の場合は大丈夫なはずので、これを利用すれば実現が可能だと思います(数値はShiftJISのもので)。ちなみに、Ktai Libraryを通すと、これらは全て文字コード等に変換されます。

■「機種判別」について

公開サイト内で「機種判別」と書いてしまって大変に申し訳なかったのですが、現時点で「キャリア判別」は出来ますが、「機種を特定する」ことはできません。
これについては、現在機種情報を調べる仕組みを準備しております。機種(AUはデバイスID)だけでなく、画面サイズなどの情報も入手出来ます。Ver0.0.2で搭載予定です。データは既に準備しましたので、あとは関数部作成と他の機能の作成待ちです。おかげでファイルサイズは5倍になりました(笑)。

■次バージョンの話

上記の機種判別の件もそうですが、次バージョン(Ver0.0.2)は次の機能がつく予定です。

  • 機種情報の入手(上記の通り)
  • PHS対応
  • 機種毎の表示画像のフィッティング

上から2つは確実ですが、最後のはもしかしたら落ちるかもしれません。
PHSとEMOBILEについては一部情報が不正確かも知れません(正確なデータが公開されていないため)。

■その他(言い訳とか)

なんか「Ktai Library」をインストールさえすれば、携帯サイトが出来てしまう…と思われがちなのですが、言い訳しますと、あくまでも「サポート」はしますが、実際には他にも自前でケアしないと出来ないと思います。例えばiMODEでは未だにPNG画像は表示できませんので、自前でファイルを選択するような記述は必要ですし、ページ構成の都合上、実質PCサイトとの振り分けは必要だと思います。
本ライブラリは、現時点では「毎度書いていた携帯関連の関数をライブラリ化した」程度のものですので、そのつもりでいただけると。もし「こんなのがあったら」というようなものは、コメント欄などで書いていただければ、頑張ってみたいと思います。

なお、「iMODE時のセッション対応」について、本来なら「これこそ」ライブラリーに搭載するべきなのですが、環境に依存する部分もあり、ライブラリとしての提供は少々難しいかと感じます。うまいやり方が思いついたら搭載します(もしくは、その部分部分をライブラリ化するのは有りなんじゃないかとも考えています)。

【Ktai】携帯ライブラリ for CakePHP1.2公開

icon_ktaiお待たせしました!
CakePHP1.2用の携帯ライブラリを公開いたします。
上記のリンクをたどっていただくか、こちら からどうぞ!

昨日公開のつもりでいましたが、アーカイブ一式を自宅に忘れてしまい、昼アップが出来ませんでした。しかし、ソースの手直しできる時間が出来たため、昨晩改良を行い、コンポーネントも作成したりと若干パワーアップしました。
まあでも、現時点でぶっちゃけ機種判定・絵文字・メアドチェックの3種類しかできないのですが、少しずつバージョンアップしていきますので、宜しくお願いいたします。

ちなみに、開発中にいくつか有用なTipsが見つかりましたので、後日ご紹介したいと思います。

【ktai】携帯ライブラリ&CakePHP1.2用ヘルパーの公開(予定)

icon_ktaiサイトの方が準備できてしまったので、先にサイトを公開いたします。表題の通り、CakePHP用の携帯サイトヘルパーです。

詳しくは上記メニューをたどっていただくか、 こちら をご覧ください。
ダウンロードは明日(12日)昼までには出来るようにします。

【追記】
公開しました。上のリンクか こちら からどうぞ!