旅するえんじにあ - Engineers to Travel -

旅するエンジニアの気まま備忘録

【PHP】 PHP5.4系とPHP7系のパフォーマンスを比較してみた

PHP5.4系とPHP7系のパフォーマンスを比較してみた

11月12日はPHP7のリリース日ですね(予定ですが) ということで、最新のRC版を入れて 今更感ありますが、簡単なパフォーマンステストをしてみました。

PHPのメジャーバージョンアップ自体が10年ぶりとなり 最近何かと話題になっています。しかも実行速度2倍以上になるとか。

さて、早速テストしてみましょう。対象はPHP5.4.42とPHP7.1.0です。

7.0.0RC6とかにしたかったんですが、簡単なパフォーマンステストだし、いっかと。

早速フィボナッチを使ってテストしてみました。

コードは以下の通り。

fibonacci.php

<?php
function fib($n) {
    if ($n < 2) return $n;
    return fib($n - 2) + fib($n - 1);
}

$time_start = microtime(true);
print fib(38);
print "\n";
$time_end = microtime(true) - $time_start;

echo $time_end . " seconds\n";

予想としては30秒くらいで返ってくるんじゃないかと思ったのですが、以外にも以下のような結果となりました。 ※過去に少しやった時バージョン忘れたのですが、そのくらいの結果だったので

[root@localhost ~]# php -v
PHP 5.4.42 (cli) (built: Jun 12 2015 09:40:10) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
1st
39088169
17.16079211235 seconds

2nd
39088169
17.973091840744 seconds

3rd
39088169
17.841723203659 seconds

4th
39088169
19.280640125275 seconds

5th
39088169
17.899519920349 seconds

平均だと 18.02 seconds くらいでしょうか。

ではまったく同じ環境で、PHPを削除し、PHP7.1.0をインストールしてみました。 その状態で全く同じプログラムを動かしてみると。

[root@localhost ~]# php -v
PHP 7.1.0-dev (cli) (built: Nov  4 2015 12:33:34) ( NTS )
Copyright (c) 1997-2015 The PHP Group
Zend Engine v3.1.0-dev, Copyright (c) 1998-2015 Zend Technologies
1st
39088169
6.7832338809967 seconds

2nd
39088169
7.8204309940338 seconds

3rd
39088169
7.7650599479675 seconds

4th
39088169
8.2238001823425 seconds

5th
39088169
7.8330240249634 seconds

平均7.68秒!!!!

2.34倍も早い!

なんてこった・・・

ここまで違いが出るとは思わなかった・・・

PHP7をphalconとか乗せたら体感でもわかるくらい早くなりそうですね!

期待は高まる!

他の言語でも同様にやってみたいなぁと思いつつ。 今ある環境でさくっと試してみた結果でした。

【PHP】ORM Idiormの導入を決意するまで

久々になっちゃいました 最近はインフラっぽいこともせず、とにかくOJTデザインパターンについて コーディング規約やら書き方を教え、コードレビューをする毎日になっていました。

人が増え、それに対して今後こういうソースの書き方をしていこうと話すのは重要でもあり 骨の折れる作業です。

さて、冒頭はこの辺にしておいて 今回はOR/Mのお話です。

今使っているFWについてはもしかしたらちょろっと書いたかもしれませんが Monobitエンジン付属のMWFフレームワークというものを使っています。

また、別会社への移管をしていた関係もあり、FWののソースがどこで どこに手を入れたのかわからない状態です。 ※正確にはSVNのログを追えばわからなくはないとは思うのですが・・・

そんなFWの中で少しでも綺麗に見やすいソースに。 そんな感じでリファクタリングもしていきつつ、テスト書きつつmigrationを入れたり色々揃えてきたわけです。

最近は一人開発から人数も増えてきて 開発チームっぽくなってきたのですが、やはり問題は山積みです。

軽量FWに近いのでOR/Mというものがありません。 どうしているかというと、PDOで接続をして直接queryを投げています。

何が悪いか?何も悪くないのですが、取得する方法が色々あるのです。 多分色んな人が便利だとうろ思って同じようなmethodを追加していったのでしょう。 聞くこともなく、忙しいからを理由に。 なのでDBからのデータ取得に関するフォーマットが決まっておらず、transaction処理も(これはまた別の話ですが)適当になっていたり。

そんな中、どれを使えばいいのだろう?このソースではこれ、あっちのソースではあれを使って、でも取得結果は一緒で。。。 ということで今後今まで使っていた資産を使わずにQueryを投げる用の新しいmethodを用意しました。

