特別付録
CI使い始めたらまずやること
Form_validationを継承したクラスを作り、ここに独自のバリデータを書く
application\libraries\MY_Form_validation.php
class MY_Form_validation extends CI_Form_validation { function __construct() { parent::__construct(); } ... }
emailにパッチを当てる
system\libraries\Email.php
public function message($body) { // 日本語メールの文字化けを治す // copy from ci-ja-all-in-one-2.0.1-1.zip //$this->_body = stripslashes(rtrim(str_replace("\r", "", $body))); if (strtolower($this->charset) == 'iso-2022-jp') { $this->_body = rtrim(str_replace("\r", "", $body)); } else { $this->_body = stripslashes(rtrim(str_replace("\r", "", $body))); } return $this; }
form_helperにパッチを当てる
system\helpers\form_helper.php
function form_prep($str = '', $field_name = '')
{
...
if (isset($prepped_fields[$field_name]))
{
//bug:
//そのまま返しちゃだめ。
//return $str;
return $prepped_fields[$field_name];
}
...
if ($field_name != '')
{
//bug:
//field_nameセットしてどうするの?
//$prepped_fields[$field_name] = $field_name;
$prepped_fields[$field_name] = $str;
}
ついでにメソッド追加
//htmlspecialchars のショートカット if ( ! function_exists('_h')) { function _h($data) { return htmlspecialchars( $data, ENT_QUOTES ); } } //echo(_h($data))とおなじこと。テンプレートの中ではなるべくこれを使ってください if ( ! function_exists('echo_h')) { function echo_h($data) { echo( _h( $data ) ); } }
Input.phpにパッチ当てる
system\core\Input.php
function is_post() { return $_SERVER['REQUEST_METHOD'] == "POST"; }
Profilerを開発環境でON
application\core\MY_Controller.php
class MY_Controller extends CI_Controller { function __construct() { parent::__construct(); if( ENVIRONMENT === 'development' ) { $this->output->enable_profiler(); } } ... }
そして全部のcontrollerをMY_Controllerから継承すればおk
application/controller/Hoge.php <?php defined('BASEPATH') or exit('No direct script access allowed'); class Hoge extends MY_Controller { ... }
これでSQLのクエリログとかも出ます!
enjoy CI!
bravewood the PHPer!
いい点/悪い点
以下比較対象はsymfony1.4
いい点
- Route.phpが書きやすい
- 設定はすべてPHP(!Yaml)
- コアのソースコードが
わりとすごい短いのですぐgrepできる - すげーーー軽い
- 何にも依存してない
- インストールコマンド必要ない。ディレクトリ解凍するだけ
- ロードしたいライブラリは、設定でON
何でもかんでもロードしておけばいいってもんじゃねーぞ
- PHP5.1.6で動くよ!
symfonyは1.4で5.1系を切ったから。これは強力なメリット
- クラス名→ファイル名の名前付けが素直でいい
class Super_default extends CI_Controller
ならファイルは
super_default.php だし
symfonyではアンダーバーをつけた時の挙動がよくわからんwww
- 便利なform helperがある
set_radio,set_selectなどの第3引数が優秀すぎて生きるのがつらい
<input type="radio" name="must_flag" value='0' <?php echo set_radio("must_flag", "0", TRUE); ?>/>いいえ <input type="radio" name="must_flag" value='1' <?php echo set_radio("must_flag", "1"); ?>/>はい
- 設定を環境毎に別ファイルに書ける
以下の様に設定が分けられる
config/development/email.php config/production/email.php
たとえば、開発環境ではメール送信にgmailを、本番環境ではsendmailを使うとし、本番環境は共有サーバーなのでアカウント情報はアップしたくない場合
単純にdevelopment以下をアップしなければOK(index.phpに設定変更は1行必要だが)で対応できる
symfonyの場合設定は一つのymlに別環境の設定を分けて書いてあるので、共有サーバーなどに置きたくない設定値(メールサーバーパスあーどとか)があれば、ファイルの中身をいじってデプロイしなければならないなど面倒
- sessionは設定変えるだけでDBにかけるよ
- 当然あってしかるべき機能だがと思うけど一応ほめておくか。すごいすごい
悪い点
DB周り
- ORMが
くそ簡易機能。
Event_modelってモデルクラスがあったとして、このメソッドで1件EventをSelectしたとするじゃん、この戻り値って何オブジェクトだと思うよ?
stdObjectオブジェクトなんだよ。はあ?
ここはどう考えても、Event_modelオブジェクトを返してほしい。ORMなんだから
なぜなら、Event_modelクラスにいろいろユーティリティメソッドを書いておいて、それを使いたいんだよ。なんでできない?
→重くなるからですね。わかります
- JOINが貧弱
JOINしたら、フィールドがすべて結合されたフラットな状態で帰ってくるので、同じ名前のフィールドがあれば上書きされてしまう?
symfonyでは Hoge->getPage()->field のように、JOINしたテーブル(page)を特別なメソッドで呼べるので見分けがついた
- テンプレート内で、外部キーをによるデータ取得ができない?
Bookmark->getEntry()->getAccount()->getName() みたいなやーつ
最初からgetName()のところまで取得しておく必要がある
- テーブル名に hoge as h などのような名前を付けられない?
テンプレート回り
Globalテンプレートがないので、自分でそういう動作を書かなければならない
すべてのアクションで同じテンプレートを呼び、その中で、個々のテンプレートをインクルードする形で
なのでslotとかが使えないので、一番下のテンプレートからグローバルテンプレートの値などを変更する手段がない
- 例えばhead内の
- 例えばJSコードを、HTML閉じる直前に出力したい
XSS周り
とりあえずクソすぎる
- その1
$config['global_xss_filtering'] = TRUE;
という設定でフォームに
<script>alert("aaaa")<script>
と入力すると
[removed]alert("aaaa")[removed]
という感じに変換されてactionに渡ってくる
そんなことしてくれなくても、テンプレート側でhtmlspecialcharを通してくれればいいんです
なので私はglobal_xss_filteringを切っている
$config['global_xss_filtering'] = FALSE;
イカンイカン。ユーザーに切られる警報装置ってどうなのよ。ヒューマンエラーの温床じゃね?
- その2
viewの中でecho してるすべての箇所を自動的にhtmlspecialchars()してくれる機能はねーのかよ
system/helpers/form_helper.php
に自前の関数
_h()
を実装して使うことにした
使いどころは
<?php echo _h( $event->title ); ?>
また、フォームの値を取得するヘルパであるset_valueは、中でいったんエスケープ処理をしているのだが、バグがあり、2度同じform名でフォームヘルパを呼ぶと、2度目以降エスケープされていないすっごいおっきいすごいXSS脆弱性がある。
参考
というわけでこの項の結論
- viewでフォームに値をセットする場合は set_value()を使う (ただしパッチ適用してね)
- 単に値を echoする場合は _h()を使う
- 日本語メール(ISO-2022-JP)が文字化けを起こす
コアのstripslashes()が悪い。数行書き換えるだけで動きますが、、
日本語パック使えばパッチあたってるよ!
でも、野良パッケージじゃなくて公式で対応してくれんかな〜
ドキュメント回り
割と知りたいことが載ってない。(一通りそろってはいるが)
- APIリファレンスがない?
set_radioってどのライブラリロードすればいいんだっけな?とか、関数/メソッド名から逆引き出来ろよ!くそが
- チュートリアル系がない。
ログ回り
これはsymfony1.4くらいまででも言えるけど、貧弱すぎ
ciはさらに貧弱すぎ
ログレベルも少ないし
ファイルサイズローテートできないし
(日付またぎでファイルが作られる)
ファイルに書き込むログしかないし
Zend_log使いたい
テスト
テストが貧弱
ブラウザ関連のテストが一切できない
結合テストできなくね?w
unit testは入ってる
テストをコントローラ経由で起動しなきゃならん
→そのアクション放置しておいたらそれがセキュリティホールとか重大なパフォーマンス低下があり得る)につながるんじゃないの?そんなテストで大丈夫か?
テスト書くところおかしくない?コントローラーに書いていいの?
Validater
どうも動きがあやしいというか、少し空気を読みすぎる
たとえばルールにrequiredを追加しなかったら、フォーム入力がない場合そのほかのルールをすっ飛ばしたり
これを回避するにはcallback_なメソッドを指定してやればいいが、
なぜかバグがあり、
callback_hoge[message]
というルールのmessage部分が無視されてしまう
バグを直す
system\libraries\Form_validation.php
function _execute($row, $rules, $postdata = NULL, $cycles = 0) { .. //if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match)) if (preg_match("/(callback_[^ ]+)/", implode(' ', $rules), $match))
アクセス制御をコントロールする機能がない
管理者向けのページとかそういうのどーすんのさ?
InputクラスにHTTPメソッドを判定するメソッドが付いていない
以下のようなコードで判定したいんだけど出来ない
if( $this->input->is_post() ) { var_dump("post"); }
なので以下のようなメソッドを追加します
system\core\Input.php
function is_post() { return $_SERVER['REQUEST_METHOD'] == "POST"; }
こういうことはユーザー自身でやってねってのが設計ポリシーなんだろうな
CI_modelをインスタンス化すると、staticプロパティにアクセスできない
class Event_model extends CI_Model { var $ipaddr = ''; const STATUS_FLAG_APPLIED = 1; // アクセスできない private static $HOGE = null; // アクセスできない }
というクラスがあって
$this->load->model( 'Event_model', '', TRUE ); var_dump( $this->Event_model );
実行結果
object(Event_model)[20]
public 'ipaddr' => string '' (length=0)
メソッド経由でとれってこと?
なのでマジックメソッドを付けといてやる
class Event_model extends CI_Model { //const STATUS_FLAG_APPLIED = 123; // これはアクセスできない。無理です private static $HOGE = 123; //private staticな値を参照するメソッド //get + static変数名でアクセス //例: getHOGE() public function __call($name,$parameters) { $method_prefix = "get"; if( strpos( $name, $method_prefix ) ) { $name = substr( $name, strlen( $method_prefix) ); return self::$$name; } thorw new Exception("unknown class property:" $name); } }
実行コード
$this->load->model( 'Event_model', '', TRUE ); echo $this->Event_model->getHOGE(); // 123 echo $this->Event_model->getPAGE(); // throws exceptin
どちらともとれる特徴
良い点とも、悪い点とも考えられる特徴
Debugツールバーがない
代わりにvar_dumpで結構代用できるし問題ない
その分軽いしね。
var_dump()デバッグ最高です。
_ ∩ ( ゚∀゚)彡 var_dump()! var_dump()! ⊂彡
でも、symfonyのデバッグツールバーのSQLログは最高だったな
_ ∩ ( ゚∀゚)彡 XYZ_Modelクラスのインスタンスをvar_dump()! var_dump()! ⊂彡
symfonyのデバッグツールバーは入ってるライブラリとかもわかるじゃん
各スタックごとに時間も出るし
ぐぬぬ…
※追記 2011/07/19
コメント欄でid:Kenji_sさんにProfilerを教えていただきました。ありがとうございます!使い方は記事の末尾に記載
symfonyほどではないにしても、いいログ取れます!
_ ∩ ( ゚∀゚)彡 Profiler! Profiler! ⊂彡
全体
印象に過ぎないかもしれないけど、割とユーザーが好きなように書ける(レールが敷かれていない感じ)ので、複数人で開発に向いてないかも?って気がするね
たとえばcontrollerでエラーがあった場合はどのエラーテンプレートを自前で呼ぶかというのはユーザーが決める。っていうかsfもそうかもw
まとめ
CIは軽い
すこしバグってる/機能不足なので、最初にモンキーパッチあてる
5.1なら迷わず使え
中〜大規模以上にはお勧めできない
CodeIgnierを使ってみて
世間ではなんですか、最後のRubyKaigiとか、サッカー女子で盛り上がってるんですか?
まあPHPerな私には関係ないですので、空気も読まず、CodeIgniterの話しまーす。
最近CodeIgniterを使ってとあるプロジェクトの仕事をしたので、メモがてら記事にしてみますよっと。
■
[iPhone][app]野焼き
初アプリがiTunesストアに並びました。
http://itunes.apple.com/jp/app/id381240602?mt=8
英語ページでは早速評価4をいただけたようです。
http://itunes.apple.com/en/app/id381240602?mt=8
ちょっとしたパズルゲームです。
無料なんでぜひやってみてください。
非常に重要な事実
5月である。
5月といえば、"机に向かいたくない","仕事に行きたくない","人に会いたくない","そもそも朝起きたくない"で知られる恐ろしい病、五月病が流行る季節である。
しかし我々の調査の結果以下の恐るべき事実が判明した。
まず、日本人男性の平均寿命(約79歳とする)を1年の長さに置き換えると
1か月は 79÷12 ≒ 6.6 であるため、以下の表を得る。
1月 0歳 〜 6.6歳 2月 6.6歳 〜 13.2歳 3月 13.2歳 〜 19.8歳 4月 19.8歳 〜 26.4歳 5月 26.4歳 〜 33歳 6月 33歳 〜 39.6歳 7月 39.6歳 〜 46.2歳 8月 46.2歳 〜 52.8歳 9月 52.8歳 〜 59.4歳 10月 59.4歳 〜 66歳 11月 66歳 〜 72.6歳 12月 72.6歳 〜 79歳
ここでわれわれが注目すべき値は以下である。
5月 26.4歳 〜 33歳
人生を1年に例えた時の5月に当たるのは26歳から33歳という事実である。
つまり、 26歳から33歳の人の5月は二重の意味で5月病である。
重要なことなのでもう一度言う。
26歳から33歳の人の5月はスーパー5月病。
しかも26歳は後厄にあたるので三重苦。