FuelPHP
認証 その4 SimpleAuthユーザ管理画面を整理
前の記事でデータ操作を一通りできるようになったので、ここで表示まわりを少し整理しましょう。
一覧のパスワード、ログインハッシュ、Profile fieldsを削除します。また、一覧からのViewもとくに必要ないので削除します。
fuel/app/views/admin/user/index.phpから該当部分を削除します。変更後のコードは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<h2>Listing Users</h2> <br> <?php if ($users): ?> <table> <thead> <tr> <th>Username</th> <th>Group</th> <th>Email</th> <th>Last login</th> <th></th> </tr> </thead> <tbody> <?php foreach ($users as $user): ?> <tr> <td><?php echo $user->username; ?></td> <td><?php echo $user->group; ?></td> <td><?php echo $user->email; ?></td> <td><?php echo strftime('%G %h %d %R:%S', $user->last_login); ?></td> <td> <?php echo Html::anchor('admin/user/edit/'.$user->id, 'Edit'); ?> | <?php echo Html::anchor('admin/user/delete/'.$user->id, 'Delete', array('onclick' => "return confirm('Are you sure?')")); ?> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <p>No Users.</p> <?php endif; ?><p> <?php echo Html::anchor('admin/user/create', 'Add new User', array('class' => 'btn btn-success')); ?> </p> |
Last loginはタイムスタンプを日付+時刻の形式で表示するようにしました。
表示してみましょう。
だいぶすっきりしました。
FuelPHP SimpleAuthをいじっていたらブラウザが310リダイレクトループエラーに
何かの拍子に、SimpleAuthがリダイレクトエラーでログインできなくなってしまった。
閲覧履歴を削除してもダメ。
一旦データベースのusersテーブルとmigrationsテーブルを削除して$ oil r migrate
したら直った。
詳しく調べていないので原因は判りません。
チュートリアル 画像認証
次 未定
おはようございます。給料日前はいつもぎりぎり生活のゆうきんです。
captcha(キャプチャ) というものはご存知でしょうか。
よくユーザ登録などの際に、画像中の文字を入力して下さい とか言ってくるあれです。
主にスクリプトやBOT対策のために使用されます。
だって、機械がルーチンワークで登録繰り返したらあっという間にDBいっぱいになっちゃうじゃないですか。
そこで、画像認証は必要なものなのです。
FuelPHP で使える画像認証用のライブラリが公開されているので、簡単に実装できます。やったね!
導入
https://github.com/carlcraig/fuel-captcha から zip ファイルを落としてきて解凍して下さい。
解凍して出来たフォルダを captcha にリネームして、 fuel/package/ に突っ込んで下さい。
これで終わりです。
config.php の編集
app/config/config.php に以下の項目を追加して下さい。
1 2 3 |
'packages' => array( 'captcha', ), |
既に packages がある場合は、 captcha を追加するだけでいいです。
captcha の config を編集
ここからsimplecaptcha を使うか、recaptcha を使うかで書き方が違います。
simplecaptcha を使う場合は以下のようにします。
1 2 3 4 5 6 7 8 9 10 |
/** Default Driver * * (string) * * The name of the default driver to load * * The default driver will be called when you forge without giving the driver * e.g. Captcha::forge(); */ 'driver_default' => 'simplecaptcha', |
逆に recaptcha を使いたい場合は、 simplecaptcha と書かれている部分を recaptcha にして下さい。
この設定は、デフォルトでどっちを使うかの設定なので、特にやる必要はないですが、多く使う方を指定しておくと楽になります。
simplecaptcha を使用する
controller フォルダに simplecaptcha.php という名のファイルを作り以下の通りにして下さい。
1 2 3 4 5 6 7 8 9 |
<?php class Controller_Simplecaptcha extends Controller_Rest { public function get_image() { return Captcha::forge('simplecaptcha')->image(); } } |
captcha 用の画像を生成して、返す関数を用意しています。
なお、forge に ‘simplecaptcha’ を渡していますが、何も書かなければデフォルトの方が渡されるようになっています。
が、このファイルでは絶対に省略しないで下さい。
ファイルを作ったら、 fuel/package/captcha/config/simplecaptcha.php を以下の様に編集して下さい。
1 2 3 4 5 6 7 8 |
/** Captcha Route * * (string) * * The route to the captcha image * e.g. Uri::create('captcha/index') */ 'captcha_route' => Uri::create('simplecaptcha/image'), |
特にいじってなければ58行目にあると思います。
これは、captcha 用の画像はどれを使うのかという指定をしています。
この場合は先程の get_image 関数を指定いるわけです。
肝心の使い方は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Captchaテスト</title> </head> <body> {Form::open('captchatest/confirm')} {Captcha::forge('simplecaptcha')->html()} </p> {Form::submit('submit','送信')} </p> {Form::close()} </body> </html> |
これがビューです。名前は captchatest.smarty としてます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php class Controller_Captchatest extends Controller { /** * アクションインデックス */ public function action_index() { return View_Smarty::forge('captchatest'); } /** * 認証に成功したか確認 */ public function action_confirm() { if( Captcha::forge('simplecaptcha')->check() ) { return '認証に成功'; } else { return View_Smarty::forge('captchatest'); } } } |
で、これがコントローラです。captchatest.php としてます。
Captcha::forge(‘simplecaptcha’)->html() で入力フォーム付きの HMLT を生成してくれます。
んでもって Captcha::forege(‘simplecaptcha’)->check() で入力した文字列が正しいかチェックをかけてます。
recaptcha を使う
私はこちらを使うのをオススメします。セキュリティの問題で・・・。
recaptcha とはgoogleさんが提供している captcha です。
まず、http://www.google.com/recaptcha にアクセスし、USE reCAPTCHA YOUR SITE をクリックして下さい。
そしたら SIGN UP をクリック、出てきた画面で recaptcha を使用したい画面のURLを入力しって、Create Key ボタンを押して下さい。
※Enable this key on all domains (global key) にチェックを付けるかドウかはTipsをよく読んでください。
ボタンを押したあとに出てきた画面で、 Public Key と Private Key をメモしておきましょう。
fuel/package/captcha/config/recaptcha.php に同じ項目があるので、メモした内容をペーストして下さい。
で、肝心の使い方なんですが、ビューとコントローラーは simplecapthca とまったく同じです。
forge 関数に渡す文字列を simplecaptcha から recaptcha に変えるだけです。
もし、デフォルトを simplecatpcha にしてて、 forge 関数ではなくも渡してないなら。デフォルトを recaptcha に変えるだけで終わりです。
以上で、画像認証についての説明を終わります。
では、おやすみなさい。・
チュートリアル セッション
前 チュートリアル 複数のアプリケーションをフォルダごとにわける
おはようございます。何も買わないのに通販番組を見るのが好きなゆうきんです。
・・・セッション・・・。この単語を聞いただけで拒否反応が出るあなた。
そんなあなたに朗報です。
FuelPHP なら、なんと!簡単にセッション管理できるんです! オーディエンス<ナンダッテー!
セッション開始
1 |
Session::create(); |
これでセッションを開始できます。
古いセッションがある場合は破棄します。
セッションに値をセット
1 2 3 4 5 |
Session::set( 'userdata', array( 'id' => 'yukin', 'name' => 'ゆうきん', 'age' => '24', 'sex' => 'male' ) ); |
多分見ただけでわかりますよね。
第1引数でセッション名、第2引数で値を渡してあげるだけです。
サンプルのように連想配列も渡すことができます。
セッションから値の取得
1 |
Session::get('userdata'); |
これで、値の取得ができます。
格納したときと同じ型で返ってくるので、この場合は連想配列です。
セッションから値の削除
1 |
Session::delete('userdata'); |
セッションから値を破棄するだけです。
セッション自体が破棄されるわけではありません。
セッションの破棄
1 |
Session::destroy(); |
これで、セッションを破棄します。
セッションにセットした値も全て破棄されます。
セッションにフラッシュ変数をセット
1 |
Session::set_flash('flash', 'mabusii'); |
構成によってページのリクエストや変数の取得が行われたら、値が破棄される変数をセットします。
使い方は普通に値をセットするときと同じです。
セッションからフラッシュ変数を取得
1 |
Session::get_flash('flash'); |
フラッシュ変数を取得すると、フラッシュ変数が削除されます。
フラッシュ変数の寿命を延ばす
1 |
Session::keep_flash('flash'); |
次の消されるタイミングまで、寿命を延ばすことができます。
セッションからフラッシュ変数を削除
1 |
Session::delete_flash('flash'); |
削除したい場合これで。
基本的には、これだけでセッション管理できます。
他にも数個、関数があるのですが、使い方がわからないため説明できません。ごめんなさい。
でも、上記の関数だけで良いと思います。
以上でセッションの説明を終わります。
ちなみに、デフォルトではセッションはクッキーに保存されますが、ファイルやDBに保存することも可能らしいです。
今のところ必要性を感じないので、使うときが来たら説明します。
では、おやすみなさい。
FuelPHP tips Fuelのセキュリティ機能2
・出力フィルタ
・URIフィルタ
・SQL文の自動エスケープ
・入力フィルタ
・XSSフィルタ
・バリデーション(検証)
・CSRF保護
はい、Fuelのセキュリティ機能1で説明できなかった残り4つ行きます。
微妙に順番が変わってるのは気にしない。
1. 入力フィルタ
入力フィルタとは、スーパーグローバル変数の内
$_POST
$_GET
$_COOKIE
この3つを処理する機能です。
フォームからの入力やクッキーの値などに独自のフィルタをかける事が可能です。
デフォルトではなにも設定されておらず全く機能しません。
1 2 3 4 5 6 |
/** * Security settings */ 'security' => array( 'input_filter' => array(), ) |
ほら、coreの方のconfig見ても何もないでしょ?array()の中が空でしょ?
この中には自分で作った関数を入れる事ができます。(使う時はcoreじゃなくappの方を編集しましょう。)
例えば、出力フィルタやURIフィルタで使用されている「htmlentities」を指定できたりします。(公式ドキュメント引用)
このブログの住人が参考にしている書籍では文字エンコーディングのフィルタを用意していましたね。
指定の仕方は例えば次のようになります。
1 2 3 4 5 6 |
/** * Security settings */ 'security' => array( 'input_filter' => array('Ore::rule'), ) |
Oreクラスのruleメソッドを指定してます。
でもって次がそのクラスです。配置は app/class/ore.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php class Ore { public static function rule($value) { /*ここでフィルタのルールを書いてやるといいと思うんだ*/ /*例えば、$valueの文字エンコードを調べたり*/ /*改行やタブはいいとしても、Delete等の普通は入力できない制御文字のチェックをするといいです*/ $foo = rand(); if( $foo % 100 != 0) return $value; else die('素晴らしく運がないなぁ君はぁ'); } } |
ランダムで100分の1の確率で処理を受け付ける的な感じになってます。
サンプルです。あくまでサンプルです。
普通だったら文字エンコーディングのチェックとかすると思います。
なぜこの入力フィルタがデフォルトで有効になってないかというと、公式ドキュメントが
「そんなことより出力フィルタが先だ」って言ってた気がする。
最終的に出力フィルタで全部エスケープするよ。って事だろうか。
2. XSSフィルタ
さぁ、なんて読むだろうか。
クロスサイトスクリプティングと読むらしい。
ちなみに、Fuelのセキュリティ機能1の出力フィルタでやった危ないサンプルがあったけど、あれがXSSです。
出力フィルタで使用しているhtmlentitiesだけでは対処が難しい場合、
Securityクラスが用意している「xss_clean()」っていう関数を使えばいいらしいです。
htmLawedというライブラリを使用してるらしいです。
なんでもかんでもタグ文字とかをエスケープするのではなく、危険なコードを安全なものに直したりするらしいんですがショウジキヨクワカラナイシシュツリョクフィルタニタヨリッキリナノデパスデオネガイシマス。
一応サンプル(Viewは割愛)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php class Controller_Test extends Controller { public function action_index() { //以下サンプル3つ。 $data['a'] ='<SCRIPT>alert("(・ω・)")</SCRIPT>'; $data['a'] = Security::xss_clean($data['a']); $data['b'] ='<div>これは大丈夫</div>'; $data['b'] = Security::xss_clean($data['b']); $data['c'] ='<A HREF="javascript:document.location='http://www.google.com/'">XSS</A>'; $data['c'] = Security::xss_clean($data['c']); //出力フィルタをfalseにしておこう return View::forge('sec_test_view.smarty',$data,false); } } |
各3つのサンプルの2行目、xss_clean()の行をコメントアウトしたりして動作を確認できます。
1つ目はスクリプト実行によりアラートが出てきます。
フィルタに通すと「<SCRIPT></SCRIPT>」を取り除いてくれます。
2つ目はそのまま表示されます。全てのタグを取り除くわけではないようです。
3つ目はフィルタを通すと「javascript」の前に「deny:」を付けて無効化(?)してくれたりします。
「<A HREF=・・・」は「<a href=・・・」とか小文字に直してくれて残してくれます。
FuelPHPの公式ドキュメントには、「パフォーマンスの理由」により個別の変数に対して使用することを推奨しているようです。
それよりもなによりも出力フィルタを優先すべきだと言ってるので、出力フィルタをオフにしないように心がけたい。
3. バリデーション (検証)
バリデーションは、あらかじめ作って置いたルールに基づき変数を検証することです。
「ユーザー名は○文字以下まで」
「パスワードは○文字以上○文字以下」
などです。
使い方はこちらを参照してください。
→チュートリアル フォームの作成とDBの使い方
4.CSRF保護
このCSRF、クロスサイトリクエストフォージェリって言います。
XSSと似てますが違います。
CSRFの恐怖はこちらを参照→Wikipedia:クロスサイトリクエストフォージェリ
このCSRFの対策とし
1.ページ作成時に次の2のためのトークンを生成
2.form送信の時にCSRF対策用のトークン(hiddenでおk)を一緒に送信
3.送られてきたトークンをチェックし有効ならそのまま、無効ならアウトォォォ!
では、1のコードから2の送信
1 2 3 4 5 |
<form 〜〜割愛〜〜> <?php echo Form::hidden(Config::get('security.csrf_token_key'), Security::fetch_token()); ?> <input 割愛〜〜> </form> |
Viewで作ってます。面倒なんで。
Config::get(〜〜〜)で「security」の「csrf_token_key」の値を取得します。
デフォルトでは「fuel_csrf_token」という文字列になってます。
Security::fetch_token()でトークンを生成しています。
そして、各フォームの値と一緒に送信します。
そして、次の3のコードでチェック
1 2 3 |
if( Security::check_token() == false ) echo "トークンが一致しません。"; else "トークンが一致。有効なリクエストだぁ"; |
メッセージが長くてうざいけど、チェックする処理自体は非常に短いです。
このCSRF保護機能ですが、中身の実装がやや怪しいらしいです(暗号論的疑似乱数生成とか)
バージョンアップで強化されていくことを願いましょう。
これで一応終了。
もうセキュリティって言葉ってなんだ?
っていうゲシュタルト崩壊が起きてきそうです。
FuelPHP tips Fuelのセキュリティ機能1
そろそろFuelPHPにも慣れてきたんじゃないっすかぁ?
じゃぁちょっとセキュリティとかっていう面倒くさいものをやりましょうか。
大丈夫大丈夫!FuelPHP優秀だから大丈夫だって!諦めんなよ!あっつくなれよ!
FuelPHPにあるセキュリティ:Security
FuelPHPには最初から以下のセキュリティ機能が入ってたりします。
・出力フィルタ
・URIフィルタ
・SQL文の自動エスケープ
・入力フィルタ
・CSRF保護
・XSSフィルタ
・バリデーション(検証)
上3つの太字で表したものはデフォルトで有効になっています。
多分意識してないと思いますが、コントローラーからビューに変数を渡すときに出力フィルタが働いています。
1つ1つ見ていきましょうか。
1. 出力フィルタ
Securityクラスにhtmlentities()って関数があって、出力フィルタとURIフィルタで使ったりしてます。
この関数は文字列をエスケープして返します。
なんのこっちゃって言う人のために、以下のサンプルをどうぞ。(Viewは適当に作ってね★)
1 2 3 4 5 6 7 8 9 10 |
/*action_index以外省略*/ public function action_index() { //例えばユーザが入力した値を表示するものとする //↓Inputクラスで取得したりしたものとする $data = "<script>alert('(・ω・)')</script>"; //↓第3引数でfalseを指定すると出力フィルタがオフになる return View::forge('test_view',array('data'=>$data) ,false); } |
このサンプルのページにいくと、javascriptのalertが出てきて「(・ω・)」とかふざけた顔文字が出てくる。
顔文字だったらカワイイだけで済むが(かわいいよねこれ(・ω・))
これをユーザーが自由に入力できるとしたら攻撃されまくりである。
例えばユーザー名に
「<script>有害コード</script>」
って入力されたりするかもね。
なんかヤバい感じを味わったら、次は
1 2 3 |
//↓第3引数でfalseを指定すると出力フィルタがオフになる //return View::forge('test_view',array('data'=>$data) ,false); return View::forge('test_view',array('data'=>$data) , true); |
さっき作ったサンプルの上の部分を変更してみよう。
第3引数をfalseからtrueにするのだ。
すると、今度はサンプルで書いたScriptのコードがそのまま出てきたと思う。
ブラウザの機能の「ソースコードを表示」で見てみると、不思議な文字の羅列があったりする。
これは出力フィルタがhtmlタグをそのまんまじゃなく、それを示す違う記号にエスケープしたのです。
ちなみに、第3引数を省略すると、デフォルトでtrue設定になるので安心だ(・ω・)b
便利ですね。
2. URIフィルタ
URIフィルタとは。
まず以下のサンプルをどうぞ
1 2 3 4 5 6 7 8 9 10 11 12 |
class Controller_Test extends Controller { public function action_index($a='') { $data = "<>以外が入力された?"; if($a === Security::htmlentities("<>")) $data = "URIフィルタOn"; else if($a === "<>") $data = "URIフィルタおっふ"; return View::forge('test_view',array('data'=>$data)); } } |
これで 「http://~~~~~/test/index/<>」というURLにアクセスしましょう。(~~~の部分は当然各環境に合わせてください)
action_indexは本来指定しなくてもアクセス出来ますが、パラメーターを$aとして受けとるために記述が必要になってきます。
そしてパラメーターとして「<>」を渡しています。(あくまで例です。他にも試してみてください。)
サンプルでは、この「<>」をSecurityクラスのhtmlentitiesを使用して、URIフィルタが機能しているかどうかを判定しています。
まぁぶっちゃけ”URIフィルタおっふ”は表示される事はないだろう。。。or
オンとかオフとか書きましたが、このURIフィルタをオフにする方法がちょっと見つからなかったので設定はデフォルトのままで放置します。
3. SQL文の自動エスケープ
FuelPHPではSQLインジェクションに対する機能があります。
データベースを扱う部分で説明すると思いますが、クエリビルダーがその役目を負っています。
例えば「DB::select()->・・・」で始まる奴とかですね。
このクエリビルダー機能で発行されたクエリは全てエスケープ済みです。
DBクラスのクエリビルダーってすごく簡単でしかもセキュリティ対策もしてくれてるのです!
いやぁSQL文使わなくても(使えなくても)いいんですよー。
あとの4つはFuelのセキュリティ機能2で説明します。
チュートリアル 複数のアプリケーションをフォルダごとにわける
おはようございます。最近21時には寝て3時に起きる、超絶早寝早起き人間のゆうきんです。
FuelPHP にも大分、慣れたことだと思います。
今回は、複数のアプリケーションをフォルダごとにわける方法を説明します。
FuelPHP のモジュール機能を使っても、数が増えすぎると限界がきますからね。
やはり、プロジェクトはわけたいところです。
1.FuelPHPを配置する
これは、終わっていると思うので、配置してあるディレクトリだけ覚えておいて下さい。
今回は、 home/work/ に fuelphp という名前で配置してあるものとします。
2.アプリケーション用のフォルダを作成する
適当な場所にフォルダを作成して下さい。
今回は FuelPHP のフォルダと同じ階層に test というフォルダを作成するとします。
作成したらフォルダ内に FuelPHP から app フォルダ、 public フォルダ、 oil をコピーして下さい。
3.ファイルを書き換える
public/index.php と oil を書き換えます。
コアへのパスがデフォルトとは違うので、1で覚えておいたFuelPHPのパスに変更して下さい。
今回の場合は、以下のようになります。
public/index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Path to the application directory. */ define('APPPATH', realpath(__DIR__.'/../app/').DIRECTORY_SEPARATOR); /** * Path to the default packages directory. */ define('PKGPATH', realpath(__DIR__.'/../../fuelphp/fuel/packages/').DIRECTORY_SEPARATOR); /** * The path to the framework core. */ define('COREPATH', realpath(__DIR__.'/../../fuelphp/fuel/core/').DIRECTORY_SEPARATOR); |
oil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * Path to the application directory. */ define('APPPATH', realpath(__DIR__.'/app/').DIRECTORY_SEPARATOR); /** * Path to the default packages directory. */ define('PKGPATH', realpath(__DIR__.'/../fuelphp//fuel/packages/').DIRECTORY_SEPARATOR); /** * The path to the framework core. */ define('COREPATH', realpath(__DIR__.'/../fuelphp/fuel/core/').DIRECTORY_SEPARATOR); |
相対パスにしていますが、絶対パスでもいいです。
3.コマンド実行
配置した test フォルダに移動して、以下のコマンドを実行してください。
1 |
php oil refine install |
4.Apacheのドキュメントルートフォルダにアプリケーションフォルダのシンボリックリンクを貼り付ける
test/public のシンボリックリンクを、Apache の htdocs フォルダに作成してください。
これで、公開されます。
※本当は test フォルダのシンボリックリンクを作成するらしいのですが、ファルダ構成とか見られてしまう可能性があるので、test/public としてます。
アプリケーションを増やすたびに 2~4 を繰り返して下さい。
では、おやすみなさい。
認証 その3 SimpleAuthのユーザ管理画面を作る
前の記事では認証画面の表示とユーザ管理テーブルのスキャフォールドまでで、ユーザデータの操作はできませんでした。
スキャフォールドを元に、Authクラスでユーザデータを操作するようにコードを変更してみます。
新規追加のための変更
最初にビューを変更します。
last_login、login_hashは新規ユーザ追加や変更時に入力する必要はありませんから削除しましょう。
また、profile_fieldsもとりあえず必要ないので削除します。
fuel/app/views/admin/user/_form.phpから以下の部分を削除します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<div class="clearfix"> <?php echo Form::label('Last login', 'last_login'); ?> <div class="input"> <?php echo Form::input('last_login', Input::post('last_login', isset($user) ? $user->last_login : ''), array('class' => 'span4')); ?> </div> </div> <div class="clearfix"> <?php echo Form::label('Login hash', 'login_hash'); ?> <div class="input"> <?php echo Form::input('login_hash', Input::post('login_hash', isset($user) ? $user->login_hash : ''), array('class' => 'span4')); ?> </div> </div> <div class="clearfix"> <?php echo Form::label('Profile fields', 'profile_fields'); ?> <div class="input"> <?php echo Form::textarea('profile_fields', Input::post('profile_fields', isset($user) ? $user->profile_fields : ''), array('class' => 'span8', 'rows' => 8)); ?> </div> </div> |
グループは管理者をデフォルトにすることにします。
同じくfuel/app/views/admin/user/_form.phpから以下の部分を削除します。
1 2 3 4 5 6 7 8 |
<div class="clearfix"> <?php echo Form::label('Group', 'group'); ?> <div class="input"> <?php echo Form::input('group', Input::post('group', isset($user) ? $user->group : ''), array('class' => 'span4')); ?> </div> </div> |
次に、モデルのvalidateメソッドから上記で削除した項目の検証を削除します。
削除後、fuel/app/classes/model/user.phpのvalidateメソッドは以下のようになります。
1 2 3 4 5 6 7 8 9 |
public static function validate($factory) { $val = Validation::forge($factory); $val->add_field('username', 'Username', 'required|max_length[50]'); $val->add_field('password', 'Password', 'required|max_length[255]'); $val->add_field('email', 'Email', 'required|valid_email|max_length[255]'); return $val; } |
最後に、コントローラがusersテーブルにデータを追加している部分を、Auth::user_createを実行するように書き換えます。 fuel/app/classes/controller/admin/user.phpのaction_createメソッドから以下の記述を削除し、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$user = Model_User::forge(array( 'username' => Input::post('username'), 'password' => Input::post('password'), 'group' => Input::post('group'), 'email' => Input::post('email'), 'last_login' => Input::post('last_login'), 'login_hash' => Input::post('login_hash'), 'profile_fields' => Input::post('profile_fields'), )); if ($user and $user->save()) { Session::set_flash('success', e('Added user #'.$user->id.'.')); Response::redirect('admin/user'); } |
以下のコードで置き換えます。
1 2 3 4 5 6 7 8 9 10 |
$userid = Auth::create_user( Input::post('username'), Input::post('password'), Input::post('email'), 100 ); if ($userid > 0) { Session::set_flash('success', e('Added user #'.$userid.'.')); Response::redirect('admin/user'); } |
それでは早速Add new Userしてみましょう。
Username、Password、Emailを入力してSaveします。
ユーザfuelが追加されました。 ログアウトしてfuelでログインしてみます。
ログインできました。うまくいったようです。
編集のための変更
SimpleAuthはユーザ名の変更が出来ません。
また、パスワードを変更するには新しいパスワードと共に元のパスワードが必要な仕様になっています。
このため、新規追加の入力フォームは流用できませんから、新しく作ることにします。
変更する項目はメールアドレスとパスワードとし、メールアドレスは変更されていれば上書き、パスワードは古いパスワードと新しいパスワードの両方が入力されたとき変更するようにします。
fuel/app/views/admin/user/_form.phpをコピーして_form_edit.phpを作り、編集フォームのための変更を行います。
Usernameの部分を削除し、Old PasswordとNew Passwordを追加します。
パスワードの初期状態はどちらも空にしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php echo Form::open(); ?> <fieldset> <div class="clearfix"> <?php echo Form::label('Email', 'email'); ?> <div class="input"> <?php echo Form::input('email', Input::post('email', isset($user) ? $user->email : ''), array('class' => 'span4')); ?> </div> </div> <div class="clearfix"> <?php echo Form::label('Old Password', 'oldpassword'); ?> <div class="input"> <?php echo Form::input('oldpassword', '', array('class' => 'span4')); ?> </div> </div> <div class="clearfix"> <?php echo Form::label('New Password', 'newpassword'); ?> <div class="input"> <?php echo Form::input('newpassword', '', array('class' => 'span4')); ?> </div> </div> <div class="actions"> <?php echo Form::submit('submit', 'Save', array('class' => 'btn btn-primary')); ?> </div> </fieldset> <?php echo Form::close(); ?> |
fuel/app/views/admin/user/edit.phpで表示するフォームを_form_edit.phpに変更します。
1 2 3 4 5 6 7 |
<h2>Editing user <?php echo $user->username?></h2> <br> <?php echo render('admin/user/_form_edit'); ?> <p> <?php echo Html::anchor('admin/user/view/'.$user->id, 'View'); ?> | <?php echo Html::anchor('admin/user', 'Back'); ?></p> |
上記のコードは、見出しにユーザ名を表示するようにしています。
次に、モデルに入力の検証コードを追加します。
fuel/app/classes/model/user.phpにvalidate_editメソッドを追加します。
1 2 3 4 5 6 7 8 9 |
public static function validate_edit($factory) { $val = Validation::forge($factory); $val->add_field('oldpassword', 'Old Password', 'required|max_length[255]'); $val->add_field('newpassword', 'New Password', 'required|max_length[255]'); $val->add_field('email', 'Email', 'required|valid_email|max_length[255]'); return $val; } |
続いてコントローラを変更します。
fuel/app/classes/coltroller/user.phpのaction_editメソッドから以下の部分を削除します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$user->username = Input::post('username'); $user->password = Input::post('password'); $user->group = Input::post('group'); $user->email = Input::post('email'); $user->last_login = Input::post('last_login'); $user->login_hash = Input::post('login_hash'); $user->profile_fields = Input::post('profile_fields'); if ($user->save()) { Session::set_flash('success', e('Updated user #' . $id)); Response::redirect('admin/user'); } else { Session::set_flash('error', e('Could not update user #' . $id)); } |
削除した部分を以下のコードで置き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
$succeed = true; if ($user->email != Input::post('email')) { if (! Auth::update_user(array('email' => Input::post('email')), $user->username)) { $succeed = false; Session::set_flash('error', e('Could not update user's Email')); } } if ($succeed) { $oldpw = Input::post('oldpassword'); $newpw = Input::post('newpassword'); if ((!empty($oldpw)) && (!empty($newpw))) { if (! Auth::change_password($oldpw, $newpw, $user->username)) { $succeed = false; Session::set_flash('error', e('Could not update password for ' . $user->username)); } } } if ($succeed) { Session::set_flash('success', e('Updated user ' . $user->username)); Response::redirect('admin/user'); } else { $this->template->set_global('user', $user, false); } |
モデルの編集フォーム検証メソッドを呼び出している部分をvalidateからvalidate_editに変更します。
1 |
$val = Model_User::validate_edit('edit'); |
それでは動作を確認しましょう。 ユーザadminでログインします。
ユーザfuelをEditします。
まずはメールアドレスを変更してみます。
fuel@example.comをmail@example.comに変更します。
mail@example.comに変更されました。 次にパスワードを変更してみます。
一旦ログアウトして、ユーザfuelの新しいパスワードでログインします。
ログイン出来ました。
これでユーザのメールアドレスとパスワードの変更ができるようになりました。
削除のための変更
このままでもユーザの削除はできるのですが、せっかくなので削除もAuthクラスを使うように変更しましょう。
コントローラの削除処理を変更します。
fuel/app/classes/controller/admin/user.phpのaction_deleteメソッドの以下の部分を
1 |
$user->delete(); |
以下のコードで置き換えます。
1 |
Auth::delete_user($user->username); |
ユーザadminでログインしてユーザfuelをDeleteします。
確認ダイアログでOKをクリックします。
ユーザfuelが削除されました。
FuelPHP tips Numクラス
Uploadクラスとかで、なんかファイルサイズを指定したりする時どんな風に書いてますか?
$size = 1024*1024*8;
とか書いてませんか?
けどこの書き方、なんか数字の羅列で見づらいですね。
慣れてる人は8MBだ、ってすぐ出てくるかもしれませんね。
今回ご紹介する商品はこちら!
FuelPHPに最初から入っている!
Numクラス!
まだ、ちょっとバイトとかビットの数字に慣れてないです・・・
っていうそこのあなた!
これさえあればもう面倒くさい計算なんてしなくていい!
先ほどの例を上げると
$size = Num::bytes(‘8M’);
ほらスッキリ!
文字列を数値に自動で!変換してくれちゃったりするんですよ〜。
みなさん。驚くのはまだ早いですよ?
今回はこれにプラスして!逆変換機能もつけちゃいます!
echo Num::format_bytes(‘1024’); → 1kB
数値を入れればそれを文字列に直してくれます!
さらに!
echo Num::format_bytes(‘1124’,1); → 1.1kB
このように第2引数で小数点以下表示も可能!
けどやっぱり、みなさんが気になるのはお値段の方ですよね〜?
大丈夫です。このNumクラス、
数値→文字列機能
文字列→数値機能
2つセットで!15回払い対応の!0円!
はい、通販番組風の前置きが長くなりましたがNumクラスについて書きます。
Numクラス
数値に関する、型変換や出力を支援してくれます。
::bytes($size=0)
引数で指定した文字列を数値(float)で返します。実はintやfloatも引数として扱えます。
「K,M,G」や「MiB」なんていう特殊なものもあります。
::format_bytes($bytes=0 , [$decimals=0])
第1引数で指定した数値を文字列で返します。その際適切な接頭辞が付けられます。
第2引数は省略可。数値を指定すると小数点以下が表記されます。
例)
1 |
echo Num::format_bytes(1124,1); //→1.1kB |
::quantity($num, [$decimals=0])
第1引数で指定した数値を適切な接頭辞が付けた上で四捨五入します。(多分)
第2引数で数値を指定すると、どこで四捨五入するか小数点以下の場所を指定できます。
例)
1 |
echo Num::quantity('1624006',5); //→1.62401M |
::format($string = “” , $fomart = “” )
見づらいんで↑シングルクォーテーションをダブルクォーテーションにしました。
まずは例)
1 2 3 |
echo Num::format("1234567890" , " (000) - 000 - 0000"); //→ (123) - 456 - 7890 echo Num::format("1234567890" , "0!0#0$0%0&0'0(0)0~,.<>-^;:@[]日本語 0"); //→1!2#3$4%5&6'7(8)9~,.<>-^;:@[]日本語 0 |
なお第2引数の0の羅列は英数字ならなんでもいいらしい。逆を言うと英数字で分けたりとかできない。
日本語で分けれるのが笑える。
(“20120128″,”0000年00月00日”)で2012年01月28日とか出ちゃう。
追記:mask_stringで実験してからこっちも試したらなんかこうなった
1 |
echo Num::format("abcdefghij", "aaa-aaa-aaa-a"); //→abc-def-ghi-j |
::mask_string($string=”” , $format=”” , ignore=””)
例)
1 2 3 |
echo Num::mask_string("1111222233334444" , "**** **** **** 0000"); //→ **** **** **** 4444 echo Num::mask_string("111122223333" , "****-0000-****" , "-"); //→ ****-2222-**** echo Num::mask_string("aaabbbccceee" ,"*** aaa *** 000"); //→ *** bbb *** eee |
数値でも文字列でも可能のようです。
数値は「0」、文字列は「a」とかそういう指定は無いみたいです。
入力された英数字は、formatの英数字部分で表示され、それ以外ではその記号が出力されるみたいです。
ignoreで指定した記号はその際文字数としてカウントしないで無視されるようです。
なのでこんな例も可能
::mask_string(“abcdefghij”, “@@@-@@@-00@” ,”-“); → @@@-@@@-gh@@
お、クレジットカードとかに使えそうじゃん?とか思ったあなた。次の関数を見てくれ。
::mask_credit_card($string , $format=null)
クレジットカード用のmask_string関数が用意されてましたとさ。
例)
1 |
echo Num::mask_credit_card("1111222233334444"); //→ **** **** **** 4444 |
用意周到ですな・・・
基本「format()」を使えばなんとなる。
他にもちょくちょくありましたが、クレジット用みたいにformat()を使いやすくしたっていうものなんで省略します。
番外編:接頭辞
途中で1MiBとかいう特殊な表記が出てきましたが、これには深いわけが。
この接頭辞(k,M,G,Tとかね)って実は2を冪乗とする数値には使っちゃいけないって決まりがあったりする。
なのでコンピュータの世界では1MBって表記するのはダメだったりする。
さり気にキロだけ小文字なのはこれも決まりなのである。「大文字を使ってはいけない」らしい。
大文字にするとケルビンの方になるらしい。
コンピュータ専用の接頭辞があってそれを2進接頭辞と呼びます。
1kB→1KiB
1MB →1MiB・・・
という感じに「i」が付いちゃう。読み方は1KiB「1キビバイト」 1MiB「1メビバイト」という感じになります。
実際は、全然普及してないっぽいよね。Linuxだとけっこうお目にかかりますが。
エクサなんてエクスビってなるんだよ。(読みづらいのがさらに読みづらくなった)
ちなみに接頭辞には1da(デカ 10の1乗)とか不思議なものがある。
チュートリアル フォームの作成とDBの使い方
次 チュートリアル 複数のアプリケーションをフォルダごとにわける
おはようございます。いつまでも中二病を卒業できないゆうきんです。
今回はフォームの作成と、送信したデータをDBに書きこむということをやってみたいと思います。
現代のWebサイトでDBが絡まないということはとても少ないです。
というわけで、作って行きましょう。
あ、Smartyの使い方ってページ読みました?
今回からビューは全てSmartyを使って書いていくので、読んでおいて下さい。
ユーザに登録させる情報を決める
今回はユーザに ユーザ名、ユーザID、パスワード、e-mailアドレス、を登録させたいと思います。
ユーザ名は1〜8文字まで、ユーザIDとパスワードは半角英数字で4〜12文字という制約を設けます。
データベースの構造を考える
まず前提として文字列は全て utf8_general_ci を使います。
データベース名 test で、テーブル名は users としましょう。
プレイマリ は id というカラムで、オートインクリメントも有効にしておきます。
ame, userid, password, email というカラムも追加しましょう。
全部 text 形式でOKです。
画面遷移を決める
- 入力ページ
- 確認ページ
- 完了ページ
の3つを作成し、上から順に遷移していくものとします。
いろいろ決まった所で作っていきます。
こらから作るファイルはサブフォルダは作らず、controler, model, views のフォルダに配置するものとします。
入力ページの作成
こちらが、入力ページのソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ユーザ情報登録入力ページ</title> </head> <body> {if (isset($errors))} {$errors} {/if} {Form::open('input/confirm')} <p> {Form::label('ユーザ名', 'username')} {Form::input('username', Input::post('username'), ['required'=>'required'])}<br /> </p> <p> {Form::label('ユーザID', 'userid')} {Form::input('userid', Input::post('userid'), ['required'=>'required'])}<br /> </p> <p> {Form::label('パスワード', 'password')} {Form::password('password', Input::post('password'), ['required'=>'required'])}<br /> </p> <p> {Form::label('e-mail', 'email')} {Form::input('email', Input::post('email'), ['required'=>'required', 'email' => 'email'])}<br /> </p> <p> {Form::submit('submit','送信')} </p> {Form::close()} </body> </html> |
Formクラスの説明をします。FormクラスはHTMLタグを生成するクラスです。
ここで使っているものを順次説明します。
Form::open
form開始タグを生成します。
第1引数 実行された時の動作、又は、属性の連想配列(例:array(‘action’ => ‘example/index’, ‘method’ => ‘post’))
Form::close
form終了タグを生成します。
引数はありません。
Form::label
labelタグを生成します。
第1引数 ラベル名
第2引数 関連付けたい部品のID
Form::input
inputタグを生成します。
第1引数 任意のID
第2引数 初期データ
第3引数 タグの属性
Form::password
inputタグを生成します。
使い方は Form::input と同じです。
input タグの属性が password になります。
他にもラジオボタンを生成したり、テキストエリアを生成したりできる関数がありますが、割愛します。
input関数の第2引数で Input::post(‘username’) としていますが、これは username が post されていたら、それを初期値で表示するとういことです。
入力内容に誤りがあったとき、確認ページで修正を選択したとき、などに前回入力したデータを初期値で持たせるということをしたいので、このようになっています。
続いて、こちらがコントローラです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<?php use FuelCoreHttpNotFoundException; class Controller_Input extends Controller { /** * アクションインデックス */ public function action_index() { return View_Smarty::forge('input'); } /** * 確認ページ */ public function action_confirm() { // POST 以外のアクセスを禁止する if( Input::method() != 'POST' ) { throw new HttpNotFoundException; } // 入力チェック $val = $this->get_validation(); // 入力された値が正しかったか? if( $val->run() ) { // 正しい場合は確認ページに遷移 $data['input'] = $val->validated(); return View_Smarty::forge('confirm', $data); } else { // 正しくない倍位は入力ページに戻り、エラーを表示 $input_view = View_Smarty::forge('input'); $input_view->set_safe('errors', $val->show_errors()); return $input_view; } } /** * 入力検証 */ // 検証ルールの定義 public function get_validation() { $val = Validation::forge(); $val->add('username', 'ユーザ名') ->add_rule('required') ->add_rule('min_length', 1) ->add_rule('max_length', 8); $val->add('userid', 'ユーザID') ->add_rule('required') ->add_rule('min_length', 4) ->add_rule('max_length', 12); $val->add('password', 'パスワード') ->add_rule('required') ->add_rule('min_length', 4) ->add_rule('max_length', 12); $val->add('email', 'e-mail') ->add_rule('required') ->add_rule('valid_email'); return $val; } } |
action_index で input ビューを表示。
action_confirm で データが post されていたら、データチェックして confirm ビューを表示。
データに誤りがあれば。エラー内容を input ビューに渡して再表示。
action_confirm で行うデータチェックのルールは get_validation で定義しています。
validation とは?
form の input で入力するデータにルール付を行うことです。
例えばこの文字は30文字しか入力できないとか、4文字以上でなければ認めない、などです。
validation の使い方
Validation::forge メソッドでクラスのインスタンスを生成し、それにルールを加えていきます。
add メソッドで ルール付けを行う input の ID と、任意のフィールド名を指定します。
フィールド名は混乱を避けるために、 input ニ関連付けた label と同じ名前が好ましいです。
そして、 add_rule メソッドでルールを追加します。
ルールの内容はソースを見ていただければわかると思います。
※ メモ:私が悩んだことなのですが trim とは、文字列前後の空白を取り除くphpの標準関数のことです
ルール付けが終わったら run メソッドでデータチェックを行えます。
正しければ true 、間違っていれば false が返ってきます。
validated メソッドでチェックをかけた後のデータを配列で取得することができるので、 confirm ビューニ渡しています。
show_errors メソッドでエラー内容を表示できるので、set_safe メソッドで input ビューに渡しています。
set_safe を使うとエスケープされずに文字列をビューに渡すことが可能です。
確認ページの作成
続いて、 確認ページのソースです。
まずはビューです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ユーザ情報登録確認ページ</title> </head> <body> <p> ユーザ名:{$input['username']} </p> <p> ユーザID:{$input['userid']} </p> <p> パスワード:{$input['password']} </p> <p> e-mail:{$input['email']} </p> {Form::open('input/')} {Form::hidden('username', $input['username'])} {Form::hidden('userid', $input['userid'])} {Form::hidden('password', $input['password'])} {Form::hidden('email', $input['email'])} <p> {Form::submit('submit','修正')} </p> {Form::close()} {Form::open('input/conplete')} {Form::hidden(Config::get('security.csrf_token_key'), Security::fetch_token())} {Form::hidden('username', $input['username'])} {Form::hidden('userid', $input['userid'])} {Form::hidden('password', $input['password'])} {Form::hidden('email', $input['email'])} <p> {Form::submit('submit','登録')} </p> {Form::close()} </body> </html> |
確認用に受け取ったデータを表示しています。
そして、修正ボタンを押したら入力ページに現在のデータを post して遷移します。
登録ボタンを押したら確認ページに現在のデータを post して遷移します。
そのためフォームタグは2つ用意しています。
以下が、見たことないと思われる関数の使い方と説明です。
Form::hidden
実際には表示されない input タグを生成します。
第1引数 任意のID
第2引数 送信したいデータ
Config::get
config.php の内容を取得できます。
第1引数 取得したい項目名
第2引数 指定した項目名がなかった場合に、代入するデータ
今回は CSRF 対策のため、security 内の csrf_token_key を取得しています。
CSRF 対策用のトークンを post する際の ID は必ず security.csrf_token_key を指定するようにして下さい。
Security::fetch_token
CSRF 対策のトークンを発行します。
引数なし
次に書くコントローラのソースで説明しますが、このトークンをチェックすることにより、CSRF 対策を行うことが出来ます。
続いてコントローラです。
入力ページで作成した Controller_Input クラスに追加して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * 完了ページ */ public function action_conplete() { if( Input::method() != 'POST' ) { throw new HttpNotFoundException; } // CSRF対策 if( !Security::check_token() ) { return 'ページ遷移が正しくありません。'; } // DBに書きこむ Model_Input::push_user( Input::post('username'), Input::post('userid'), Input::post('password'), Input::post('email') ); // 完了ページ表示 return View_Smarty::forge('complete'); } |
Security::check_token
引数なし
CSRF 対策トークンをチェックします。
まず post されているかチェック、CSRF トークンチェックを行い、どちらも認証できれば、データベース操作に移ります。
Model_Input::push_user は独自に作ったクラスと関数です。
以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php class Model_Input extends Model { public static function push_user( $username, $userid, $password, $email ) { $result = DB::insert('users')->set(array( 'username' => $username, 'userid' => $userid, 'password' => $password, 'email' => $email, ) )->execute(); return $result; } } |
確認ページの作成
「登録完了しました」と表示しているだけなので、特に記載しません。
以上で、フォームの作り方の説明を終わります。
大分省きました。
本当は、ユーザ名、ユーザIDとe-mailアドレスの2重チェックとか、データベースに書き込み成功したかのチェックかけたりとかしなければなりません。
ここまで、少し調べれば出来ることだと思うので、気が向いたら書くかも知れないです。
以下、どうでもいい話
これ作ってる最中に、同僚に「e-mailアドレスの変数名って emal じゃなくて email_address じゃないの?」って言われました。
そこで「email_address だと長いから address は?」と返したんですが、「address だと住所じゃん(笑)」と言われてしまいました。
確かに email だと意味が違いますし、 address だと住所になっちゃうんで、 email_addressが正解ですね(笑)
でも、なんか間違いを認めると負けた気がするので、私はずっと emal で通しますよ!
同じ理由で職場のエアコンで暖房を付けることを、ストーブを付けると言いはってます。
では、おやすみなさい。