こういう事するからそうやって増えていくんだよ。ってわかっていながらも。 色々試行錯誤してQueryを投げるためにエラーチェックも入れ、環境に考慮した形で組んだつもりだったのですが 色々と問題も起きます。

PDOでQueryを投げて取得するときはfetchするのですが、fetchAllしている関係でそのmethodを使った結果 単一のデータを取得したくても配列の0番目に返ってきてしまうのです。

期待している返り値

array [
    ["id"] => "1",
    ["hoge"] => "hogehoge",
    ["created"] => "2015-10-22 10:00:00"
]

実際の返り値

array [
    [0] => [
        ["id"] => "1",
        ["hoge"] => "hogehoge",
        ["created"] => "2015-10-22 10:00:00"
    ]
]

もちろん単一データを取得するときは期待している返り値のほうが扱いやすいですよね。 ということで単一データでも取れるようにしようかと色々思考錯誤したのですが、これ以上取得methodを増やしていけば同じような歴史を繰り返すに違いないと また、便利に色々とできるようにしたほうが使い勝手もいいだろうし、今はSelectについて考えてるけど、insertは?updateは?どうすんの? みたいなのが頭を過ぎります。

正直これ以上増やす事を考えるなら各エンジニアにPDOで投げてfetchでもfetchAllでも自由に使ってくれって思ったのですが あまりにも投げやりだと。

そこでOR/Mがなんとかしてくれるかなと思いました。

正直エディタの宗教戦争もありますが、OR/Mも一つの宗教戦争だと思います。 使う事が悪いわけではないですし、知識があればOR/Mなんて使わずにQueryを書いて投げたほうが速度は早いです。 学習コストもかかりますしね。

どっち派みたいなものはないので適材適所だとは思っていますが、今回のように開発をしていくうちにソースに統一性、一貫性がなくてなっていく場合は有効かなと思っています。 もちろん今まで作ったソースについてもリファクタリングが必要にはなってくるのですが。

OR/Mを入れる事におけるメリットとしてはSQL混入による視認性の低下を回避する事ということがあります。

SQL言語というのは非オブジェクト思考言語になります。細かい話は一旦置いときましょう。んじゃPHPはどうなんだよとか色々出てくるので。 要は継承やカプセル化ができない言語をソースに交える事により、オブジェクト思考言語と非オブジェクト思考言語が混ざることによる視認性の低下が懸念されます。 その点OR/Mは拡張性を持ち、永続化することが可能です。更にSQLの混入を回避できます。もちろん拡張性や視認性も上がります。

もちろんいいことだけでもないです。 正直OR/Mこそ、通常のSQLでのセキュリティ対策やSQL言語を知っている人こそ使うべきなのかな(道具として)と思っています。 OR/Mを使うことによってプレースホルダの問題を意識しなくても良くなり、むしろOR/Mで書いている人は極端な話SQL言語を覚えなくてもある程度のことはできてしまいます。 本当にこれで大丈夫か等考えずに組めてしまう部分がある。 更にSQL言語を知っていたとしてもOR/Mによって仕様が違うので、それぞれに学習コストがかかるのもデメリットと言えます。

結構前の話ですが、ORMがアンチパターンである11の理由とか結構有名になりましたよね。

今でももちろん賛否両論あるのですが、とにかく適材適所でもあり、チームのレベルや言語、デザインパターンやコーディング規約等色々な状況に対して導入すべきかをチームで考えればいいと思います。

ということで今回はMWFフレームワークにOR/Mを入れてみました。 この記事は別途書いていきますね。

因みに今回入れたOR/MはPHPで使えるidiormというものになります。 最近OR/M ActiveRecordをIdiorm & Parisで作るのが流行っているみたいですが 必要だったのはOR/MのみだったのでIdiormを入れてみました。

ただやはりMWFフレームワークで現在のソースに対応させるのに苦悩しながら組み込みをしましたので、別途ブログで共有できればと。

今回は長い話になり、技術的な要素が少ないですが、この辺にしておきたいと思います。

【git】 git revert で変更を元に戻す

たまにありますよね。 あーーーーpull requestで先に入れないといけないbranchあったのに マージする順番間違えたー

はい僕です。

そんな時あー面倒くさいどうしようってちょっと混乱気味になって

git reset --hardで戻して・・・git push -f...でいいかな?

とかやってると次そのbranchが共有すべきbranchで

別の人がpullしようと思ったらできなくなるみたいな。

