[CakePHP2] json形式のデータを手軽に出力する

メインで使うPHPフレームワークをCakePHP1.3系→2系にしてから数ヶ月が立ちました。

PHPUnitや、Pluginなどでも2系の恩恵を感じていますが、今回はより手軽にできるようになっていたjson形式のデータ出力方法をご紹介してみます。
 
APIなどでよく使うjsonですが、1.3系までだと独自のwrapperメソッドを用意してstatusと結果を一緒に返すような仕組みをつくったりしていました。

2系からはCakeResponseを使った方法など含め、簡単なやり方が増えているみたいですが、中でも以下のやり方が手軽で気に入っています。
 

例えば、status=”complete”、message=””みたいな結果をjson形式のデータを、レスポンスとして返却する場合。
コントローラ内のfunctionで以下のようにすればOKです。

public function jsonTest() {
    $result = ['status' => 'complete', 'msg' => 'Request completed.'];
    $this->viewClass = 'Json';
    $this->set(compact('result'));
    $this->set('_serialize', 'result');
}

 

これをブラウザ側で叩くと、

{"status":"complete","msg":"Request completed."}

となります。

$result変数の中身を、viewに渡して、_serialiseにセットしたresultを指定すると、cake標準のviewによってjson形式の出力結果を返してくれます。
 

もちろん連想配列でもいけます。

    public function jsonTest()
    {
        $result = ['status' => 'complete', 'fruits' => ['apple', 'orange', 'banana']];
        $this->viewClass = 'Json';
        $this->set(compact('result'));
        $this->set('_serialize', 'result');
    }

 

結果は、

{"status":"complete","fruits":["apple","orange","banana"]}

となります。
 

modelのデータ配列をそのまま渡して使うなど、実践的に使える場面もありそうです。
 

viewClassの指定など、CakePHP2.1で超簡単にJSONを出力する方法を参考にさせてもらいました。

お試しあれ。


[PHP] range関数によるforeach文でfor文に挑んでみる

PerlやRubyには(“01”..”10″);や[1..10]といったお馴染みのrange指定による配列の書き方があるのにPHPにはなくて残念、と思っていたPHPerです。

これまで知らなかっただけで、PHPにもrange(1, 10)という近い役割の関数があることを最近知りました。

range指定が使えれば、一定数で回したい時などに直感的に書けて便利そうです(Rubyでは10.timesとかもありますが)。
 

PHPでお馴染みのforeachを使うと、

for ($i = 1; $i < = 10; $i++) {
    echo $i;
}

foreach (range(1, 10) as $i) {
    echo $i;
}

みたいな置き換えが考えられます(何も考えずに書けそう)。
 

ただし、楽に書けるのはいいのですが、一般にforeachはforよりも処理速度が遅いという問題がネックになりそうです。

ということで、どれくらい速度差があるのか、検証して見ることにしました。

 

forとforeachの処理速度を計測してみる

環境

  • Windows上のVM
  • CentOS6.2
  • PHP 5.4.2 (cli)

for文のテストコード

$max = $argv[1];
$startTime = microtime(true);

for ($i = 1; $i < = $max; $i++) {
    echo "{$i}\n";
}

$endTime = microtime(true);
$spendTime = round($endTime - $startTime, 3);
echo "\nspend:{$spendTime}\n"

foreach文のテストコード

$max = $argv[1];
$startTime = microtime(true);

foreach (range(1, $max) as $i) {
    echo "{$i}\n";
}

$endTime = microtime(true);
$spendTime = round($endTime - $startTime, 3);
echo "\nspend:{$spendTime}\n"

計測結果

それぞれ5回ずつ試行した結果の平均値をまとめてみます。

対象 1万件 10万件 100万件
A. for文 0.035秒 0.277秒 2.864秒
B. foreach文 0.040秒 0.290秒 3.074秒
差(B – A) 0.040秒 0.290秒 3.074秒

for文の方が遅いのは確かですが、件数が1万件、10万件の場合の差は10ミリ秒以下と意外と少なかったです。
100万件だと200ミリ秒くらいの差が出てきますね。

もう少しforeachがんばれないかなーということで、少しコードをいじってみます。
 

forとforeach(改良版)で処理速度を計測してみる

foreachに渡すrange関数の結果の値を先に変数に入れてから、やってみます。

foreach文のテストコード(改良版)

$max = $argv[1];
$startTime = microtime(true);

$range = range(1, $max);
foreach ($range as $i) {
    echo "{$i}\n";
}

$endTime = microtime(true);
$spendTime = round($endTime - $startTime, 3);
echo "\nspend:{$spendTime}\n"

計測結果

先ほどと同じように、それぞれ5回ずつ試行した結果の平均値をまとめてみます。

