Do You PHP はてな このページをアンテナに追加 RSSフィード Twitter

2015-07-29

[]PHP 7 Compatibility Checker(php7cc)を触ってみた

珍しくPHPネタを連投してます;-)

β2がリリースされてだいぶ実体が見えてきたPHP7ですが、PHP5.xから多くの改良、機能の追加・非推奨化・廃止、また仕様変更が含まれています。既存のコードがPHP7で動作するかどうかを

で、既存のコードがPHP7で動作するかどうかをチェックするツールGitHubで公開されていましたので、ちょっと触ってみました。

php7cc is a command line tool designed to make migration from PHP 5.3-5.6 to PHP 7 easier. It searches for potentially troublesome statements in existing code and generates reports containing file names, line numbers and short problem descriptions. It does not automatically fix code to work with the new PHP version.

sstalle/php7cc

Introductionにもあるとおり、あくまでlintのような"コードチェッカー"であり、コードを修正してくれるツールではありません。また、PHP7はまだ開発中なのですべての変更点を洗い出せるわけではないことに注意です。

環境

  • CentOS6.4
  • PHP5.6.11

インストール

README.mdのInstallationに書かれているとおり、composer経由でインストールします。

$ PATH=/usr/local/lib/php56/bin:$PATH php ./composer.phar create-project sstalle/php7cc php7cc --stability=dev
        :
Writing lock file
Generating autoload files
Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]? n
$ 

インストールの最後にVCSの履歴を削除するかどうかを聞かれます。ここで"no"としておけば、今後git pullで更新できるようになるようです。

チェック対象のPHPスクリプト

PHP7.0.0β2に含まれているUPGRADINGファイルに記載されているコードを使ってチェック用のスクリプト(UPGRADING.php)を作ってみました。

<?php
/**
 * @see https://raw.githubusercontent.com/php/php-src/php-7.0.0beta2/UPGRADING
 */

/**
 * Indirect variable, property and method references are now interpreted with
 * left-to-right semantics. Some examples:
 */
$$foo['bar']['baz']; // interpreted as ($$foo)['bar']['baz']
$foo->$bar['baz'];   // interpreted as ($foo->$bar)['baz']
$foo->$bar['baz'](); // interpreted as ($foo->$bar)['baz']()
Foo::$bar['baz']();  // interpreted as (Foo::$bar)['baz']()


/**
 * The global keyword now only accepts simple variables. Instead of
 */
global $$foo->bar;


/**
 * Parentheses around variables or function calls no longer have any influence
 * on behavior. For example the following code, where the result of a function
 * call is passed to a by-reference function
 */
function getArray() { return [1, 2, 3]; }

$last = array_pop(getArray());
// Strict Standards: Only variables should be passed by reference
$last = array_pop((getArray()));
// Strict Standards: Only variables should be passed by reference


/**
 * Array elements or object properties that are automatically created during
 * by-reference assignments will now result in a different order.
 */
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
    :

全体はGistにありますので参照してみてください。

実行してみる

インストール後に生成されるphp7ccディレクトリ直下にbinディレクトリがあり、その中にあるphp7cc.phpファイルを実行します。引数PHPスクリプトを含むパスもしくはファイル名を指定します。パスを指定した場合は、第2引数拡張子(カンマ区切りで複数指定可能)できます。

$ PATH=/usr/local/lib/php56/bin:$PATH php ./php7cc/bin/php7cc.php ./UPGRADING.php

File: ./UPGRADING.php
Line 7. Indirect variable, property or method access: /**
 * Indirect variable, property and method references are now interpreted with
 * left-to-right semantics. Some examples:
 */
${$foo['bar']['baz']};
Line 8. Indirect variable, property or method access: // interpreted as ($$foo)['bar']['baz']
$foo->{$bar['baz']};
Line 9. Indirect variable, property or method access: // interpreted as ($foo->$bar)['baz']
$foo->{$bar['baz']}();
Line 10. Indirect variable, property or method access: // interpreted as ($foo->$bar)['baz']()
Foo::$bar['baz']();
Line 16. Complex variable without curly braces in global keyword: // interpreted as (Foo::$bar)['baz']()
/**
 * The global keyword now only accepts simple variables. Instead of
 */
global ${$foo->bar};
Line 37. Possible array element creation during by-reference assignment: $array['a'] =& $array['b'];
Line 53. Empty list assignment: /**
 * Empty list() assignments are no longer allowed. As such all of the following
 * are invalid:
 */