ってことで今回はgit revertです。 もちろんgit rebase -iとかでも良いのですが 間違いは認めてとりあえず戻そうみたいな感じです。

ただ上記に書いた「戻そう」という表現はある意味間違っています。

何故かというと今回のgit revertという作業はcommitに対してその前の状態に戻すというか 新しくその前の状態にcommitするという感じなのです。

やってみれば早いですね。

まずは適当にbranchを切ります。

今回は* feature/revert_testというbranchを切りました。

更に2回くらいcommitしてみましょう。

commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d
Author: deadcode
Date:   Tue Jun 2 18:10:26 2015 +0900

    add var_dump.

commit 1409869b3da7d478d6f541b6ac8507d8a5c51241
Author: deadcode
Date:   Tue Jun 2 18:10:02 2015 +0900

    add comment.

こんな形でcommitをしてみました。

何をしたかというと

commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d
Author: deadcode
Date:   Tue Jun 2 18:10:26 2015 +0900

    add var_dump.

diff --git a/controllers/app/SampleController.class.php b/controllers/appindex 3a72df2..9522164 100644
--- a/controllers/app/SampleController.class.php
+++ b/controllers/app/SampleController.class.php
@@ -2,7 +2,7 @@
 // ここにコメントを入れてみました。
 
 
-
+var_dump('test');
 
 
 

commit 1409869b3da7d478d6f541b6ac8507d8a5c51241
Author: deadcode
Date:   Tue Jun 2 18:10:02 2015 +0900

    add comment.

diff --git a/controllers/app/SampleController.class.php b/controllers/appindex 9dadae8..3a72df2 100644
--- a/controllers/app/SampleController.class.php
+++ b/controllers/app/SampleController.class.php
@@ -1,4 +1,25 @@
 <?php
+// ここにコメントを入れてみました。
+
+
+
+
+
+
+
+
+
+
+

こんな感じでコメントと空行を入れてcommitした後に、var_dumpを入れました。 この状態でvar_dumpを誤ってcommitしてpushしてしまったとします。 更にそのbranchは共有しているbranchでgit reset --hard HEAD^とかでlocalは戻るとしても force pushしようものなら共有している作業者が困る事になります。

ということで早速revertしてvar_dumpが入る前のcommitに戻してあげましょう。

この場合git revertを使うのですが、git revert 後commitIDを入れてあげる必要があります。 今回の場合、var_dumpを入れたcommitをadd commentのcommitを戻すので

commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d
add var_dump.

commit 1409869b3da7d478d6f541b6ac8507d8a5c51241
add comment.

e39a5f73e5ffaf2ef1c956a05d73f13f9aff431dを指定します。 CommitIDはフルIDでなくてもいいのですが、せっかくgit logで出てくるので。 行ったcommitを戻すってイメージですね。

早速実行してみましょう。

$ git revert e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d

すると以下のような画面になります。

Revert "add var_dump."

This reverts commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch feature/revert_test
# You are currently reverting commit 1409869.
#
# Changes to be committed:
#       modified:   controllers/app/SampleController.class.php
#

この画面を編集すればCommit commentに反映されます。 今回はこのまま編集を終了させます。

$ git revert e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d
[feature/revert_test 6b3bb17] Revert "add var_dump."
 1 file changed, 1 insertion(+), 1 deletion(-)

うまくいったみたいですね。 早速ログを見てみましょう。

commit 6b3bb174b5fd2316c99da9bbd8133023d5e75e48
Author: deadcode
Date:   Tue Jun 2 18:27:51 2015 +0900

    Revert "add var_dump."
    
    This reverts commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d.

diff --git a/controllers/app/SampleController.class.php b/controllers/appindex 9522164..3a72df2 100644
--- a/controllers/app/SampleController.class.php
+++ b/controllers/app/SampleController.class.php
@@ -2,7 +2,7 @@
 // ここにコメントを入れてみました。

-var_dump('test');
+

commit e39a5f73e5ffaf2ef1c956a05d73f13f9aff431d
Author: deadcode
Date:   Tue Jun 2 18:10:26 2015 +0900

    add var_dump.

diff --git a/controllers/app/SampleController.class.php b/controllers/appindex 3a72df2..9522164 100644
--- a/controllers/app/SampleController.class.php
+++ b/controllers/app/SampleController.class.php
@@ -2,7 +2,7 @@
 // ここにコメントを入れてみました。
-
+var_dump('test');

こんな風になりました。 今回revertすることにより、追加されたvar_dump('test')が消された状態のものが用意され、それをcommitしたことになりました。