対象 1万件 10万件 100万件
A. for文 0.035秒 0.277秒 2.864秒
B. foreach文(改良版) 0.033秒 0.268秒 2.914秒
差(B – A) -0.002秒 -0.009秒 0.050秒

なんと、件数が1万件、10万件の場合はforeachの方がほんのちょっと速い結果になりました。
100万件の場合だと若干for文のほう50ミリ秒速いものの、1/4まで差が縮まったことが見てとれます。

まとめ

思ってたよりもforeach文ががんばってくれた結果になりました。
レスポンスにシビアな処理や、かなり大量のデータを扱う処理でない限り、range関数を使ったforeach文でもまったく問題ないのではないでしょうか。

普段使いのバッチ処理や、テストコードなんかでは活躍する場面がありそうです。

さらにforeach文の改良版(range関数の結果の値を先に変数に入れておく)であれば、変数定義で多少手軽さは失われますが、誤差のレベルまで速度は縮まります。

今回の結論(ざっくり)

  • 意外とforeachがんばってくれる
  • レスポンスにシビアな処理でない限り、十分に代用できそう
  • PHP5.4系の恩恵ももしかしたらあるのかも

 
viva PHP


[locate] locateコマンドを実行するとエラーになる

すばやくファイルを検索するときに非常に役立つ「locate」コマンドですが、

対象サーバで初めて使う場合や、インストール直後だと以下のようなメッセージが表示されることがあります。

 

locate: can not stat () `/var/lib/mlocate/mlocate.db': No such file or directory

 

日本語環境だと以下の様なメッセージになると思います。

locate: stat () `'/var/lib/mlocate/mlocate.db' できません: そのようなファイルやディレクトリはありません

 

エラーメッセージは一見、権限の問題のようにも見えるのですがそうではなく、以下のコマンドを実行することで解決できます。

updatedb

 

locateコマンドはあらかじめ構築した検索インデックスのデータベース(ファイル)を元に高速に検索をします。

このコマンドは、データベースを再構築してくれるので、なにかをインストールした直後などで、locateの検索にひっかからないファイルがある場合にも、コマンドを実行することで検索対象に含まれるようになります。

 

仕組みを知っていればなんてことはないのですが、備忘録的に。


[iPhone] Google ChromeのiOS版を使う

PC版のChromeを使い慣れている人は待ち望んでいた(と思われる)、Google ChromeのiOS版が出ました!

http://itunes.apple.com/jp/app/chrome/id535886823?mt=8

ブックマークをPCと同期できる

個人的には、Safariがめちゃめちゃ使いにくいとは思わないのですが、PCではメインでChromeを使っているため、なんといってもブックマークの同期がPCとスマホでできない、というのがストレスでした。

Chromeになったので、GoogleアカウントでログインすればPCのブックマークと自動で同期されます。
また、入力履歴やパスワード(※設定している場合)も同期されるので、PCでさっきまで見ていたサイトのURLを一部入力してすぐに閲覧したり、かなりシームレスに使えるようになると思います。

ブックマーク自体は一番上のフォルダがわかれていて、iPhoneのブックマークのデフォルトは「モバイルブックマーク」のフォルダになっているようです。
ブックマーク時にフォルダは選択できました。

複数タブもサクサク動く

まだ使い込んではいないですが、複数タブ立ちあげ時の動きや、UIのわかりやすさなどは、Safariよりも上な感じがしています。

複数タブから任意のタブを選択する際も、タブが重なって一部が見える状態で表示してくれ、小さい画面でも見やすいつくりになっています。

全体的な動きも軽快に感じられ、十分にiPhoneのメインブラウザとして使っていけそうです。

 

まだ初日なので、引き続き使ってみて、機能や使い勝手など探っていきたいと思います。


[iPhone] AppleID(メールアドレス)変更時の対応

Apple IDのメールアドレスを変更したところ、iPhone側でiTune、iCloudなどなどの認証時に、前のメールアドレス(固定)+パスワード入力を求められてしまう状態に。

案内されるアカウント設定画面だとメールアドレスは変更できないようなので、どうやるのか調べたところあっさり解決しました。

 

Setting > Store

 

の一番下に表示されている「Apple ID:メールアドレス」をクリックして、「Sign Out」を選択。

すると「Sign In」のボタンが表示され、新しいメールアドレス+パスワードで無事認証できました。

 

わかればなんてことはないのですが、メールアドレス変更自体あまりない機会だけにハマる人もいるんじゃないかと思いメモ的に投稿。

※英語表記のため、日本語表示の場合は読み替えてください。手順は変わらないはず。


[AWS] RDSでslow queryを出力する

AWSのRDS、デフォルトはslow query log(スロークエリーログ)の出力オフなのですが、パラメータの変更によりテーブルに出力することができます。