list();
Line 54. Empty list assignment: list(, , );
Line 55. Empty list assignment: list();
Line 99. Possible adding to array on the last iteration of a by-reference foreach loop: $array[1] = 1;
Line 109. Duplicate function parameter name "unused": public function foo($a, $b, $unused, $unused)
{
}
Line 122. Function argument(s) returned by "func_get_arg" might have been modified: func_get_arg(0);
Line 142. Invalid octal literal 0781: 7;
Line 172. String containing number in hexadecimal notation: '0x123';
Line 173. String containing number in hexadecimal notation: '0x123';
Line 174. String containing number in hexadecimal notation: '0xe';
Line 174. String containing number in hexadecimal notation: '0x1';
Line 176. String containing number in hexadecimal notation: '0x1';
Line 184. String containing number in hexadecimal notation: '0xffff';
Line 197. Unicode codepoint escaping "\u{xyz}" in a string: '\\u{xyz}';

Checked 1 file(s) in 0.048255 second(s)
$ 

用意したPHPスクリプトのすべてではないですが、結構いい感じにチェックできているんじゃないでしょうか?

ちょっとだけ深堀り

PHPスクリプトの解析にはPHP Parserが使われています。php7cc側では、PhpParser\NodeVisitorAbstractクラスを継承したAbstractVisitorクラスが用意されており、それを継承した各Visitorクラスで「何をチェックするか」が実装されています。

たとえば、func_get_arg/func_get_args関数戻り値の変更に対するチェッカー(php7cc/src/NodeVisitor/FuncGetArgsVisitor.php)は次のような感じです。

<?php

namespace Sstalle\php7cc\NodeVisitor;

use PhpParser\Node;
use Sstalle\php7cc\Helper\NodeHelper;

class FuncGetArgsVisitor extends AbstractVisitor
{

    public function enterNode(Node $node)
    {
        if (!NodeHelper::isFunctionCallByStaticName($node)) {
            return;
        }

        /** @var Node\Expr\FuncCall $node */
        $functionName = $node->name->toString();
        if ($functionName == 'func_get_arg' || $functionName == 'func_get_args') {
            $this->addContextMessage(
                sprintf('Function argument(s) returned by "%s" might have been modified', $functionName),
                $node
            );
        }
    }
}

まとめ

今年中には正式リリースされる感じのPHP7ですが、こういうツールを上手く使って楽に移行をしたいですね(希望的観測)。。。(汗)

というか、サードパーティ製の拡張モジュールが対応してくれるのか不安だなぁ。。。

2015-07-24

[]GoPHP7とGoPHP7-ext

何だかGoPHP5を思い出しますが、すでにGoPHP7のサイトがあるようです。

TOPページにはリリースバージョンが表示されるだけでリンクも何もないんですが。。。

で、各種拡張モジュールのPHP7への対応状況をまとめた情報も公開されています。

ここの「Catalog」タブをクリックすると、対応状況一覧へのリンクが現れます。

これを見てみると、コアモジュールはかなりの割合でPHP7に対応しているようです。一方で、Xdebugmemcached、runkitなどのサードパーティ製モジュールはまだまだのようですね。

とりあえず、ssh2とyaml拡張モジュールについてはPHP7.0.0β2との組み合わせで問題なくbuildできることを確認しました。

2015-05-21

[]Motorola Moto 360を買った

すでに1ヶ月前になりますが、MotorolaMoto 360を購入しました。

f:id:shimooka:20150521122355j:image

注文から届くまで

購入先はネットのコンビニAmazon.comです。「.co.jp」ではなく「.com」ですが、日本への発送も対応しています。

Motorola Moto 360 - Light Metal, 23mm, Smart Watch

f:id:shimooka:20150508120845j:image

値段は本体+送料+税で256.29USD(31,983円)。

バンドは23mmのメタルバンド(Light Metal)を選択。レザーバンドは昔夏場にひどく肌が荒れたことがあって、それ以来敬遠しています。また、18mmバンドではなくなぜ23mmにしたか?なんですが、18mmバンドと比較して注文時点で60USDも値段に差があるとさすがに厳しいです(汗)

f:id:shimooka:20150521122407j:image

注文から到着までの流れですが、

  • 注文:2015/04/14
  • 発送:2015/04/15
  • 到着:2015/04/18

と言った具合で、思ったよりも全然早く到着しました。ただし、発送先の氏名が何故かメールアドレスになってしまっていたため、入管で1〜2日ほど止まっていたっぽいです(泣)

で、荷物はこんな感じ。Amazon.comのダンボールに入って到着。

f:id:shimooka:20150418102122j:image

化粧箱はこんな感じです。

f:id:shimooka:20150418102814j:image

到着が揃うよう、保護フィルムとQi充電パッドも別途注文しておきました。

f:id:shimooka:20150418102841j:image

開封の儀

開封の儀は。。。写真を撮ってあったんですが見つからないのでこちらを参照してください;-)

Moto360日本購入レビュー。初期設定方法やバッテリーの持ちなどの使用感。 - Android(アンドロイド)おすすめアプリ/格安SIM/SIMフリースマホ情報|アンドロイドラバー

保護フィルムの貼り付け

ディスプレイ面が割れるとどうしようもないので、とりあえず高硬度のガラスフィルムを選択しました。