これがrevertになります。

他にもpull request時のマージも取り消したい場合等はまた別途。

【PHP】 爆速と噂されるPHP FrameworkのPhalconをインストールしてみる

最近爆速と話題のPhalconをインストールしたのでメモ。

今回は以下サーバに対してインストールをしてみようと思います。

最低限のインストール

まずは今回使うサーバです。 ちょっと古いですが、以下のサーバにインストールしようと思います。

# cat /etc/redhat-release 
CentOS release 6.2 (Final)

php,gcc,php-develのインストール

ということで一旦PHPgcc,php-develのインストールをします。

epelレポジトリ、remiレポジトリをインストールします。

# rpm -Uvh http://ftp.jaist.ac.jp/pub/Linux/Fedora/epel/6/i386/epel-release-6-8.noarch.rpm
# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

既にこの辺インストール終わってるよーPHPもインストールしてるよーって人は飛ばしちゃってくださいね。

さてPHP,gcc,php-develをインストールします。

# sudo yum -y install gcc make
# sudo yum -y install php --enablerepo=remi
# sudo yum -y install php-devel --enablerepo=remi

これでPHPが入りました。 自分の環境では既にPHPが入ってたんですが、ついでだからupdateしちゃいました。

[root@www6421uf php.d]# php -v
PHP 5.4.41 (cli) (built: May 14 2015 23:37:34) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies

といっても5.4なんですけどね。

今年末にはPHP7が出るというのに・・・そこら辺はまた別途。

Phalconダウンロード

早速Phalconを落としましょう。

Phalconはgitにソースがあるので、cloneします。 ついでにインストールも。

# git clone git://github.com/phalcon/cphalcon.git
# cd cphalcon/build
# sudo ./install

php.iniの設定

更にphp.iniの設定も行います。

/etc/php.d/以下にファイルを作成します。

touch phalcon.ini

ファイルを作ったら以下を記述します。

extension=phalcon.so

それでは無事phalconがインストールされたか確認してみましょう。

[root@www6421uf php.d]# php -i |grep Phalcon
PHP Warning:  PHP Startup: mongo: Unable to initialize module
Module compiled with module API=20090626
PHP    compiled with module API=20100525
These options need to match
 in Unknown on line 0
Author => Phalcon Team and contributors

ん?なんかphalconとは関係なさそうなmongoでなんかWarningが出てる・・・ ってことでpeclでmongoをインストールします。

sudo pecl install -f mongo

再度確認してみます。

[root@www6421uf php.d]# php -i | grep phalcon
/etc/php.d/phalcon.ini,
phalcon
phalcon => enabled
OLDPWD => /var/www/html/cphalcon/build
_SERVER["OLDPWD"] => /var/www/html/cphalcon/build

phalconがenabledになったのでこれでインストール完了かな。

Phalcon DevToolsのインストール

phalconにはPhalcon DevToolsというものがあります。

こちらもインストールしておきます。

これがあるとCakeのBakeみたいな感じでプロジェクトが簡単に作れます。

まずはgitからcloneします。

# git clone git://github.com/phalcon/phalcon-devtools.git

今回は落としてきたphalcon-devtoolsを/usr/local/srcへ移動させます。

そしてインストール。

# mv phalcon-devtools /usr/local/src
# cd /usr/local/src/phalcon-devtools
# . ./phalcon.sh

実際インストールといってもphalcon.shではbashにPATH通してるだけみたいです。

ということで~/.bash_profileを確認してみましょう。

export PTOOLSPATH=/usr/local/src/phalcon-devtools/
export PATH=$PATH:/usr/local/src/phalcon-devtools

上記の2行が追加されてればインストールは完了です。

確認のためにphalconコマンドを打ってみましょう。

# phalcon

Phalcon DevTools (2.0.1)

Available commands:
  commands (alias of: list, enumerate)
  controller (alias of: create-controller)
  model (alias of: create-model)
  all-models (alias of: create-all-models)
  project (alias of: create-project)
  scaffold (alias of: create-scaffold)
  migration (alias of: create-migration)
  webtools (alias of: create-webtools)

インストールできていますね。

Phalcon DevToolsでprojectを作成

それではprojectを作っていきましょう。

# phalcon project phalcon-test

Phalcon DevTools (2.0.1)

  Success: Controller "index" was successfully created.

  Success: Project 'phalcon-test' was successfully created.

phalcon-testというprojectを作りました。

早速中身を確認してみましょう。

