[cakePHP]findの戻り値からモデル名を除去する

cakephpでModel->find(‘all’); をつかうと、検索結果を取得することができる。
この結果を

$this->set('results',$this->Model->find('all'));
$this->set('status','ok');

という具合にパラメタにセットして、

 protected function return_as_json(){
$this->header('Content-Type: text/html');
echo $this->indent( json_encode($this->viewVars) );
exit();
}

というような関数を呼び出してあげれば、検索結果をJSONで返すWebAPI的なものを作ることができる。

ところがこれがちょっと変で。JavaScriptからみた戻り値はこんなふうに見える。

{
"results": [
{
"Model": {
"id": "5",
"user_id": "8",
   :(略)
}
},
{
"Model": {
"id": "4",
"user_id": "8",
   :(略)
}
},
],
"status": "ok"
}

普通APIで検索結果を取得する時って、普通こうなってません?

{
"results": [
{
"id": "5",
"user_id": "8",
   :(略)
},
{
"id": "4",
"user_id": "8",
   :(略)
},
],
"status": "ok"
}

呼び出し側のJavaScriptから見たら、

function(res){//success
var models = res['results'];
for (var i = 0; i < models.length; i++) {
console.log(models[i]['Model'].id)
}
}

って謎の型名をつけるよりも、

function(res){//success
var models = res['results'];
for (var i = 0; i < models.length; i++) {
console.log(models[i].id)
}
}

のほうが自然ですよね。

前置きが長くなったけれど、そういうわけで型名を付けないで検索結果を返すようにする実装。
本当はjson_encodeするときに型名を落とすのが適切だと思うのだけれど、うまいやり方を思いつかなかったので、modelのカスタムfindとして定義してみた。

連想配列のまま構造を変えるのではなくて、一旦フラットにしてテキストとしてモデル名を除去しています。

allの代わりにnoModelNameを使えば、モデル名部分のないハッシュを得ることができます。

 $this->set('results', $this->Model->find('noModelName') );
$this->return_as_json();

FacebookSDKがCakePHPのSessionを使うようにする

cakePHPでFacebookログイン[1/2] – もぎゃろぐで書いたとおり、
FacebookSDKをつかうと簡単にfacebookログインを実装することができます。,

FacebookSDKは、この簡単実装を実現するために、内部で$_SESSIONを呼び出して、accessTokenや CSRF対策のstate変数を保存しています。
この実装は、単純なWEBサーバであれば困らないのですが、複数のWEBサーバで分散処理するためにセッションをDBに保存する、というような設定をしている場合、 このままだと動かないことが予想されます。

CakePHPは、そういうときのためにセッションをデータベースに保存したりすることができるようになっていますので、FacebookSDKの変数も、CakePHPのセッションを使ってもらうようにしたいところです。

facebookのエンジニアの人たちは優秀なので、こういう用途も想定してSDKを書かれたようです。

facebookSDKのFacebookクラスは、ほとんどの実装がbase_facebook.phpのBaseFacebookクラスで実装されて、上記セッション変数の処理だけが、facebook.phpのFacebookクラスで実装されています。
なので、facebook.phpをコピーしたcakeFacebook.phpを作って、$_SESSIONの部分をcakePHPのCakeSessionに書き換えたCakeFacebookクラスを用意すれば、FacebookSDKもcakePHPの設定に従ってくれるようになります。

ということで出来上がったのがこちらになります。

facebook.phpと同じディレクトリに配置して

App::import(‘Vendor’, ‘facebook/cakeFacebook’);

$this->facebook = new CakeFacebook(array(
‘appId’ => Configure::read(‘facebook.appid’),
‘secret’ => Configure::read(‘facebook.secret’),
));

という具合にして使います。あとは通常のFacebookクラスと同じ使い方でOKです。

cakePHPでFacebookログイン

前の記事で予想がついている方もおられるかと思いますが、facebookログインするサイトを作っています。
RubyじゃなくてPHPで。人生何が起こるかわからないものですね。

さすがに生のPHPは書いてられないので、CakePHPを使っています。
ということで、今日の記事は、CakePHP+FacebookSDKです。
ぐぐれば似たような記事はいくつか出てくるのですが、いずれも記述が古くて動かなかったので、僕バージョンをまとめました。これも半年位すると動かなくなるのかもしれないですが。