0.3mmとは言うものの、思ったよりも厚みがありました。まあ、Moto 360自体が思ったより厚みがあるので誤差範囲でしょう。貼り付けはスマートフォンと同様ですが、フィルムが円形なので位置合わせはそこまで難しくなかったです。

Moto 360のセットアップ

セットアップについても先ほどのサイトに詳しく書いてありますが、ハマる点としては

  • Android端末側にAndroidWearアプリインストールする必要がある
  • ペアリング完了後の処理に結構時間がかかるので、充電してからセットアップした方が良いかも(20〜30%ぐらいしか電池残量がなかった)

の2点です。

OSアップデート

セットアップが終わったら、OSを最新版に更新。つい先日Android Wear 5.1.1へのアップデートが始まりましたが購入当時では5.0.2がリリースされていました。

アップデートは、Moto 360側のメニューの

設定→端末情報→システムアップデート

から行います。アップデート後のソフトウェアバージョンが更新されていれば成功です。

f:id:shimooka:20150521130759p:image

で、通常はあまりないとは思いますが、Adblock Plus for Android等の広告フィルタAndroid FirewallなどのiptablesアプリををAndroid端末にインストールしている場合、アップデートファイルをダウンロードできない場合があります(というか、ハマった)ので、ダウンロードできない場合は

$ adb logcat

などでAndroid端末のログを確認してみてください。

開発者向けオプションメニューの有効化

Android端末をそれなりに使ってる人にはピンと来ると思いますが、Android Wearにも開発者向けオプションメニューがあり、有効にするとBluetooth経由でのadbデバッグなどが可能になります。

有効化する手順もAndroid端末と同様、端末情報メニューにある「ビルド番号」を連続タップするというものです。

f:id:shimooka:20150521130757p:image

メタルバンドの長さ調整

メタルバンドの長さですが、個人的には若干長めだったので、ヨドバシアキバの3Fにある時計修理コーナーでやってもらいました。所要時間は約10分で500円程度でした。一瞬、工具を買って自分でやろうかと思ったんですが、頻繁に調整するものでもないし素直に時計屋さんでやってもらった方が良いと思います。

インストールしているWearアプリ

あちこちで紹介されているものから個人的な好みまで、有料・無料問わずいくつか入れてあります。

  • Wear Mini Launcher
    • スワイプするとアプリドロワーを表示するアプリ。何でこの機能が標準でないのか不明なぐらい必須アプリAndroid Wear 5.1.1から機能的に追加されるらしい。また、Androidのクイック設定パネルのようにWi-FiテザリングのON/OFF、ボリューム調整などのメニューもある。
  • Slumber for Android Wear
    • 必須アプリ。充電中にスクリーンをオフにするアプリMoto 360は充電中にスクリーンオフにならず、これによるディスプレイの焼き付きが問題となっている(いた?)模様。対策としてScreen Off Wearがよく紹介されているが、現時点でGooglePlayでは公開されていないためこちらで代用。機能的には必要十分。
  • Feel The Wear
    • 必須アプリMoto 360のバイブレーションは結構弱くて、設定を変更できるアプリがないか物色していたところで見つけたアプリ。振動パターンは有料版だとカスタマイズ可能(無料版は7種類のプリセットから選択)で、アプリごとにパターンを変更したり設定を適用しない曜日や時間帯も設定できる。
  • Wear Battery Stats
    • Android Wearのバッテリー残量を表示するアプリAndroid端末側だけではなく、Android Wear側でもグラフを表示できる。確認したところ、だいたい1時間あたり3〜6%ぐらいのバッテリー消費ペースなので、通常なら朝から夜まではバッテリーが保つ計算。ただし、Moto 360側で設定変更などをガシガシやっているとあっという間にバッテリーがなくなるので、予備のQi充電器を職場に用意するなどの対策は必要。

f:id:shimooka:20150521133958p:image

今のWatch Face

Watch Face - Minimal & Elegantを使ってます。

f:id:shimooka:20150521122416j:image

時間ごとに背景のグラデーションを変更できるのと、Little Worldsというちょっとした画像を表示できディスプレイ下のデッドスペースをうまく目立たなくさせられるのがポイント。アナログ時計じゃないので視認性はあまり良くないですが、かなりシンプルで個人的には好みです。

f:id:shimooka:20150521130758p:image

1ヶ月使ってみてどうだったか?