# cd phalcon-test
# tree
.
├── app
│   ├── cache
│   ├── config
│   │   ├── config.php
│   │   ├── loader.php
│   │   └── services.php
│   ├── controllers
│   │   ├── ControllerBase.php
│   │   └── IndexController.php
│   ├── migrations
│   ├── models
│   └── views
│       ├── index
│       │   └── index.volt
│       ├── index.volt
│       └── layouts
├── index.html
└── public
    ├── css
    ├── files
    ├── img
    ├── index.php
    ├── js
    └── temp

15 directories, 9 files

とんでもなくシンプルですね。

ではapacheのvhostを設定して繋いでみましょう。

と思ったら早速Warningがでました。。。。

Warning: Phalcon\Mvc\View\Engine\Volt\Compiler::compileFile(/var/www/html/phalcon_source/phalcon-test/app/config/../../app/cache/_var_www_html_phalcon_source_phalcon-test_app_views_index_index.volt.php): failed to open stream: Permission denied in /var/www/html/phalcon_source/phalcon-test/public/index.php on line 27 Volt directory can't be written

よくあるやーつですね。

今回は/var/www/html以下にphalcon_sourceというディレクトリを作ってphalcon-testというprojectを作ったのですが、app/cacheに対して権限が与えられていなかった為にPermissionエラー。

この辺は適当にPermissionを設定してあげると簡単になおります。

chmod -R 777 /var/www/html/phalcon_source/phalcon-test/app/cache

ということで解決

これでこんな表示が出力されればOKです。

Congratulations!
You're now flying with Phalcon. Great things are about to happen!

This page is located at views/index/index.phtml

さて、さくさくとインストールまで説明しましたが、実際これから少しずつイジっていこうかな。

【Linux】ファイル内の文字列を検索する

よく使うんだけど、忘れそうなのでメモです。

Linuxのファイル内で特定の文字列を検索したい。 そんなことってたまにありますよね。

自分の場合は、同じプロジェクトのソースでも色々な理由からRepositoryが別れていて IDEにRepository毎に編集したりするんだけど、どこかに文字列が入ってる。 でもいちいち検索するのが面倒くさいし、全検索は結構メモリ食うんだよね。

ってことでどうせならLinux内に全てあるのであれば検索しちゃえと。

以下コマンドで実現できます。

$ find / -type f -print | xargs grep 'hoge'

どれもよく使うコマンドですね。

find

findの次で指定したディレクトリ以下のファイルを検索する。 これはホントよく使います。 ファイルを検索したい時とか特に。

find / -name '*.php'とかそんな感じで使うとファイルパスが一覧で出力されます。 configとかイジってるのに全然設定変わらない・・・もしかして同じファイル名でどこか別のディレクトリにある!?なんて時に使ったりします。

次に指定しているのが「/」ですね。 これは/以下なので全てのディレクトリを検索することになります。 必要なディレクトリだけであれば「./」とか「/var/」とかフルパスを指定することも可能です。

-print

検索結果を標準出力します。

-type

このoptionでは検索するタイプを指定します。 今回は「f」としているので通常ファイルが対象になります。 その他にも d ディレクトリ l シンボリックリンク という指定もできます。

xargs

これはよく出ますね。 標準入力からコマンドラインを作成して実行します。

grep

これも使います。 ようは検索です。 今回の場合はファイル内の文字列を検索するので、grepの後に検索したい文字列を入れます。

たまに使うのに毎回調べちゃうから忘れないようにメモメモ。

【Parse】 Parse.comのCloudCodeでレコードの保存、取得、変更を行う

さて、前回ParseのCloudCodeについてブログを書きましたが 早速使ってみました。 といっても簡単なものですが保存、検索等よく使うものの簡単な説明です。

もちろんafterSaveやbeforeSave等便利なものは多くあるのですが まずは基本です。

今回はTestって安直なClassを用意しました。 そこに対してSaveして、更にSaveしたものをGetして確認 そこからSaveした内容を変更してみたいと思います。

[TOC]

レコードの保存(Save)

/**
 * save
 */
Parse.Cloud.define("saveTest", function(request, response) {


    var TestClass = Parse.Object.extend('Test');
    var Test = new TestClass();

    var userName = "Ben";
    var age = 18;

    Test.set("name", userName);
    Test.set("age", age);
    Test.save(null, {
        success: function(result) {
            response.success("done!");
        },
        error: function(error) {
            response.error(error);
        }
    });
});