さて、PHPでFacebookのOAuth2.0ログインを実装するやり方については、公式サイトに記述があります。

Login for Server-side Apps – Facebook Developers

ただこれだとAPI呼び出しが面倒なので、Facebook公式のfacebook-php-sdkをつかいます。
facebook-php-sdkの利用サンプルも、ぐぐればたくさん出てきますが、その大半は古い内容で現在のSDKでは動きません(getAccessToken()をわざわざ呼び出しているのは古いコードです。)
Migrating to OAuth 2.0 update: PHP SDK v.3.1.1- Facebook Developersを参考に書き直したのがこちら。

要するに、$facebook->getUser()して取れたらログイン成功、取れなかったらログインURLにリダイレクトすればOKです。昔のSDKで必要だったgetAccessTokenの呼び出しは、SDKが内部で処理してくれるようになりました。
コールバック引数を見てアレコレする部分もSDKが面倒を見てくれるので、login.phpとcallback.phpのように二つのファイルを作る必要すらありません。PHP用のSDKらしい実装ですね。

これを踏まえて、CakePHPでFacebookContollerを書いたのがこちら。

  • facebook SDKは、/app/Vendor/facebook/の下に配置。
  • appidとsecretは、bootstrap.phpに
    Configure::write('facebook',array(
    'appid'=>'***',
    'secret'=>'***'
    ));
    

    という具合に。

一応loginとcallbackを分けておきましたが、getUser()部分をbeforeFilterに移動させれば、ひとつのメソッドにまとめてしまうことも可能だと思います。
もっといえば、FacebookControllerというクラスを用意しなくても、ほかのControllerのbeforeFilterに書いてしまってもかまわないかも知れないですね。

[php]error_logが使えない環境でログをとる方法

sxchu_752518stop.jpg

※今日のお話は、最初から最後まで、知っている人にとっては当たり前すぎて困るレベルのお話です。

 phpでは、error_logという関数でログを出すことが出来て、一般にこれはApacheのエラーログに出力されるので、これを使うとなんだか動かないスクリプトの動きを追うことが出来ます。

ところが、一部のレンタルサーバで、PHPは使えるんだけどログを見せていただけない環境が存在します。全部見せてくれない環境はさすがに少ないですけど、例えばサブドメインを使っていると駄目とか、共用SSLを使っていると駄目とか。
こういう場合に、しょうがないので自前ログを用意する手順。

1.絶対パスを確認

ログを書き出す場所を決めるために、まずはディレクトリ構成を知る必要があります。
log.phpとか適当な名前のファイルに、こういうスクリプトを書いて走らせてみればだいたい分かります。

<?php
echo "cwd:".getcwd();
?>

「/home/mogya/public_html/」的なものが得られるはず。これだったら、/home/mogya/debug_log あたりにログを出力するようにすれば良さそうですね。

2.ログ出力関数

<?php
function dbg_log($str){
$filepath = "/home/mogya/debug_log";
$fp = fopen($filepath, "a+");
fputs( $fp, $str."\n" );
fclose($fp);
}
?>

ログを出力したいファイルの頭あたりにこんなのを書いておいて、error_log()の代わりにdbg_log()を書けば、変数の中身なり何なりを出力して動きを追うことが出来る。
環境によっては/home/mogya/debug_log に書き込めないことがあり得るので、その場合はFTPで適当なファイルをあげておいて、アクセス権を777にしてしまえば書き込めるようになる。

PHPでファイルに文字を書くだけなんだから当たり前の話なんだけど、毎回思い出すのが面倒なので書いてみた。
セキュリティとか深刻に考えたスクリプトじゃないので、終わったら消すことが前提です。


PHPでメール送信(webteko第四回)

 ウェブテコ第4回で、PHPで作るメールフォームという話をさせていただきました。
プレゼンとソースを公開します。

メールフォームを作る

View SlideShare presentation or Upload your own. (tags: webteko)

ソース:sendmail.lzh

あと、メールアドレスのミスを指摘してくれる入力フォームもよかったらご覧くださいませ。

2009-01-19 10:03:23
ソースがダウンロードできなかったので、圧縮した物を上げ直しました。lzhで圧縮してあるので、解凍してご覧くださいませ。

2009-01-19 19:51:28
Tempa Playground – ウェブテコ第4回終了が公開されていますね。すでに記事あげた方は、トラックバックするといいんじゃないかと思います。