主に平日に朝から晩まで付けてみた感想です。

  • メタルバンドはいい感じのズッシリ感。今では、これぐらい太めのメタルバンドでも意外と良いんじゃないか?という感じ。
  • セットアップ後に操作チュートリアルがあるんだけど、あれだけで操作を覚えられる人いるのかな?最初は結構「あれ?あれれ?」な感じで操作してましたが、だいたいがタップ/ロングタップ/上下左右へのスワイプのどれかなので「あとは身体で覚えろ」的な感じがする。
  • 携帯を見なくても、手元で通知が判る&内容を確認できるのは非常に良い
  • 音声による操作は意外と認識精度が高くて便利。ただし、「腕時計に向かっていきなり話しかける」というシチュエーションは日本人にとってはハードルが高い(と思う)。
  • 手元でAndroid端末の操作(テザリングとか)ができるアプリインストールしておくと良い
  • Watch Faceを自分好みに変更できるが、種類がホントに多すぎて迷う
  • アプリによっては通知が鬱陶しいほど来るので、通知ブロックをうまく使う必要がある
  • Bluetoothの接続距離が思ったより狭いので、Android端末との接続が意外と切れる
  • Fitness機能はあまり意識してません。たまに心拍数を計るぐらいかなぁ。。。

まとめ

Androidガジェット」ということで遅ればせながら飛びついてみた感満載なわけですが、個人的には買って損はなかったかな?と思います。

ガジェットっぽくないデザインも良いし、当初予想していた通り通知機能に関しては非常に便利です。ただ、単なる「通知専用ガジェット」ならコストパフォーマンスが見合わないですかね。また、今のところAndroid端末との連携がほぼ前提になっているので、Android Wear側からAndroid側を操作できるといろいろと助かる場面が増える感じはしますが、これらもキラーアプリ/コンテンツにはなり得ないような気がします。

まあ、ここでウダウダ書かなくても皆さん何か別の「圧倒的に便利なこと」を模索してるんだろうし、これからどうなっていくかを焦らず見て行きましょうかね。何かアイデアを思いついたらWearアプリも作ってみたいし:-)

あ、あとAndroid Wear 5.1.1には期待してますよ!(が、まだ降って来てない)

2015-05-13

[]PHPからInfluxDBに繋いでみた

最近PHPからいろいろなデータベース(RDBMS以外も)に繋ぐことが多くなっていた中で、InfluxDBにも繋ぐ必要が出てきたのでざっくりまとめてみました。

間違ってる所があれば、指摘をお願いします;-)

基本的な環境

  • CentOS 6.4(x86_64, ClouderaのQuickStartVM)
  • PHP 5.6.8
  • InfluxDB 0.8.8(rpm)

InfluxDBとは

InfluxDBは時系列データを格納するためのオープンソースなデータストアで、Goで書かれています。

no title

とりあえず、以下のページをざっと読むと良いかと。

2015/05/12現在、最新版は0.8.8で0.9をそろそろリリースするとアナウンスされています(が、なかなか出てきません。。。)。

特徴としては、以下の様なものがあります。

  • クラスタ環境も構築可能
  • SQLに似た問い合わせ言語が用意されており、基本的な集約関数やJOINも利用可能。
  • HTTP(S) APIJSONが強力で、データ登録や問い合わせだけでなくデータベース作成などいろいろ操作できる
  • 管理画面が組み込みで用意されている

インストール

RedHatCentOS向けにrpmが用意されているので、それをインストールするのが楽チンです:-)

$ sudo rpm -ivh http://s3.amazonaws.com/influxdb/influxdb-latest-1.x86_64.rpm

InfluxDB関連のファイルは、/opt/influxdbに配置されます。設定ファイルは/opt/influxdb/shared/config.tomlです。設定オプションは、以下を参照してください。

InfluxDBを起動・停止してみる

起動・停止は、serviceコマンドで行います。

$  sudo service influxdb start
Setting ulimit -n 65536
Starting the process influxdb [ OK ]
influxdb process was started [ OK ]
$ 

起動後、8083、8086、8090、8099の4ポートでLISTENしていることを確認して下さい。

$ sudo netstat -tnlp | grep -P "80(83|86|90|99)"
tcp        0      0 0.0.0.0:8083                0.0.0.0:*                   LISTEN      12943/influxdb
tcp        0      0 0.0.0.0:8086                0.0.0.0:*                   LISTEN      12943/influxdb
tcp        0      0 0.0.0.0:8090                0.0.0.0:*                   LISTEN      12943/influxdb
tcp        0      0 0.0.0.0:8099                0.0.0.0:*                   LISTEN      12943/influxdb
$ 

LISTENできていない場合は、/opt/influxdb/shared/log.txtを確認してみてください。

管理画面を使ってデータベース作成〜データ登録する

まずはデータベースの作成を行います。HTTP(S) APIでもデータベースを作成できますが、とりあえず用意されている管理画面からデータベースを作成してみます。

1. ログインする

ブラウザから、http://[ホスト名]:8083/ にアクセスするとログイン画面が表示されます。

f:id:shimooka:20150512140857p:image

デフォルトのusernameとpasswordは"root"ですので、入力し"connect"ボタンを押下します。

2. データベースの作成

ログインすると、以下の様なデータベース一覧画面が表示されます。

f:id:shimooka:20150512143930p:image

