特別付録

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&#40;"aaaa"&#41;[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()を使う
コマンドライン

コマンドラインという機能自体無い
(テストどーすんのさ!)


Mail
  • 日本語メール(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なら迷わず使え
中〜大規模以上にはお勧めできない

導入

CIってなに?

特徴は?

  • MVC
  • とにかく軽いってこと
  • 覚えることが少ないってこと
  • PHP5.1で使えるってこと
    • CentOS5.6って普通にyum install php したら5.1.6入るんですけど。symfony1.4使えないじゃないですか。バカなの?死ぬの?→CI有るヨ!
    • 自分でPHPインストールできない場合とかにいいんじゃね?(今回のプロジェクトがこれでした)
    • PHP4何ですけど、使えますかね?
      • 無理。
  • で、どこに使うよ?
    • 小規模Webアプリケーション
    • 決して中規模以上に使えないわけじゃないのかなぁ…
    • けど、DB周りとか貧弱なんで、それを補強するコードを書く気ならいいんじゃね
    • あと、ドキュメント少ないので…あとは分るな?

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歳は後厄にあたるので三重苦。

SJC-P受けてきた

プログラマーになって早数年。
いままでC++とかPHPとか使ってて、Java?何それ食えるの?
遅いし、遅いし、遅いじゃん。とかって思ってたんだけど、
GAE/JとかAndroidとかJRubyとか、超盛り上がってんじゃん。
Javaのくせに。
キーーーーーッ。生意気!



そんなわけで、避けて通れなくなったので、
食わず嫌いはやめてJavaでもラーニングしてやんよ!
ってことでSJC-Pの試験受けてきた。



結果はぎりぎり合格。
フロー制御が18%とかありえんだろう。
どこで間違えたんだよ<俺。



まあ、いいや。そんなんコンパイラが教えてくれるし。