これは基本的なものですね。 userNameとageを定義してそのユーザのデータを作成します。 Test.setにColumn名指定し、そこに入れたいデータですね。 そして最後にsaveするとsetしたデータが作成されます。

実行は以下のコマンドで

curl -X POST
 -H "X-Parse-Application-Id: [Application ID]"
 -H "X-Parse-REST-API-Key: [REST API Key]"
 -H "Content-Type: application/json"
 -d "{}"
 https://api.parse.com/1/functions/saveTest

今回はパラメータの指定はないのでjsonは空にしていますが -dを削っちゃってもOKです。

返り値は以下の通りです。

 # curl -X POST -H "X-Parse-Application-Id: [Application ID]" -H "X-Parse-REST-API-Key: [REST API Key]" -H "Content-Type: application/json" -d "{}" https://api.parse.com/1/functions/saveTest
{"result":"done!"}

成功しているので response.success("done!"); となっているところが実行され、返ってきました。

でもこのコードだと毎回"Ben"ってユーザしか登録されません。 ということでパラメータを渡して登録させましょう。 パラメータを受け取るとコードでは

request.params.xxxx

という形で受け取れます。

コードは以下の通りです。

/**
 * save result.params
 */
Parse.Cloud.define("saveTestRequestParams", function(request, response) {

    var TestClass = Parse.Object.extend('Test');
    var Test = new TestClass();

    var userName = request.params.userName;
    var age = request.params.age;

    Test.set("name", userName);
    Test.set("age", 18);
    Test.save(null, {
        success: function(result) {
            response.success("done!");
        },
        error: function(error) {
            response.error(error);
        }
    });
});

実行は以下です。

curl -X POST
 -H "X-Parse-Application-Id: [Application ID]"
 -H "X-Parse-REST-API-Key: [REST API Key]"
 -H "Content-Type: application/json"
 -d '{"userName": "deadcode", "age": 18"}'
 https://api.parse.com/1/functions/saveTestRequestParams

-dとして空Jsonにしていましたが、jsonにパラメータをつけました。 返り値は以下の通りです。

 # curl -X POST -H "X-Parse-Application-Id: [Application ID]" -H "X-P
arse-REST-API-Key: [REST API Key]" -H "Content-Type: application/json" -d '{"userName":
 "deadcode"}' https://api.parse.com/1/functions/saveTestRequestParams
{"result":"done!"}

さて更に言うと、登録した内容(setした内容)を返したい場合があります。 この場合はsave後にsuccess: function(result)としているところに注目です。 resultには登録した内容が返されます。

以下のようにすると返却内容を見ることができます。

   Test.save(null, {
        success: function(result) {
            response.success(result);
        },
        error: function(error) {
            response.error(error);
        }
    });

上記のように変更して実行してみると返却値は以下のようになります。

# curl -X POST -H "X-Parse-Application-Id: [Application ID]" -H "X-Parse-REST-API-Key: [REST API Key]" -H "Content-Type: application/json" -d '{"userName": "deadcode"}' https://api.parse.com/1/functions/saveTestRequestParams
{"reult":{"__type":"Object","age":18,"className":"Test","createdAt":"2015-05-15T09:05:18.498Z","name":"deadcode","objectId":"cMIr47mjEy","updatedAt":"2015-05-15T09:05:18.498Z"}}

登録された内容がそのまま返却されます。 ここで登録した内容の一部 要はnameとageだけを返却したい場合は以下のようにします。

   Test.save(null, {
        success: function(result) {
            var res = {};
            res.userName = result.get("userName");
            res.age = result.get("age");
            response.success(res);
        },
        error: function(error) {
            response.error(error);
        }
    });

上記のようにすることによって登録した一部の情報を返す事ができます。

レコードの取得(find, first)

では次にsaveした内容をfindしてデータを取得します。 parseではfindとfirstが用意されています。 レコードが一意である場合はfirst, 複数取得する場合はfindを使います。

先ほど取得したデータを取得してみます。 コードは以下の通りです。

/**
 * get find
 */
Parse.Cloud.define("getTestFind", function(request, response) {

    var query = new Parse.Query("Test");

    var userName = request.params.userName;

    query.equalTo("name", userName);
    query.find({
        success: function(results) {
            var res = {};
            res.userName = results[0].get("name");
            res.age = results[0].get("age");
            response.success(res);
        },
        error: function(error) {
            response.error(error);
        }
    })
});

今回findを使ってのデータ取得ですが saveとの違いはquery部分です。 今回はqueryとしてParse.Queryを使ってテーブル(Class)指定をします。