とりあえず、"test"というデータベースを作ってみました。ここでは、データベース名に"test"と入力し、右下の"Create Database"ボタンを押下します。ここでは割愛しますが、Shard Specsではデータの保存期間やクラスタ構成時の分割数を指定します。詳細は、以下を参照してください。

データベース作成が成功すると、一覧に作成済みのデータベースが表示されます。

f:id:shimooka:20150512143931p:image

3.データベース接続用のユーザーを登録する

データベースを作成したら、データベース接続用のユーザーを追加しましょう。

先ほどの画面に表示されているデータベース一覧からデータベース名"test"をクリックすると、ユーザーの登録画面になります。

f:id:shimooka:20150513115011p:image

接続用ユーザーのユーザー名とパスワードを入力し、"Create"ボタンを押下します。

4.データを登録する

ここまでできたら、データを登録してみましょう。

画面上部のメニューの"Databases"をクリックすると先ほどのデータベース一覧画面になりますので、"Explore Data"リンクをクリックします。すると、データの参照・登録画面になりますので、下にある"Write Point"からデータを登録します。

f:id:shimooka:20150512143932p:image

InfluxDBには、RDBMSのテーブルのように"シリーズ(Series)"と呼ばれるテーブル的なものがあります。データはオブジェクトJSON形式で1レコードに相当します。この時、キーがRDBMSでいうカラム名に相当します。フォーマットの詳細は、以下を参照してください。

まずは、

  • Time Series Name:series01
  • Values:{"event": "test", "value": "123"}

と入力し、"Write Point"ボタンを押下します。

f:id:shimooka:20150512143933p:image

これでボタンの右に"200 OK"と表示されればデータ登録完了です。続けて何件かデータを登録します。

5.問い合わせを実行してみる

データをある程度登録したら、先ほどの画面の上にある"Read Points"から問い合わせを実行してみます。

"Query"に

SELECT * FROM series01

と入力し、登録済みのデータが全件出力されればOKです。

f:id:shimooka:20150512183920p:image

この時、データとしては登録していないはずの

  • time
  • sequence_number

というカラムも出力されていると思いますが、データ登録した日時のタイムスタンプ(ms単位)とシーケンス番号になります。これはInfluxDBが付与したもので、これら2カラムでユニークになります。

その他集約関数やデータ削除、シリーズの削除については、以下を参照してください。

InfluxDB-CLI

「管理画面もいいけど、やっぱ黒い画面よね」という方向けにInfluxDB-CLIというものもあります。

今のところ、Go/Ruby/Node.js製のものがあるようですが、今回はNode.js製のものをインストールしてみました。

$ sudo yum install -y nodejs npm --enablerepo=epel
$ sudo npm install influxdb-cli -g
$ 

コマンドは"influxdb-cli"です。

$ influxdb-cli --help
InfluxDB SQL CLI
Usage: influxdb-cli

Options:
  -h, --hostname  Host [%s]                             [default: "localhost"]
  --port          Port [%s]                             [default: 8086]
  -s, --secure    Use HTTPS if flag is present          [default: false]
  -u, --user      User [%s]                             [default: "root"]
  -a, --auth      HTTP basic auth [%s]                  [default: ""]
  -p, --password  Password [%s]                         [default: "root"]
  -d, --database  Database [%s]                         [default: "db"]
  --pretty        Display time in a human readable way  [default: false]

$ influxdb-cli --database=test
test> Connecting to http://localhost:8086/db/test ...
test> &#10004; ready
test> select * from series01;
┌────────┬────────┬────┬───┐
│ time          │ sequence_number │ value │ event │
├────────┼────────┼────┼───┤
│ 1431424603006276961510001789test  │
│ 14314090029592769614900013     │ baz   │
│ 14314089986662769614800012     │ bar   │
│ 14314089836682769614700011     │ foo   │
│ 1431408976749276961460001456test  │
│ 1431408770502276961450001123test  │
└────────┴────────┴────┴───┘
Query took  9 ms
test>

やっぱこっちの方が扱いやすいですね:-)

PHP+InfluxDB

ようやくPHPからInfluxDBに繋げてみます(汗)

InfluxDBには各言語向けのクライアントライブラリが公開されていますので、それを使います。

2015/05/12現在、InfluxDB 0.9用と0.8.x用が公開されていますが、今回は後者を利用します。インストールはcomposerで。

$ vi composer.json
$ cat composer.json
{
    "require": {
        "crodas/influx-php": "0.1.*"
    }
}
$ ./composer.phar install
$ 

続いて、以下の様なテスト用スクリプト(test.php)を作成します。接続先のポート番号は8086です。

<?php
require_once __DIR__ . '/vendor/autoload.php';