DB負荷や、アプリ側のボトルネック調査に役立つので設定をしておきたいと思います。

 

今回は

  • slow query logをテーブルに出力する
  • 1秒以上かかっているクエリーが対象
  • 10,000行以上読み込んだクエリーが対象

の設定をおこないます。

RDSのmy.cnfをいじることはできないため、「RDS Command Line Toolkit」を利用して設定を変更していきます。

Linuxサーバで作業します。

コマンドラインツール利用の準備

コマンドラインツールを初めて使う場合はもろもろ設定です。

  • Javaは/usr/bin下に配置
  • ツール群を/usr/local/rds-tools下に配置
  • アクセスキーペアの設定のファイルを/usr/local/rds-tools/.credentialに配置
  • リージョンは日本(ap-northeast-1)

 

アクセスキーペアの設定ファイルの中身は以下です。

AWSAccessKeyId=<アクセスキー>
AWSSecretKey=<シークレットキー>

 

以下のように環境変数をセットします。

export JAVA_HOME=/usr
export AWS_RDS_HOME=/usr/local/rds-tools
export PATH=${PATH}:${JAVA_HOME}/bin:${AWS_RDS_HOME}/bin
export AWS_CREDENTIAL_FILE=/usr/local/rds-tools/.credential
export EC2_REGION=ap-northeast-1

新しいグループをつくる

パラメータ設定用に新しいグループをつくります。

  • グループ名は「modify-params-group」
  • MySQLのバージョンは「5.5」

 

以下を実行します。

rds-create-db-parameter-group modify-params-group  -d "parameter group for modify-params-group" -f MySQL5.5

グループを作成したら、設定を変更します。

rds-modify-db-parameter-group modify-params-group \
--parameters="name=slow_query_log, value=ON, method=immediate" \
--parameters="name=long_query_time, value=1, method=immediate" \
--parameters="name=min_examined_row_limit, value=10000, method=immediate"

 

インスタンスに新グループを適用する

最後に、対象としたいインスタンスのグループを今回つくったグループに変更します。

  • インスタンス名は「mydb」

 

以下を実行します。
rds-modify-db-instance mydb --db-parameter-group-name=modify-params-group

 

最後にインスタンスを再起動・・・と思ったのですが、
再起動なしでも反映されていました。

 

確認する

以下を実行します。
rds-describe-db-instances
 DBINSTANCE  daws01  2012-05-10T01:23:30.180Z  db.m1.small  mysql  5  root  available  daws01.clkgkdk5umud.ap-northeast-1.rds.amazonaws.com  13001  ap-northeast-1a  30  n  5.5.20  general-public-license
       SECGROUP  default:vpc-4bac1822  active
       PARAMGRP  modify-params-group  pending-reboot
       SUBNETGROUP  modify-params-group  Complete
       OPTIONGROUP  default:mysql-5-5  in-sync

 

MySQL上からも設定を確認します。

mysql > show global variables like 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | ON    |
+----------------+-------+
1 row in set (0.00 sec)

mysql> show global variables like 'long_query_time';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
1 row in set (0.00 sec)

mysql> show global variables like 'min_examined_row_limit';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| min_examined_row_limit | 10000 |
+------------------------+-------+
1 row in set (0.00 sec)

 

問題なく設定されていれば完了です。

今後slow queryが発生したら、mysql.slow_logテーブルにレコードが登録されます。

 

なお、設定パラメータの確認は、RDSのウェブコンソールの「DB Parameter Groups」メニューからも確認できます(パラメータの検索も可能)。

RDSのコマンドラインツールは初めて使ったのですが、今回のようなグループ追加、設定変更だけでなく、インスタンスの作成や削除などもできるので、使いこなすとかなり幅が広がりそうです。

いずれはウェブコンソールからもさくっとできるようになるのかなと思います(実はもうできるとかあったら、教えて欲しいです)。

 

今回は以下のブログを参考にさせてもらいました。

http://blog.hori-uchi.com/2009/11/amazonrdsslow-query.html

※パラメータなど一部古い情報の部分は変更しています。


[iOS6] Safariでスマホサイト上からもファイルアップロードが可能に

WWDC2012が終わりいろいろ情報出てきていますが、iPhone開発まわりでうれしい情報が。

これまでは、iPhone用のサイトをつくっても、写真をアップロードさせるにはiPhoneアプリ一択でしたが、Safari上のスマホサイトでもアップロードができるようになります。

 

iOS6からSafari上でのinputタグ

<input type="file" name="hoge" />

がサポートされるためで、ひとまずiPhone用にも機能を提供したいときなど、活躍しそうです。

 

iOS6は2012秋から提供開始予定とのことです。

参考:
http://blog.shonanshachu.com/2012/06/ios6safari.html