更にそのqueryでequalToを使って検索しています。 ここに関しては色々と使えるのですが、次の時にでも書きます。

また、success: function(results)として受け取ってるresultsに検索結果が配列で返ってきます。 findに関しては一致するレコードを全て取得します。 今回は1レコードしかないので直接配列の0番目を指定しています。

また、返却されたresultsの値を取る場合はgetを使ってカラム名を指定することによって取得できます。

実行してみましょう。

curl -X POST
 -H "X-Parse-Application-Id: [Application ID]"
 -H "X-Parse-REST-API-Key: [REST API Key]"
 -H "Content-Type: application/json"
 -d '{"userName": "deadcode"}'
 https://api.parse.com/1/functions/getTestFind

今回はuserNameを先ほど登録した"deadcode"として指定します。 返り値は以下のようになります。

 # curl -X POST -H "X-Parse-Application-Id: [Application ID]" -H "X-Parse-REST-API-Key: [REST API Key]" -H "Content-Type: application/json" -d '{"userName": "deadcode"}' https://api.parse.com/1/functions/getTestFind

{"result":{"age":18,"userName":"deadcode"}}

ちゃんと返ってきましたね。 firstについてはCakeと同じように1レコードのみ取得してきます。

コードはfindをfirstに変更するだけです。

また、違いとしてはfindは配列で返ってくるのに対して firstは1レコードだけ持ってくる為、getで取得する時にresuls[0]とfindでは配列のキーを指定していましたが findではresults.getという形で取得します。

レコードの変更

次はレコードを変更してみましょう。 既に登録してあるレコードがあるのでそこを変更します。 変更方法はいくつかあるのですが、以下のようなコードを書きました。

/**
 * updateTest
 */
Parse.Cloud.define("updateTest", function(request, response) {

    var query = new Parse.Query("Test");

    query.equalTo("name", request.params.userName);
    query.first({
        success: function(result) {
            var TestClass = Parse.Object.extend("Test");
            var Test = new TestClass();

            result.set("age", request.params.age);
            result.save();

            response.success("done!!");
        },
        error: function(error) {
            response.error(error);
        }
    });
});

手順としてはfirstで変更したいレコードを取得して上書き保存みたいな形で変更しています。 firstで実行して取得したレコードにsetを使ってsaveすることによって上書きができます。

実行してみましょう。

curl -X POST
 -H "X-Parse-Application-Id: [Application ID]"
 -H "X-Parse-REST-API-Key: [REST API Key]"
 -H "Content-Type: application/json"
 -d '{"userName": "deadcode", "age": 20}'
 https://api.parse.com/1/functions/updateTest

返り値は以下の通りです。

# curl -X POST -H "X-Parse-Application-Id: [Application ID]" -H "X-Parse-REST-API-Key: [REST API Key]" -H "Content-Type: application/json" -d '{"userName": "deadcode", "age": 20}' https://api.parse.com/1/functions/updateTest
{"result":"done!!"}

以上でParseのCloudCodeで保存、取得、変更の基本が完了です。 他にも色々とか書きたい事、やってみたい事があるのですが、それは次の機会にでも。

【Parse】 最近流行りのMBaaS BaaSの Parse.com を触ってみた。

Baas MBaaSなんて言葉を最近良く聞くようになりました。 これは(Mobile) Backend as a Service の略称になり IaaSとかPaaSなんかのように自分でバックエンドのコードを書いて実装する必要がありますが BaaSやMBaaSについてはSDKAPIでなんとかしちゃおう的な感じです。

要はクライアントサイドは作れる、View周りのコーディングだってできるというフロントの方々が動的なページが必要だったり、システム部分が必要になったらサーバサイドの開発が必要。

更に言うと、PHPやNode.js、Javaだってできるぜって方でもLinux(でなくてもいいけど)環境を構築できるかというと色々とスキルが必要になってきます。

そこでBaaSです。

今回から何度かに渡って最近使ってるParseについて書いていければなと思います。 さて今回はParseとはなんぞやってところからなのかなと。

一言で言うと、サーバ側のコードを書かずにデータストアへのアクセスからプッシュ通知、アカウント管理機能まで付いている 結構便利なやーつ。

しかもチュートリアルも簡単で直感的操作で簡単にできるっていうのが嬉しい。

だがしかし。

あくまで技術ブログなので少しくらい弄りますよ。 ってことで、Parseはサーバ側にCloudCodeというものを備えており、JavascriptAPIを書くことができる。 ここについて少し書いていこうかと思います。