define('INFLUXDB_HOST', 'localhost');
define('INFLUXDB_PORT', 8086);
define('INFLUXDB_NAME', 'test');
define('INFLUXDB_USER', [接続用ユーザーのユーザー名]);
define('INFLUXDB_PASS', [接続用ユーザーのパスワード]);

$client = new crodas\InfluxPHP\Client(INFLUXDB_HOST, INFLUXDB_PORT, INFLUXDB_USER, INFLUXDB_PASS);
$db = new crodas\InfluxPHP\DB($client, INFLUXDB_NAME);
$query = "SELECT * FROM series01";
try {
    var_dump($db->query($query));
} catch (Exception $e) {
    var_dump($e->getMessage());
}

で、実行。管理画面から確認したデータが取得できていればOKです。

$ php -v
PHP 5.6.8 (cli) (built: May  8 2015 14:17:31)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.4-dev, Copyright (c) 1999-2015, by Zend Technologies
    with Xdebug v2.2.7, Copyright (c) 2002-2015, by Derick Rethans
$ php test.php
class crodas\InfluxPHP\Cursor#4 (1) {
  private $storage =>
  array(6) {
    [0] =>
    class stdClass#5 (4) {
      public $time =>
      int(1431424603)
      public $sequence_number =>
      int(276961510001)
      public $event =>
      string(4) "test"
      public $value =>
      string(3) "789"
    }
    [1] =>
    class stdClass#6 (4) {
      public $time =>
      int(1431409002)
      public $sequence_number =>
      int(276961490001)
      public $event =>
      string(3) "baz"
      public $value =>
      string(1) "3"
    }}
$ 

データのバックアップと移行

開発用環境など「とりあえずそれなりの量のデータが入っていれば良い」という状態にしたい場合、opt/influxdb/shared/data以下のファイルをコールドバックアップ&リストアすることで、データを移行することができます。

ただし、データベースのRetensionの設定によってはデータ保持期限切れでデータが消えてしまうので、移行後にHTTP(S) APIを使ってデータベースの設定を変更する必要があるかもしれません。

$ sudo service influxdb stop
$ sudo \rm -rf /opt/influxdb/shared/data
$ sudo tar zxf data.tgz -C /opt/influxdb/shared/
$ curl -X POST "http://localhost:8086/cluster/shard_spaces/test/test?u=root&p=root" -d '{ "retentionPolicy": "inf" }'
$ sudo service influxdb start
$ 

あとは、バックアップ/リストアツールもいくつかと公開されているようなので、それを使ってみるのも手かもしれません。

まとめ

またもや「ちゃんと繋げられましたよー」で終わっちゃいますが。。。(汗)

InfluxDBへの接続自体はライブラリのお陰でほとんど苦労することなくできると思います。

あと、SQLっぽい問い合わせ言語が使えるのはメリットが大きいんですが、結構クセがあるので「えっ?」ということが多いことに注意です。

2015-05-12

[]JsViewsチュートリアル - データが更新された時に独自のイベントハンドラを実行する

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでデータリンクしたデータの更新を監視し、独自のイベントハンドラを実行する。

コード

<html>
<body>
<div id="list"></div>
<hr/>
<button id="code">update code</button>
<button id="name">update name</button>
<button id="color">update color</button>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="js/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// リンクされたデータの更新を監視し、イベントハンドラを実行する
$(function() {
    // データのオブジェクト
    var application = {
            log: [],
            fruits: {
                code: "01",
                name: "りんご",
                color: '赤'
            }
        };

    // テンプレートに名前をつけて登録する
    $.templates({
        pathTemplate: "#template"
    });

    // テンプレートlinkTemplate(#template)と変数applicationをリンクさせ、
    // レンダリング結果をid="list"に表示
    $.link.pathTemplate("#list", application);

    $(document)
        .on('click', '#code', function(ev) {
            $.observable(application.fruits).setProperty('code', application.fruits.code + '9');
        })
        .on('click', '#name', function(ev) {
            $.observable(application.fruits).setProperty('name', application.fruits.name + '!');
        })
        .on('click', '#color', function(ev) {
            $.observable(application.fruits).setProperty('color', application.fruits.color + '色');
        })

    // application.fruitsのnameプロパティもしくはcolorプロパティが
    // 更新された場合にハンドラが実行される
    $.observe(application, 'fruits.name', 'fruits.color', function(ev, args) {
        $.observable(application.log).insert([args.path + 'が' + args.value + 'に更新されました']);
    });
});
</script>

<script id="template" type="text/x-jsrender">
    <div>
        <p><span data-link="fruits.code"></span>:<span data-link="fruits.name"></span>(<span data-link="fruits.color"></span>)</p>
    </div>
    <div>
        {^{for log tmpl="#template_log"/}}
    </div>
</script>
<script id="template_log" type="text/x-jsrender">
    <li><span data-link="#data"></span></li>
</script>
</body>
</html>

説明

