April 27, 2013

FuelPHPのLogクラスでInfoとWarningの間のレベルでログ出力する

FuelPHPのLogクラスでDebugレベルな内容を出力すると、同時にInfoレベルも出力されます。尚、1.4まではDebugレベルとInfoレベルの関係は逆でした。1.5でMonologを採用したことによる仕様変更なのかなと。

そこで、試しにLogクラス https://github.com/fuel/log/blob/1.5/develop/classes/log.php を見てみると
250 => 'NOTICE',
とありました。このレベルはInfoレベルとWarningレベルの間になります。(対応するレベル定数はFuelクラスには無いですし、対応するLogクラスのメソッドも無いです。)

とりあえず、以下のやり方で出力は出来ました。

config.phpで以下を設定
'log_threshold' => 250,
Noticeレベルで出力
Log::write(250, 'xxx');
結果(DebugレベルとInfoレベルは出ませんでした。)
NOTICE - 2013-04-27 18:59:19 --> xxx
"Log::notice"的なメソッドをどこかに用意してやれば良いかなー。

April 24, 2013

NginxでIP制限とベーシック認証メモ

メモです。

それぞれ、nginx.confを編集します。

IP制限する場合
http {
    deny xxx.xxx.xxx.xxx;
    deny yyy.yyy.yyy.yyy;
    allow all;
...
ベーシック認証する場合
* 下記の.htpasswdファイルはApacheで使用しているいつものと同じ
server {
    auth_basic "Authentication";
    auth_basic_user_file "xxx/yyy/.htpasswd";
...
それぞれ、nginx.confを編集後にリロードします。
$ sudo nginx -s reload

April 23, 2013

FuelPHP x Ratchet x Nginxでコネクションが勝手に切れる

FuelPHP x Ratchet x Nginxな環境で、WebSocketのコネクションを張ったまま何も操作せずにブラウザを放置すると、接続が勝手に切れる現象が発生しました。

Macの Chrome / Firefox / Safari で同じ現象を確認しています。

これはFuelPHP x Ratchetというより、Nginxのプロキシ周りが怪しい気がしていますが(ポート番号直の時にこの現象は出ていなかった。はず。。。)、Nginxのkeep alive関係とかをググってもピンとくる解決策は今のところ見つかっていません。

なので暫定的に、30秒ごとにpingを送信して回避するようにしました。
https://github.com/mp-php/fuel-ratchet-samples/commit/dbbab237165a41bdad72915e8a4ddf773279bc54

とりあえず、接続は維持されるようになりました。

April 22, 2013

Nginxのプロキシ先のApacheでクライアントのIPアドレスをアクセスログに出力する

mod_rpafを使うと出来るっぽかったので、やってみました。Ubuntu上で確認しています。


1. libapache2-mod-rpafをインストールします。
$ sudo apt-get install libapache2-mod-rpaf
$ sudo a2enmod rpaf
$ sudo service apache2 restart
2. http://wiki.nginx.org/FullExample の proxy_conf の通りに /usr/local/nginx/conf/proxy.conf を作成します。
proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;
3. http://wiki.nginx.org/FullExample の nginx.conf のように proxy.conf を include します。

4. 3までを行なって反応しなかったので /etc/apache2/mods-enabled/rpaf.conf に以下の変更を行いました。(Apacheの再起動も併せて。)
<IfModule mod_rpaf.c>

<IfModule mod_rpaf-2.0.c>
に変更。

参考:
http://serverfault.com/questions/393207/mod-rpaf-problems-with-nginx-front-apache-back-end-after-ubuntu-upgrade

APCのインストールメモ

インストール
* 選択肢は全てデフォルトのままとした
$ sudo pecl install apc
php.iniに追記
extension=apc.so
Apacheの再起動
$ sudo service apache2 restart
参考:
UbuntuでのAPCのインストール方法
http://9jp.info/archives/12303

APCでPHPの処理を高速化
http://www.webcreator-net.com/tips_memo/server/20120612083050.html

Nginx + Apacheで静的ファイルのみをNginxで返すメモ

locationの書き方メモです。
location ~ .*\.(jpg|JPG|jpeg|JPEG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|ico|ICO) {
    root /var/www/;
}
参考:
http://wiki.nginx.org/FullExample
http://kray.jp/blog/nginx/

April 20, 2013

FuelPHP x Ratchetのサンプルを公開しました

2014/01/01 追記
デモサイトは停止しましたm(_ _)m

--

FuelPHPのRatchetパッケージを何の気なしに作ってみたわけですが、その後、けっこう面白くて、せっかくなのでサンプルを公開してみました。(暫くは動かしておくと思います。メンテとかで落ちてたらゴメンなさい。)


URLは以下になります。
http://fuelratchet.madroom.org/

デモのソースは以下になります。(デモではちょこっとカスタマイズしてます。)
https://github.com/mp-php/fuel-ratchet-samples

FuelPHPのRatchetパッケージは以下になります。
https://github.com/mp-php/fuel-packages-ratchet


Ratchet公式の、商用環境向けドキュメントは以下です。
http://socketo.me/docs/deploy


以下、当ブログの関連エントリです。


FuelPHP x RatchetをSupervisorで永続的に起動する
http://madroom-project.blogspot.jp/2013/04/fuelphp-x-ratchetsupervisor.html

FuelPHP x RatchetをNginx + Apacheで動かす
http://madroom-project.blogspot.jp/2013/04/fuelphp-x-ratchetnginx-apache.html

UbuntuにLibeventをインストールしてみる
http://madroom-project.blogspot.jp/2013/04/ubuntulibevent.html

FuelPHP x Ratchetでulimit -nを指定して複数タブで開いてみる
http://madroom-project.blogspot.jp/2013/04/fuelphp-ratchetulimit-n.html

FuelPHPでWebSocketを扱うパッケージを作りました
http://madroom-project.blogspot.jp/2013/04/fuelphpwebsocket.html

PHPでWebSocketを用いたチャットサンプル
http://madroom-project.blogspot.jp/2013/04/phpwebsocket.html


次はWampServerのサンプルを作りたいなーと思っています。

FuelPHP x RatchetをSupervisorで永続的に起動する

2013/05/05 追記
supervisor.confの設定例に"environment"を追加して、"FUEL_ENV=production"を設定するようにしました。

--

Ratchetドキュメント http://socketo.me/docs/deploy にSupervisorの記載があります。

SupervisorはPython製のプロセス管理ツールです。Supervisorを用いることで、プロセスを永続化させられます。

先日作成したRatchetパッケージを、Supervisorで永続的に起動させてみます。
https://github.com/mp-php/fuel-packages-ratchet

以下、手順です。

1. Supervisorのインストール
$ sudo apt-get install python-setuptools
$ sudo easy_install supervisor
2. 設定ファイルの雛形作成
* 以下、コマンドはFuelPHPのプロジェクトルートで実行しているものとします。
echo_supervisord_conf > fuel/packages/ratchet/supervisor.conf
3. FuelPHP x Ratchet用の設定例
* 2で作成した雛形に以下を追記
[program:ratchet]
environment             = FUEL_ENV=production
command                 = php oil r ratchet:ws [fuel/appにあるクラス名]
numprocs                = 1
autostart               = true
autorestart             = true
user                    = root
stdout_logfile          = fuel/app/logs/supervisor/info.log
stdout_logfile_maxbytes = 1MB
stderr_logfile          = fuel/app/logs/supervisor/error.log
stderr_logfile_maxbytes = 1MB
4. 起動
$ mkdir -p fuel/app/logs/supervisor # ログ用ディレクトリを作成
$ sudo supervisord -c fuel/packages/ratchet/supervisor.conf
5. その他、コマンド例
# ステータスの確認
$ sudo supervisorctl -c fuel/packages/ratchet/supervisor.conf status

# ストップ
$ sudo supervisorctl -c fuel/packages/ratchet/supervisor.conf stop all

# スタート
$ sudo supervisorctl -c fuel/packages/ratchet/supervisor.conf start all

# リスタート
$ sudo supervisorctl -c fuel/packages/ratchet/supervisor.conf restart all

これでやっと準備が整ったと思う。。。

--

関連:
FuelPHPでWebSocketを扱うパッケージを作りました
http://madroom-project.blogspot.jp/2013/04/fuelphpwebsocket.html

FuelPHP x RatchetをNginx + Apacheで動かす

JSでRatchetへのコネクションをオープンする時
new WebSocket("ws://example.com:1337");
のようにポート番号を指定しますが、これを消したいなーと。

Ratchetドキュメント http://socketo.me/docs/deploy にはHAProxyの記載がありますが、NginxのWebSocket Proxyでやってみました。尚、使用したNginxは最新の1.3.16です。


1. Apacheのポートを変更
* Nginxにポート80を使用させたいので、Apacheのポートを変更します。
http://madroom-project.blogspot.jp/2013/04/ubuntuapache.html
* 併せて、ApacheへのアクセスをNginxからのみに制限するには /etc/apache2/sites-available/default でドキュメントルートに対して"allow from all"を"allow from 127.0.0.1"にします。

2. Nginxのインストール
* 最新バージョンが欲しいので、ソースからインストールします。
$ wget http://nginx.org/download/nginx-1.3.16.tar.gz
$ tar xzvf nginx-1.3.16.tar.gz
$ cd nginx-1.3.16/
$ ./configure
$ make
$ sudo paco -D make install
$ nginx -v
nginx version: nginx/1.3.16
3. /usr/local/nginx/conf/nginx.conf を編集
* とりあえず http://nginx.org/en/docs/http/websocket.html を参考にしました。
* "location /"は、全てApacheに向けます。
* 以下、Ratchetのポートを1337、Apacheのポートを8000とします。
$ diff /usr/local/nginx/conf/nginx.conf.default /usr/local/nginx/conf/nginx.conf
42a43,49
>         location /socket/ {
>             proxy_pass http://127.0.0.1:1337;
>             proxy_http_version 1.1;
>             proxy_set_header Upgrade $http_upgrade;
>             proxy_set_header Connection "upgrade";
>         }
>
43a51,52
>             proxy_pass http://127.0.0.1:8000;
>
4. Nginxを起動
$ sudo nginx

# 停止は以下
$ sudo nginx -s stop
無事、JSで以下の形で接続出来ました。
new WebSocket("ws://example.com/socket/");
その他、設定すべき項目は有るのかもしれませんが、とりあえず、最小限の手順はこんな感じなのかなと。

(タイトルにFuelPHPとつけたけど全然関係なかったな。。。)


関連:
FuelPHPでWebSocketを扱うパッケージを作りました
http://madroom-project.blogspot.jp/2013/04/fuelphpwebsocket.html

UbuntuでApacheのポートを変更するメモ

メモです。

1. /etc/apache2/sites-available/default
<VirtualHost *:80>

<VirtualHost *:8000>
に変更。

2. /etc/apache2/ports.conf
Listen 80

Listen 8000
に変更。

3. Apacheを再起動。
$ sudo service apache2 restart

April 19, 2013

UbuntuにLibeventをインストールしてみる

引き続き、Ratchet関連です。


Ratchetドキュメントの商用環境関連の記載に、Libeventなるものの記載があったので、とりあえずインストールしてみました。
http://socketo.me/docs/deploy

Libeventについては、以下が非常に参考になりそうです。

C言語 libeventとは イベント通知とI/Oバッファ
http://kaworu.jpn.org/kaworu/2008-11-29-1.php

liveventの意義
http://nippondanji.blogspot.jp/2008/09/livevent.html


以下、インストール手順ですが、色々とエラーが出たので、あまり参考にならないかもしれませんm(_ _)m

とりあえず記載の通り
$ sudo apt-get install libevent libevent-dev
とすると
E: Unable to locate package libevent
が発生。
$ sudo apt-get install libevent-dev
ならインストールできたので、dpkgで確認してみると
$ dpkg -l | grep libevent
ii  libevent-2.0-5                            2.0.16-stable-1                                     Asynchronous event notification library
ii  libevent-core-2.0-5                       2.0.16-stable-1                                     Asynchronous event notification library (core)
ii  libevent-dev                              2.0.16-stable-1                                     Asynchronous event notification library (development files)
ii  libevent-extra-2.0-5                      2.0.16-stable-1                                     Asynchronous event notification library (extra)
ii  libevent-openssl-2.0-5                    2.0.16-stable-1                                     Asynchronous event notification library (openssl)
ii  libevent-pthreads-2.0-5                   2.0.16-stable-1                                     Asynchronous event notification library (pthreads)
と出ました。(これで良いのだろうか。)

続いて、PECL拡張モジュールのインストールです。
$ sudo pecl install libevent
として、またエラーが出ました。
Failed to download pecl/libevent within preferred state "stable", latest release is version 0.0.5, stability "beta", use "channel://pecl.php.net/libevent-0.0.5" to install
install failed
調べてみると、以下が見つかりました。
https://groups.google.com/forum/?fromgroups=#!topic/phpdaemon/KulET8lECIA
http://serverfault.com/questions/271554/problems-installing-php-libevent-pecl-package

真似して
$ sudo pecl install channel://pecl.php.net/libevent-0.0.5
とすると
configure: error: Cannot find libevent headers
と出ました。

諦めたくなって来ましたが、ソースからインストールしてみます。
http://pecl.php.net/package/libevent
$ cd ~/src/
$ wget http://pecl.php.net/get/libevent-0.0.5.tgz
$ tar xzvf libevent-0.0.5.tgz
$ cd libevent-0.0.5/
$ phpize
$ ./configure
$ make
$ sudo paco -D make install

----------------------------------------------------------------------
Libraries have been installed in:
   /home/xxx/src/libevent-0.0.5/modules

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,--rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
Installing shared extensions:     /usr/local/php/php-5.4.10/lib/php/extensions/no-debug-zts-20100525
(おお。進んだ。)

php.iniに以下を記述。
extension=libevent.so
Apacheを再起動。
$ sudo service apache2 restart
phpinfoを確認。
(0.0.5をDLしたはずだけど0.0.4となっているぞ。)


正しくインストール出来たのだろうか。。。

--

関連:
FuelPHPでWebSocketを扱うパッケージを作りました
http://madroom-project.blogspot.jp/2013/04/fuelphpwebsocket.html

April 18, 2013

FuelPHP x Ratchetでulimit -nを指定して複数タブで開いてみる

Ratchetのドキュメントに商用環境向けの設定例等が書いてあります。
http://socketo.me/docs/deploy


先日作成したRatchetパッケージを使って、ulimit -nの指定を確認してみました。
Linux(Ubuntu)とMacで確認しています。

$ ulimit -n 10 && php oil r ratchet:ws <class name>
で起動したRatchetサーバに対して、複数タブで同時にアクセスしてみると、7タブ目で
Error: stream_socket_accept(): accept failed:
が発生して、プロセスが落ちました。Linux(Ubuntu)とMac共に、同じ結果です。

Ratchetのドキュメントの同ページにあるSupervisorを用いれば自動リスタートが可能と思われますが、それでも、それまで繋がっていたコネクションは切断されるのかなと思います。(未検証。)

今回は、確認結果ということで、それだけです。。。
(タブ間でコネクション共有が出来るのか。も調べないと。。。)

--

関連:
FuelPHPでWebSocketを扱うパッケージを作りました
http://madroom-project.blogspot.jp/2013/04/fuelphpwebsocket.html

UbuntuでApacheのmod_rewriteを有効にするメモ

メモです。

1. コマンドの実行
$ sudo a2enmod rewrite
2. /etc/apache2/sites-available/default の修正
<Directory /var/www/>
# 省略
    AllowOverride None
# 省略
</Directory>

<Directory /var/www/>
# 省略
    AllowOverride All
# 省略
</Directory>
に変更。

3. Apacheの再起動
$ sudo service apache2 restart

April 13, 2013

FuelPHPでWebSocketを扱うパッケージを作りました

追記: 2013/04/23
簡単なサンプルを公開しています。
http://fuelratchet.madroom.org/

追記: 2013/04/21
一部、内容が古くなっている箇所があります。最新の内容は、GitHubのREADME.mdに記載しています。

--

以前、以下の記事を書きました。

PHPでWebSocketを用いたチャットサンプル
http://madroom-project.blogspot.jp/2013/04/phpwebsocket.html

今回、上記で使用しているRatchetを組み込んだ、FuelPHPのパッケージを作ってみました。
* 以下、Ratchetパッケージとします。
https://github.com/mp-php/fuel-packages-ratchet


当記事では、Ratchetパッケージを、ローカル環境で動作させるまでの手順について書いておきます。


(1) ZeroMQのインストール
まず、ZeroMQをインストールします。RatchetのWampServerで使用します。(当記事のサンプルでは使用しません。)
各OSでのインストール手順は、以下になります。
Linux(Ubuntu): http://madroom-project.blogspot.jp/2013/04/ubuntu-phpzeromq.html
Mac(MAMP): http://madroom-project.blogspot.jp/2013/04/mampmaczeromq.html
Win(XAMPP): http://madroom-project.blogspot.jp/2013/04/xamppwindowszeromq.html

正しくインストール出来たかは、phpinfoで確認ができます。

ZeroMQは、Webとコマンド、双方で有効にして下さい。(php.iniが異なる場合が有るので注意です。)


(2) FuelPHPのインストール
FuelPHPをgit cloneするなりzipでDLするなりして、適切な場所に配置して下さい。


(3) Ratchetパッケージのインストール
Ratchetパッケージをgit submoduleするなりzipでDLするなりして、fuel/packages/ratchetとして配置します。
submoduleとして追加する場合、以下で可能です。
# FuelPHPのプロジェクトルートで実行
$ git submodule add git://github.com/mp-php/fuel-packages-ratchet.git fuel/packages/ratchet


(4) Ratchetパッケージが必要とする外部ライブラリのインストール
以下のコマンドを実行します。
$ cd fuel/packages/ratchet # Ratchetパッケージに移動
$ php composer.phar install # 必要な外部ライブラリのインストール
* (1)のZeroMQが正しくインストールされていないと、エラーが発生します。


(5) 簡単なメッセージ送信機能を作ります。
fuel/app/config/config.php の always_load.packages にRatchetパッケージを追加します。
'always_load'  => array(
    'packages'  => array(
        'ratchet',
fuel/app/classes/my/ratchet/ws.php を作成します。
<?php

require PKGPATH.'ratchet/vendor/autoload.php';

class My_Ratchet_Ws extends Ratchet_Ws
{

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(\Ratchet\ConnectionInterface $conn) {
        $this->clients->attach($conn);
    }

    public function onClose(\Ratchet\ConnectionInterface $conn) {
        $this->clients->detach($conn);
    }

    public function onMessage(\Ratchet\ConnectionInterface $from, $msg) {
        foreach ($this->clients as $client) {
            if ($from != $client) {
                $client->send(Security::htmlentities($msg));
            }
        }
    }

    public function onError(\Ratchet\ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }

}

/* end of file ws.php */
public/ws.htmlを作成します。
* 実際にはControllerから呼び出すViewで使用されるviewファイルになります。
* 'example.com'の箇所は、置換して下さい。
<html>
<head>
<script>
var conn = new WebSocket('ws://example.com:8001');
// 接続時
conn.onopen = function(e) {
    console.log("Connection established!");
};
// メッセージ受信時
conn.onmessage = function(e) {
    console.log(e.data);
};
function send() {
    var msg = document.getElementById("text").value;
    conn.send(msg);
}
</script>
</head>
<body>
<input type="text" id="text" />
<input type="button" id="button" value="Send" onclick="send();" />
</body>
</html>

(6) タスクから起動します。
* 第二引数のポート番号は、適宜置換して下さい。その際、(5)のws.htmlに書いてあるポート番号も変更して下さい。
$ php oil r ratchet:ws My_Ratchet_Ws 8001
* 終了はctrl + cです。
* 商用環境では、Supervisorでの起動を推奨します。その方法は、改めて書きたいと思います。


(7) 二つのブラウザから、(5)のws.htmlにアクセスします。
コンソールに'Connection established!'と表示されることを確認します。


(8) どちらかのブラウザから、メッセージを入力してSendボタンを押します。
もう一方のブラウザのコンソールに内容が表示されることを確認します。


P.S.
僕自身、WebSocketについての学習をこれから始めるので、色々と手探り状態です。今後は、以下に備忘録を兼ねたサンプルを追加していこうと思っています。
https://github.com/mp-php/fuel-ratchet-samples

とりあえず、Ratchetサーバ側とでのSession共有方法と、RatchetのWampServerあたりをなる早で作ろうかなと思っています。

April 10, 2013

Ubuntu + PHPでZeroMQを使えるようにする

2013/10/29 追記
以下、最新のインストール手順です。
http://mp-php.github.io/reveal-fuel-ratchet/#/7/3

--

MacとWinでは確認済みですが、一応、Linux(Ubuntu)でも。まあ、一番簡単でした。尚、pacoはパッケージ管理ツールなので、必須ではありません。

(1) libzmqとphp-zmq(とpaco)のインストール
$ sudo apt-get install -y libzmq-dev paco
$ cd ~/src/
$ git clone git://github.com/mkoppanen/php-zmq.git
$ cd php-zmq/
$ phpize
$ ./configure
$ make
$ sudo paco -D make install
(2) Apacheで使用するphp.iniに以下を記述
extension=zmq.so
(3) 以下のコマンドで表示されるphp.iniにも上記を記述
$ php -i | grep 'Configuration File'
関連:

MAMP(Mac)でZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/mampmaczeromq.html
XAMPP(Windows)でZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/xamppwindowszeromq.html

XAMPP(Windows)でZeroMQを使えるようにする

2013/10/29 追記
以下、最新のインストール手順です。
http://mp-php.github.io/reveal-fuel-ratchet/#/7/4

--

(1) 必要なdllの取得
https://github.com/mkoppanen/php-zmq/wiki
* php-zmq/zeromq-3.2.2/libzmq.dll を xampp/php/libzmq.dll としてコピー
* php-zmq/[php53 | php54]以下の適切なphp_zmq.dllを xampp/php/ext/php_zmq.dll としてコピー
* phpinfoで"zmq"の項目が有ること

(2) Apacheで使用するphp.iniに以下を記述
extension=php_zmq.dll
(3) 以下のコマンドで表示されるphp.iniにも上記を記述
$ php -i | grep 'Configuration File'
参考:
http://www.zeromq.org/bindings:php

関連:
MAMP(Mac)でZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/mampmaczeromq.html
Ubuntu + PHPでZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/ubuntu-phpzeromq.html

April 9, 2013

MAMP(Mac)でZeroMQを使えるようにする

2013/10/29 追記
以下、最新のインストール手順です。
http://mp-php.github.io/reveal-fuel-ratchet/#/7/1

--

RatchetでWampServerを用いたサンプルにZeroMQなるものが記載されていたので、インストールしてみました。
http://socketo.me/docs/push

ZeroMQ
http://www.zeromq.org/

(1) libzmqのインストール
$ cd ~/src/
$ git clone git://github.com/zeromq/libzmq.git
$ cd libzmq/
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
(2) 後述のpecl installでphp.hのエラーが出るため、公式からPHPをDL
参考: http://d.hatena.ne.jp/zebevogue/20120630/1341020573
$ wget http://www.php.net/get/php-5.4.12.tar.bz2/from/jp1.php.net/mirror
$ tar zxf php-5.4.12.tar.bz2
$ cd php-5.4.12
$ ./configure
$ cd ../
$ mkdir /Applications/MAMP/bin/php/php5.4.4/include
$ mv php-5.4.12 /Applications/MAMP/bin/php/php5.4.4/include/php
(3) php-zmqのインストール
$ cd ~/src/
$ git clone git://github.com/mkoppanen/php-zmq.git
$ cd php-zmq/
$ phpize
$ ./configure
$ make
$ sudo make install
(4) Apacheで使用するphp.iniに以下を記述
extension=zmq.so
(5) 以下のコマンドで表示されるphp.iniにも上記を記述
$ php -i | grep 'Configuration File'
で、Ratchetのサンプルをごにょごにょしたら動きました。(これは後日、整理した上で書きたいと思っています。)

関連:
XAMPP(Windows)でZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/xamppwindowszeromq.html
Ubuntu + PHPでZeroMQを使えるようにする
http://madroom-project.blogspot.jp/2013/04/ubuntu-phpzeromq.html

April 7, 2013

PHPでWebSocketを用いたチャットサンプル

PHPでWebSocketを扱うにはどうしたら良いかなーと探してみると、Ratchetというライブラリが見つかりました。

公式:
http://socketo.me/

GitHub:
https://github.com/cboden/Ratchet

GitHubのスターの数もそれなりだったので、どんな感じか確認してみました。以下、手順です。尚、Macで確認しています。

(1) composer.jsonを用意して、いつものコマンドを実行します。
http://socketo.me/docs/install を参考にしています。
{
        "require": {
                "cboden/Ratchet": "0.2.*"
        }
}
$ curl -s https://getcomposer.org/installer | php
$ php composer.phar install
(2) サンプルファイルを配置する為のappディレクトリを作成します。
$ mkdir app
$ ls
app  composer.json composer.lock composer.phar vendor
(3) app/chat.phpを作ります。所謂、サーバサイドの処理です。
ポートと、autoload.phpとの位置関係以外は https://github.com/cboden/Ratchet#a-quick-server-example の通りです。
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;

    require __DIR__.'/../vendor/autoload.php';

/**
 * chat.php
 * Send any incoming messages to all connected clients (except sender)
 */
class Chat implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        foreach ($this->clients as $client) {
            if ($from != $client) {
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}

    // Run the server application through the WebSocket protocol on port 8080
    $server = IoServer::factory(new WsServer(new Chat), 9000);
    $server->run();
(4) app/chat.phpを起動します。
$ php app/chat.php
(5) app/chat.htmlを作ります。所謂、クライアント(ブラウザ)サイドの処理です。
http://socketo.me/docs/hello-world を参考にしています。
<html>
<head>
<script>
var conn = new WebSocket('ws://localhost:9000');
conn.onopen = function(e) {
    console.log("Connection established!");
};
conn.onmessage = function(e) {
    console.log(e.data);
};
function send() {
    var msg = document.getElementById("text").value;
    conn.send(msg);
}
</script>
</head>
<body>
<input type="text" id="text" />
<input type="button" id="button" value="Send" onclick="send();" />
</body>
</html>
(6) 二つのブラウザからアクセスします。
* SafariとChromeで試してみました。
* 右がChromeで、コンソールを開いています。"Connection established!"と出力されているのがわかります。

Safariから適当にメッセージを入力してSendボタンを押してみます。
きました!

ちょっと前にPHP + Redis + Node.jsとかで考えてみたんですけど、暗号化/復号化周りの話で、セッションの共有が厄介そうでした。
(FuelPHPじゃなくても同じ問題は出てくるでしょう。)

でも、この方法なら、けっこういけるんじゃないだろうか。。。


P.S.
console.logしているだけなので、全然チャットサンプルではなくてすみませんm(_ _)m

April 6, 2013

TraceKitを使ってブラウザ側でJavaScriptのエラーを購読して、エラー発生時にサーバへ送信するサンプル

昨夜、SentryをUbuntuにインストールしてみました。

UbuntuにSentryをインストールしてみた
http://madroom-project.blogspot.jp/2013/04/ubuntusentry.html

Sentryを使ってJavaScriptのエラーレポートを集計してみた
http://madroom-project.blogspot.jp/2013/04/sentryjavascript.html

SentryでJavaScriptのエラーレポート集計が可能になりますが、簡易版みたいなものを自作しようかなと考えています。そしたら、TraceKitというJavaScriptのエラーを購読出来るライブラリが有りました。Sentryもこれを使っているようです。ライセンスはMITと記されています。
https://github.com/occ/TraceKit

早速、使い方を確認してみました。JavaScriptのエラーを購読して、サーバ(PHP)に送信するまでのサンプルを書いておきます。

index.html
* tracekit.jsは前述のGitHubのURL先からDLしました。
* TraceKit自体はjQueryを必要としません。AjaxでPHPに送信する為に使用しています。
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="tracekit.js"></script>
<script>
TraceKit.report.subscribe(function myLogger(errorReport) {
    console.log(errorReport);
    $.post("receiver.php", { report: errorReport } );
});
</script>
</head>
<body>
<script>
var result = undefined_1 + undefined_2;
</script>
</body>
</html>
receiver.php
<?php

file_put_contents('trace.log', print_r($_POST, true), FILE_APPEND);
index.htmlにアクセスしてJavaScriptのエラーを発生させると、その内容がreceiver.phpに送信され、trace.logへ出力されます。

trace.log
Array
(
    [report] => Array
        (
            [mode] => onerror
            [message] => Uncaught ReferenceError: undefined_1 is not defined
            [url] => http://example.com/index.html
            [stack] => Array
                (
                    [0] => Array
                        (
                            [url] => http://example.com/index.html
                            [line] => 14
                            [func] => myLogger
                            [context] => Array
                                (
                                    [0] => });
                                    [1] => </script>
                                    [2] => </head>
                                    [3] => <body>
                                    [4] => <script>
                                    [5] => var result = undefined_1 + undefined_2;
                                    [6] => </script>
                                    [7] => </body>
                                    [8] => </html>
                                    [9] => 
                                )

                        )

                )

            [useragent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31
        )

)

これは便利だ。。。

Androidアプリで1x1サイズのWidgetが2x2になったりしたので修正した

今日、Xperia Zに機種変しました。そしたら自作アプリのウィジェットで、1x1のはずが2x2になってしまったりして、これは使いものにならない。ということで、修正しました。


ぐぐってみると、以下が出て来ました。
http://stackoverflow.com/questions/9282724/1x1-android-widget-shows-as-2x2-widget-on-wxga720-screen


とりあえず
http://developer.android.com/guide/practices/ui_guidelines/widget_design.html#anatomy
のColumns or Rowsに対するAvailable Size (dp)を参考に修正してみると、直りました。

他のバージョンに悪影響無ければ良いけど。。。


UbuntuにSentryをインストールしてみた

先に、以下を相当参考にさせて頂きました。

Sentryをインストールした
http://www.kyamada.com/28

--

以下、手順のバックアップを兼ねて、記述します。
尚、DBはMySQLを使用しています。

(1) 下準備(必要らしいパッケージをインストール等)
$ sudo apt-get install python-setuptools
$ sudo apt-get install python-virtualenv
$ sudo apt-get install python-mysqldb
$ sudo apt-get install python-dev
$ sudo virtualenv /var/www/sentry/
$ source /var/www/sentry/bin/activate
(2) Sentryのインストールと設定
$ sudo easy_install -UZ sentry
$ sentry init
$ cp ~/.sentry/sentry.conf.py ~/.sentry/sentry.conf.py.org
$ vim ~/.sentry/sentry.conf.py
$ diff ~/.sentry/sentry.conf.py.org ~/.sentry/sentry.conf.py
14c14
<         'ENGINE': 'django.db.backends.sqlite3',
---
>         'ENGINE': 'django.db.backends.mysql',
16,18c16,18
<         'NAME': os.path.join(CONF_ROOT, 'sentry.db'),
<         'USER': 'postgres',
<         'PASSWORD': '',
---
>         'NAME': 'sentry',
>         'USER': 'xxx',
>         'PASSWORD': 'yyy',
(3) DBを作成
$ mysql -u root -proot
mysql> create database sentry;
mysql> quit
(4) マイグレーションしようとしてエラー発生
$ sentry upgrade # ImportError: No module named request
(5) マイグレーションのエラー要因になっているパッケージをダウングレードして再実行
* 新しいバージョンの方のeggはリネームして避けました。
$ sudo easy_install -U "cssutils <= 0.9.9"
$ sudo easy_install -U "raven <= 3.2" # これは別のエラー関係
$ sentry upgrade
参考:
https://bugs.launchpad.net/singing-dancing/+bug/1164486
(つい最近じゃん。。。)

(6) スーパーユーザを作成しようとしてエラー発生。。。
$ sudo sentry createsuperuser # TypeError: decode() argument 1 must be string, not None
(7) 言語設定をして再度スーパーユーザを作成
$ export LANG=ja_JP.UTF-8
$ export LC_ALL=ja_JP.UTF-8
$ sudo sentry createsuperuser
(8) 起動してhttpでポート9000にアクセス、スーパーユーザでログイン
$ sentry start
# ctrl + c で終了
(9) Sentryをサービスとして起動
$ sudo easy_install supervisor
$ sudo vim /etc/supervisord.conf
$ more /etc/supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)
;chmod=0700                 ; socket file mode (default 0700)
;chown=nobody:nogroup       ; socket file uid:gid owner
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))

;[inet_http_server]          ; inet (TCP) server disabled by default
;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
;port=172.16.1.20:9001       ; (ip_address:port specifier, *:port for all iface)
;username=hoge              ; (default is no username (open server))
;password=hoge              ; (default is no password (open server))

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)
;umask=022                   ; (process file creation umask;default 022)
;user=chrism                 ; (default is current user, required if root)
;identifier=supervisor       ; (supervisord identifier, default is 'supervisor')
;directory=/tmp              ; (default is not to cd during start)
;nocleanup=true              ; (don't clean up tempfiles at start;default false)
;childlogdir=/tmp            ; ('AUTO' child log dir, default $TEMP)
;environment=KEY=value       ; (key value pairs to add to environment)
;strip_ansi=false            ; (strip ansi escape codes in logs; def. false)

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris              ; should be same as http_username if set
;password=123                ; should be same as http_password if set
;prompt=mysupervisor         ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history  ; use readline history if available

[include]
files = /etc/supervisord.d/*.ini

$ sudo mkdir /etc/supervisord.d
$ sudo vim /etc/supervisord.d/sentry.ini
$ more /etc/supervisord.d/sentry.ini
[program:sentry-web]
directory=/var/www/sentry/
command=/usr/local/bin/sentry start http
autostart=true
autorestart=true
redirect_stderr=true

$ supervisord
$ supervisorctl status
sentry-web                       RUNNING    pid 1989, uptime 0:00:05
(10) httpでポート9000にアクセス

--

関連:
Sentryを使ってJavaScriptのエラーレポートを集計してみた
http://madroom-project.blogspot.jp/2013/04/sentryjavascript.html

April 5, 2013

Sentryを使ってJavaScriptのエラーレポートを集計してみた

2013/04/06 追記
Ubuntuにインストールしてみました。
http://madroom-project.blogspot.jp/2013/04/ubuntusentry.html

2013/04/05 追記
自分のサーバとかにもインストールできるらしいです。やらねば。。。
http://www.kyamada.com/28
--

Sentryというサービスで、JavaScriptのスクリプトエラーのレポート集計が出来るようだったので、試してみました。JavaScript以外の集計も出来るっぽいです。

公式
https://www.getsentry.com/welcome/

試してみたコースは、無料のTrialコースです。
説明に "7 day history, 100 events a day, no team members" とあるので、1日最大100レポートまで受け付けて、直近7日間の集計が見れる感じでしょうか。


(1)
まず、適当に進めてプロジェクトの作成までを完了させました。

(2)
次に、プロジェクトのSettingsでAllowed Domainsに、トラッキングしたいサイトのドメインを入力しました。

(3)
次に、プロジェクトのCLIENT CONFIGURATIONのJavaScript項に書いてあるスクリプトを、先ほど入力したドメインのサイトに貼り付けました。
* 以下、アカウント毎の固有情報を含むかもしれない部分は置換しています。
<script src="//xxx/raven.min.js"></script>
<script>
Raven.config('https://yyy/zzz').install();
</script>
(4)
入力したドメインのサイト側で、適当にスクリプトエラーを発生させてからSentryのEventsページにアクセスすると、レポートが有りました。
ドメインをクリックすると、詳細が見れました。

簡単で素晴らしいですね。