phpでapacheのログにログを出す方法

 error_logでエラーログに出せばよい。

おわり。



…いやでもさ。まずいことがあった時に出力されるはずのエラーログに変数の値をprintfデバッグするのはどうなのさ? ファイルを分けたい時もあるでしょ?

そういう時は、apache_note関数とapacheのhttpd.confの記述を組み合わせる。



httpd.confに

<VirtualHost *:80>
ServerName test1.example.com
ServerAlias localtest1.example.com
DocumentRoot /var/www/someapp/html/
    LogFormat "%h %l %u %t \"%r\" %>s %b %{phpdebug}n" phpdebug_log
    CustomLog /var/www/someapp/log/phpdebug_log phpdebug_log
ErrorLog /var/www/someapp/log/error_log
TransferLog /var/www/someapp/log/access_log
</VirtualHost>



 こんな具合にLogFormatとCustomLogを書いておく。



で、PHPの方では、

apache_note("phpdebug", "php debug message: {$_SERVER['REMOTE_ADDR']} ({$_SERVER['HTTP_USER_AGENT']})");



こんな感じでログを出す。



すると、CustomLogで書いたファイル名のファイルにログが出力される。


[daisuke@hige ~]$ sudo tail -f /var/www/someapp/log/phpdebug_log
192.168.11.128 - hoge [08/Jul/2008:09:13:33 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)
192.168.11.128 - hoge [08/Jul/2008:09:13:48 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)
192.168.11.128 - hoge [08/Jul/2008:09:16:37 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)



こうしておけば、apacheの通常のログと同様にログファイルを扱うことができるので、ログのローテーションとかパースに既存の仕組みを使うことができて嬉しい。


おまけ



 別のファイルに出すと、tailでログを監視するときerror_logと二つのウィンドウを開かないといけないじゃないかって?

tailはなんとファイル名を複数監視することができる。


[daisuke@hige ~]$ sudo tail -f /var/www/someapp/log/error_log /var/www/someapp/log/phpdebug_log
==> /var/www/someapp/log/error_log  /var/www/someapp/log/phpdebug_log <==
192.168.11.128 - hoge [08/Jul/2008:09:13:33 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)
192.168.11.128 - hoge [08/Jul/2008:09:13:41 +0900] "GET /graph/someapp.php HTTP/1.1" 200 - -
192.168.11.128 - hoge [08/Jul/2008:09:13:48 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)
192.168.11.128 - hoge [08/Jul/2008:09:16:37 +0900] "GET /graph/someapp.php HTTP/1.1" 200 4856 php debug message: 192.168.11.128 (Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15)


携帯絵文字変換スクリプトをデバッグしてみた。

今月の流行は携帯サイトとPHPのもぎゃです。こんにちは。
携帯絵文字変換スクリプトをPHPで作ってみた。 携帯ホームページを作ろう! -ちょっと詳しいモバイルサイトの作り方-というのがあって、フリーで使えるのはすばらしい!と思ったのですが。
これどうも、ドコモの絵文字が正しく出ません。すくなくとも手元の環境では出ない。

  • サーバ:Apache/2.0.52
  • PHP:PHP 5.1.6 (cli) (built: Oct 26 2007 13:49:00)
  • 携帯:F705i。あと、i-mode HTML Simulator II ver5.1も試したけど出なかった。

 しょうがないのでデバッグしました。

結論

 emoji.csvをこっちに差し替えると出るみたいです。
emoji.csv
元のファイルとの違いは、iモード絵文字をダブルクオーテーションでくくっただけです。

問題だったこと

 作ろうiモードコンテンツ:基本絵文字一覧 | サービス・機能 | NTTドコモを参考に、べたにphpファイルに文字列を貼り付けたら、これは表示できました。
 Wiresharkでパケットを拾ってのぞいてみると、データが化けているとかじゃなくて、そもそもデータが出ていません。
 さかのぼっていくと、そもそも$emoji_array[1]が空っぽのままみたいです。
fgetcsvでググってみると、どうもこの関数は結構危険らしく。
テレパス・ラボ : fgetcsv()で日本語が読み込めない
iモード絵文字も、SJIS範囲外とはいえ日本語っぽい文字なのは同じなので、環境によって空っぽになってしまうんじゃないでしょうか。上記サイトで言われているように、問題の文字列をダブルクオーテーションでくくってみたら動いたので、とりあえずご報告まで。