データリンクしたデータが更新されたタイミングで、ログ出力や視覚的なエフェクトをかけるなど何らかのハンドラを実行したい場合があるかと思います。JsViewsではこのための仕組みも用意されています。

$.observe([対象の変数], [プロパティへのパス1], [プロパティへのパス2], ... , [イベントハンドラ])

プロパティへのパスは1つ以上指定可能で、「対象の変数」が基準となります。先ほどのコード例では、application.fruitsオブジェクト直下にあるnameプロパティもしくはcolorプロパティが更新された場合にログ出力するイベントハンドラが実行されます。

プロパティへのパスですが、ワイルドカードが使えます。例えば、application.fruitsオブジェクト直下にあるすべてのプロパティが対象であれば、

$.observe(application, 'fruits.*', function(ev, args) {});

といった具合になります。

また、イベントハンドラ引数を2つ取ります。1つ目はjQueryのイベントオブジェクト、2つ目はJsViewsのイベントオブジェクトで更新前後の値やどういう更新を行ったのか?という情報が含まれます。

また、対象の変数に存在するすべてのプロパティに対してイベントハンドラを割り当てたい場合は、次の書式が利用できます。

$.observable([対象の変数]).observeAll([イベントハンドラ])

JSFiddleで動作を見る

参照

2015-04-10

[]JsViewsチュートリアル - データリンクしたオブジェクトメソッドを呼び出す

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

JsViewsでデータリンクしたオブジェクトメソッドを呼び出す。この際、データリンクした値が変更された場合にメソッド戻り値を再評価させてみる。

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
// リンクされたオブジェクトのメソッドを呼び出す
$(function() {
    var data = {
        a: 'foo',
        b: 'bar',
        unlinkedFunction: function() {
            return 'hoge';
        },
        linkedFunction: function(a, b) {
            return a === b;
        },
        notLinkedFunction: function() {
            return this.a === this.b;
        },
        notLinkedFunctionWithDepends: function() {
            return this.a === this.b;
        }
    };

    // notLinkedFunctionWithDependsの戻り値はデータリンクした値に
    // 依存しているので、依存関係を定義する
    //
    //   [対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]
    //
    // "*"は、dataオブジェクト直下のすべてのプロパティについて変化した
    // かどうかを監視する
    data.notLinkedFunctionWithDepends.depends = [data, '*'];

    // テンプレートに名前(ここでは"linkTemplate")をつけて登録する
    $.templates({
        linkTemplate: "#template"
    });

    // テンプレート"linkTemplate"と変数dataをリンク
    $.link.linkTemplate("#result", data);
});
</script>

<!-- テンプレート -->
<script id="template" type="text/x-jsrender">
<p>テキストボックスが同じ値の場合、boolの値が変化します</p>
<div>
    <p data-link="a"></p>
    <input type="text" data-link="a trigger=true"/>
</div>
<div>
    <p data-link="b"></p>
    <input type="text" data-link="b trigger=true"/>
</div>
<p>固定値:<span data-link="unlinkedFunction()"></span></p>
<p>linkedFunction(a, b):<span data-link="linkedFunction(a, b)"></span></p>
<p>notLinkedFunction():<span data-link="notLinkedFunction()"></span></p>
<p>notLinkedFunctionWithDespanends():<span data-link="notLinkedFunctionWithDepends()"></span></p>
</script>
</body>
</html>

説明

JsRenderチュートリアル - テンプレートに渡したオブジェクトのメソッドを呼び出す - Do You PHP はてなテンプレートに渡されたオブジェクトメソッドを呼び出してみましたが、JsViewsの場合も基本的にはそのまま記述すればOKです。ただし、データリンクした値が変更された場合にメソッド戻り値を再評価したいかどうかJavaScript側のコードが変わってきます。

1. 戻り値を再評価しなくて良い場合

JsRenderと同様の記述方法でOKです。unlinkedFunctionメソッドはデータリンクした値と関連がありません。

<span data-link="unlinkedFunction()"></span>
2. 戻り値を再評価するが、引数でデータリンクした値を受け取っている場合

これもJsRenderと同様の記述方法でOKです。linkedFunctionメソッドはデータリンクした値を受け取っているので、「データリンクした値が変更された場合にメソッドを呼び出す」といった依存関係が自動的に定義されるためです。

<span data-link="linkedFunction(a, b)"></span>
3. 戻り値を再評価するが、引数でデータリンクした値を受け取っていない場合

たとえば

var variable = {
    a: {
        b: {
            c: 'foo'
            d: 'bar'
        },
        e: 'bar'
    },
    func: function() {
        return this.a.b.c > this.a.b.d && this.a.e !== 'hoge';
    }
};

という階層を持ったオブジェクトをデータリンクしても、JsViews(実際はJsObservable)にとってはfuncメソッドがどういう依存関係を持っているのか分からないため、戻り値が再評価されません。この場合は先ほど説明した依存関係を定義する必要があります