今回はインストールからCloudCodeにソースをアップして確認するところまで。

まずはParseを登録してCreateAppしておいてください。Appの名前はなんでもいいです。 今回は適当に「test」というアプリを作成しました。 まずはAPIを用意するだけなので、Core(ストレージ)でClassを作ってとかいうのは次にでも。

さてそしたら次はLinux側でParseをインストールします。 もちろんMacUnix環境でも可能です。

$ curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash

公式に書いてあることそのままですが、parseは/usr/local/bin/parseにインストールされ 付随しているファイルもないので、アンインストールは/usr/local/bin/parseを削除するだけです。

次に、プロジェクトを作成します。 自分はとりあえず、/home/[ユーザ名]以下にparseというディレクトリを作り、その中で作業をすることにしました。

$ mkdir parse

早速Cloud Codeのセットアップをしていきます。 以下コマンドを実行して対話形式に行っていきます。

$ parse new test

すると、まずは自動的に必要ディレクトリが作成され、Email、Passwordを求められます。 Email,PasswordについてはParseのログイン時のものを入力します。

Creating a new project in directory /home/[user_name]/parse/test
Creating directory /home/[user_name]/parse/test/config
Creating config file /home/[user_name]/parse/test/config/global.json
Creating directory /home/[user_name]/parse/test/cloud
Writing out sample file /home/[user_name]/parse/test/cloud/main.js
Creating directory /home/[user_name]/parse/test/public
Writing out sample file /home/[user_name]/parse/test/public/index.html
Email: deadcode.ben@gmail.com
Password: 

入力が完了するとParseで作ったアプリケーションの一覧が出てきます。 testの他にもaaaaaという適当なアプリも作ったので2つ出てきています。 どのアプリケーションのAPIを作るかなので、今回はtestを選択します。

1:      test
2:      aaaaa
Select an App: 1
Set test as default
Written config for test

これでCloudCodeのセットアップは完了です。

ここから作成されたディレクトリを見て行きましょう。

test
├── cloud
│   └── main.js
├── config
│   └── global.json
└── public
    └── index.html

3 directories, 3 files

少な! 今回お世話になるのは test/cloud/main.jsになります。

こちらを早速開いてみます。

// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

既にサンプルコードが書かれていますね。 とりあえずHello world!って表示されそうなものですね。

細かい説明は次にして、早速ちょっと変更してアップしてみましょう。

response.success("Hello world!");

ここの記述を

response.success("Hello parse.com world!");

こんなふうにしてみました。

早速このソースをParseにアップしましょう! まずはtestディレクトリまで移動します。

cd /home/[user_name]/parse/test

そこからDeploy!

$ parse deploy test
Uploading source files
Deploying recent changes to scripts...
Deploying recent changes to hosting...
Finished uploading files
New release is named v1 (using Parse JavaScript SDK v1.4.2)

ここではparse deploy testとアプリケーション名を指定していますが 特に指定しなくても大丈夫です。

ということでこれでアップが完了しました。

次は確認をします。

curl -X POST \
  -H "X-Parse-Application-Id: [Application ID]" \
  -H "X-Parse-REST-API-Key: [REST API Key]" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://api.parse.com/1/functions/hello

こんな感じでCurlでアクセスしにいきます。 [ApplicationID] [Rest API Key] この二点については新しくAppを作成した時に表示されていました。 覚えてない場合はDocsに行くと、App毎のIDやKeyが書かれた状態での上記コマンドがあるのでそれを使いましょう!

参考:https://parse.com/docs/jp/cloud_code_guide

さて、これを叩くと

[root@localhost test]# curl -X POST \
>   -H "X-Parse-Application-Id: 9hEaqooViTltgUK5lBZrqGmv8kZQyY5MVflITDzQ" \
>   -H "X-Parse-REST-API-Key: CaSYfQGd4tdHZz269qwrOONAeyVvEqxIoxNHcd2r" \
>   -H "Content-Type: application/json" \
>   -d '{}' \
>   https://api.parse.com/1/functions/hello
{"result":"Hello parse.com world!"}

こんな感じで出てきます。 最終行に注目してください。

{"result":"Hello parse.com world!"}

先ほど設定した内容がresultとして出力されました。

こういった形でmain.jsに処理を書くことによって少し複雑な事だってできるのです。

今後はここから更にAppのClassよりデータを取得したり、新規にデータ作成、保存したりするような事を書いていければと! 新しい事を覚えるのは楽しいですねー。