依存関係の定義方法ですが、定義したいオブジェクトメソッドのdependsプロパティとして、

配列もしくは文字列として記述します。

[対象のメソッド].depends = [対象の変数, 依存するプロパティへのパス, ...]

後者のプロパティへのパスですが、先ほど出てきたオブジェクトの場合、プロパティcへのパスは

a.b.c

また、プロパティeへのパスは

a.e

といった具合になります。また、直下のすべてのプロパティを表す"*"(アスタリスク)を指定することもできます。

ここまで踏まえた依存関係の定義例は以下のとおりになります。それぞれ、いずれの書き方でもOKです。

// variableオブジェクトのプロパティ"e"が変更された場合にのみ
// variable.funcを再評価する
variable.func.depends = [variable, 'a.e'];
variable.func.depends = [variable.a, 'e'];
variable.func.depends = 'a.e';

// variableオブジェクトのプロパティ"c"または"d"が変更された場合に
// variable.funcを再評価する
variable.func.depends = [variable, 'a.b.c', 'a.b.d'];
variable.func.depends = [variable, 'a.b.*'];
variable.func.depends = [variable.a, 'b.c', 'b.d'];
variable.func.depends = [variable.a, 'b.*'];
variable.func.depends = [variable.a.b, 'c', 'd'];
variable.func.depends = [variable.a.b, '*'];
variable.func.depends = ['a.b.c', 'a.b.d'];
variable.func.depends = ['a.b.*'];

// variableオブジェクトのすべてのプロパティについて、変更があった
// 場合にvariable.funcを再評価する
variable.func.depends = [variable, 'a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = [variable, 'a.*', 'a.b.*'];
variable.func.depends = ['a.e', 'a.b.c', 'a.b.e'];
variable.func.depends = ['a.*', 'a.b.*'];

JSFiddleで動作を見る

参照

2015-04-02

[]JsRenderチュートリアル - 存在しないプロパティを参照した時のエラーを回避する

JavaScript製でjQuery非依存なテンプレートエンジンであるJsRender/JsViewsを使ったチュートリアルを書いてみようと思います。

以前に書いたエントリJsRender入門 - Do You PHP はてなも参照してください。

今回やること

JsRenderやJsViewsで存在しないプロパティを参照した時のエラーを回避する

コード

<html>
<body>

<!-- レンダリング結果を表示するスペース -->
<div id="result"></div>

<script src="https://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="http://www.jsviews.com/download/jsviews.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
    // データの配列(もしくはオブジェクト)
    var fruits = {
            code: "01",
            name: "りんご",
            color: {
                name: "赤"
            },
            err: function() {
                return "エラーです";
            }
        };

    $.views.helpers({
        onerror: function(message) {
            return message + "です";
        }
    });

    // テンプレートに名前をつけて登録する
    $.templates({
        fruitsTemplate: "#template"
    });

    // テンプレート"fruitsTemplate"に変数fruitsを渡し、
    // レンダリング結果をid="result"に表示
    $("#result").html($.render.fruitsTemplate(fruits));
});
</script>

<script id="template" type="text/x-jsrender">
    <div>
        <li>{{>color.name}}</li>

        {{!--
            存在するオブジェクトのundefinedなプロパティを参照しても
            エラーにならず、空文字が表示される
        --}}
        <li>{{>color.foo}}</li>

        {{!--
            #data直下のundefinedなプロパティを参照してもエラーにならず、
            空文字が表示される
        --}}
        <li>{{>foo}}</li>

        {{!--
            undefinedのプロパティを参照すると

              {Error: TypeError: data.color.foo is undefined}

            というエラーになるが、onerrorを使うとエラー時に表示する
            メッセージを指定できる
        --}}
        <li>{{>color.foo.bar onerror="barがありません"}}</li>

        {{!-- リテラルだけではなくリンクした変数やヘルパーも使える --}}
        <li>{{>color.foo.bar onerror=color.name + "です"}}</li>
        <li>{{>color.foo.bar onerror=err()}}</li>
        <li>{{>color.foo.bar onerror=~onerror("ヘルパー")}}</li>
    </div>
</script>
</body>
</html>

説明

JsRenderやJsViewsで存在しないプロパティを参照した場合、

{Error: TypeError: data.foo.bar is undefined}

のようなメッセージが表示されることがありますが、このデフォルトのメッセージではなく任意のメッセージを表示させることができます。

JsRenderでの書式は次のとおりで、変数や式に続けて"onerror="と表示するメッセージを記述します。

{{>[変数や式] onerror=[表示するメッセージ]}}

表示するメッセージには、文字列のほか、テンプレートに渡された変数の値やヘルパーも使用できます。

JsViewsの場合、データリンクしたタグ({^{...}})のほかにdata-link属性にも記述可能です。

<li data-link="color.foo.bar onerror='エラーです'"></li>

JSFiddleで動作を見る

参照