2015年5月9日土曜日

PHPカンファレンス関西2015に参加したい人生だった

色々不調で今年は参加できません。

参加できずとも個人スポンサーなら俺ごときアレにでもできる貢献ではないかと公式サイトのお知らせを見たら 4/30 で応募を締め切っていた。 こうなりゃ参加しないまでもチケット購入するか?(果てしなく迷惑な行為)と http://phpconkansai2015.peatix.com/ を覗いてみると「当日は参加できないけど、イベントは応援したい! そんなときはカンパしよう! 」とあるではないか。ここから個人スポンサーの一口分の金額を送らせていただいた。

このカンパの入力フォームで惨めなジレンマがあった。 peatix は匿名でカンパができる。また、寄付者本人が望むなら名前を入れることもできる。 結局俺は自身のハンドルネーム oasynnoum を入れた。 弱い人間だと思う。「俺は応援してるよ!」と俺が思っているということに気付いて欲しかった。 そう、気付いて欲しかったからこのブログも書いている。 ゴル、いやミスター・デューク・東郷ならユーモアのある仮の名前と共に100万ドルぐらいは放り込んでいたに違いない。

とにかく peatix にカンパの機能があってよかったと思う。 ついでと言ってはあれだけどPHPカンファレンス福岡の方にも寄付をしようかと漁ってみたが、個人スポンサーの募集もチケット販売サイトにカンパの機能もなくひとまず断念した。

2014年9月15日月曜日

C で社内フレームワーク作るってのはやはり正気ではないのか

今お仕事で開発に関わらせてもらっているとあるシステムは PHP で動いている。
これは1画面1ファイルという典型(古典)的な PHP スクリプトからなり、一つの PHP ファイルにビジネスロジック・ビュー・JavaScriptのコードなどが混在している。テストコードや開発者が気軽にアクセスできるドキュメントは無い。つらい。(もちろんそのシステムには色々優れた点もあるけどここでは割愛)
ビジネスロジックとビューを分離したい。そのビジネスロジックをライブラリ化したい。単体テスト書きたい。

どうライブラリ化するか。 PHP には composer もあるし、そのまま PHP でライブラリ作ろうかと思ったけど、ほんとに PHP でいいのか? PHP のオワコン化が怖い。

C で書きたい。C で書く必要性を感じている。このシステムに長生きしてもらいたい。このシステムの仕様はすでに枯れ始めているので大切な部分は把握できるし、そこだけを抜いて大事にとっておきたい。

C で書くメリットは大体次のような感じだろうか。
  • 大抵の言語は拡張インターフェースを C で用意していて(PHP とか)、それらのバインディングを書けるので言語の流行り廃りの影響をうけにくい
  • それなりに書けば Mac, Windows, Linux 以外にも iOS や Android でも使える

また、デメリットとしては
  • 保守にあたり、少なくとも C や make などの知識が必要になる
というのはあると思う。

はっきり言って俺にそれだけのスキルが有るかは不明。不明だからこそやりたいんだよね。出来るか出来ないか分からないことをやるのが楽しいんだと感じる。

DB アクセスは libdbi を使おうと思う。ライブラリの最低限の骨組みだけを作って Mac で試したところ、そのライブラリはテストアプリにうまくリンクできた。(もちろん libdbi も)
libdbi は Cygwin 環境なら Windows でもコンパイル・リンクできるらしい。これを試してうまく行けば、そのまま C で書き進める。

2014年7月2日水曜日

PHPカンファレンス関西2014で PHPMake\Firmata について話(すつもりで)した #phpkansai


firmata-space-travel demo  (ぜひ日本語字幕をONにしてご覧ください)
PHPMake/firmata-space-travel on Github

発表内容について

PHPカンファレンス関西2014 で「 Arduino を PHP で制御する」というタイトルでお話させていただきました。
今年のはじめくらいから作り始めた PHPMake\Firmata というライブラリについての発表でした。
firmata とは、PCなどから Arduino のようなマイコンボードを制御するためのプロトコル(ルール)です。 PHPMake\Firmata は PHP で firmata を実装したライブラリです。
「PHPMakeFirmata を使えば Arduino とWebを連携させたアプリが簡単に PHP で作れる!」というのが売り文句です。

個人的な課題

私はここしばらく、「 PHP を使って現実世界で動くものをコントロールしたいんじゃ!」というテーマで試行錯誤しています。 PHPMake\Firmata はそれを解決する一つの方法で、今のところうまく行っています。しかし大きな不満を感じています。それは firmata プロトコル概要で触れた firmata のデメリットそのものです。
  • ホストとデバイスはケーブルで繋がる必要がある
  • ホストが無いと何も出来ない
これらを解決する方法はおそらく firmata の枠を超えるでしょう。
私はハードウェアは全然詳しくないですし、かといってソフトウェアのスペシャリストとかそんなんでもありません。はっきり言えばただのぺちぱーです。扱っているテーマが自分の分を超えているのはなんとなくわかっています。私の手に余る問題だと感じています。多分に失敗するでしょう。
今後はこれらをやっつけるべく試行錯誤していきます。

反省・後悔

総括すると「大失敗」でした。
PHP の話は出てきませんでした。30分という時間のうち firmata プロトコルの詳細をベラベラくっちゃべって @voidofglans さんに「あと5分です」と指摘頂いて、悲鳴を上げながら行ったデモは、手元の Arduino, ブレッドボードを持ち上げた時に、それに衝撃が加わって動かなくなりました。 Ubuntu もフリーズしかけるしで。私の話を聞きに来てくださった皆さんには本当に申し訳ないことをしました。本当にすみませんでした。
更に、私の後に控えておられた @martini3oz さんにも大変ご迷惑をお掛けしました。ごめんなさい。私のデモに興味を持ってくださった方達が、入れ替わりの5分の時間に私の机周辺に来られました。私は調子に乗ってデモを敢行し、結果、スケジュールの進行に遅れをもたらしたものと考えています。
今振り返れば、会議室を出たところに、少なくとも興味のある人たちだけでも輪になって話せるような空間があれば個人的には嬉しかったです。いわば、タイムテーブルの外で延長線を行うことで、それこそ conference と呼べるようなやりとりが出来たような気がします。

楽しかった遠足の思い出

ひこうき、こわかったです。

大阪在住のプログラマーである私の友人 @margaman にはとてもお世話になった。
金曜日、前日に大阪についたのが午前10時、ホテルにチェックインできる時間が16時。彼がいなかったら、疲労の溜まっていた私は街中の電柱のそばで突っ立ったまま眠る可哀想な人になっていたはずです。ありがとうマーガマン!

今回の大阪滞在の目的の一つにハードのスペシャリスト、T-4(@horothewolf)に会うというものがありました。この目的はPHPカンファレンス関西のスピーカー募集時から頭にありました。ツイッターのアイコンのイメージ通りの ~(女性) でした。という冗談はさておき、実はT-4さんには Gorilla を一番最初にリリースした際非常にお世話になっていました。実際にT-4さんの手元で動かして頂いて、バグが見つかり、こちらで再現しなくて苦しんでいたらT-4さんが正しくその解を見つけて教えて下さりました。その時はそのことで舞い上がって満足し、大変感謝しただけで、ある大切なことを忘れたまま時が流れて行きました。「あれ?T-4さんって紛れもなく contributor じゃねーか!」と頭によぎったのは半年後とかそのぐらいで、それから「 contributor として phpinfo() の Gorilla のセクションに名前載っけてもいいですか」とはなかなか聞けずにいました。それを直接面と向かって切り出したかったのです。
今回の滞在中、T-4さんには色々教えていただきました。 USB デバイスを作ってみたいと言ったら色々アドバイス頂きました。29日には日本橋を案内してもらいながら、電磁気学の講義を頂きました。本当に有難く、楽しかったです。さらに「わからないことあったらいつでも訊いて、相談して」とのこれまたひっじょーに有難いお言葉を頂きました。遠慮なく Skype, ビデオチャットで質問しまくる所存です。おみやげに頂いた551の豚まん美味しかったです。ありがとうございました。

たけさん (@ww24) 、にはセッション後に声をかけて頂き、T-4さん、マーガマン、私とでお昼をご一緒しました。デモの WebSocket 部分に興味を持って頂き、私は馬鹿みたいに Ratchet, Ratchet としか繰り返さず、せっかく声をかけていただいたのに申し訳なかったです。デモのコードを一緒にゆっくり見れる時間とスペースがあればよかったなぁなどと思いました。
LTお疲れ様でした。(PHPer のための Node.js 入門)

懇親会では「えーい、ままよ!」とばかりに適当なテーブルに突っ込んでいったところ、きよっちさん(@kiyotchi)ご夫妻が快く相手して下さり助かりました。きよっちさんが沖縄好きでその話で少しでも盛り上がれたのは幸運でした。また、きよっちさんが開発しておられる Seezoo CMS のプロジェクトの話、成り行きが聞けたのは貴重でした。面白かったです。ありがとうございました。

懇親会のルーレット?で「PHPライブラリ&サンプル実践活用」を見事引き当てた「だーしま」こと島田さんに凸して「このGorillaというモジュールはワシが書いた」という非常に迷惑なライブラリ作者アピールを行いましたが、快く歓迎してくれました。島田さんの提案により、本を二人で抱えて一緒に写真を取りましたが、結局この写真を一番欲しがったのは私自身でした。技術書の著者が自著を紹介するときの販促スマイルがありますが、日頃、それを生ぬるいと感じていた私はここぞとばかりに「これが販促スマイルだ!」と渾身の笑みで写真を取らせて頂きました。ここで今ご紹介できないのが残念です。
島田さんのご厚意により写真アップOKの許可が出ましたので、せっかくなのでお手本とも呼ぶべき販促スマイルと私の渾身の販促スマイルを並べてみたいと思います。

@yando さんによるお手本とも呼ぶべき正統派な販促スマイル

'I have nothing to lose' な無政府主義者たちのスマイル
右が島田さん、左が私です。ちなみに私も島田さんも無政府主義者ではありません。
島田さん、初対面にも関わらずこのような馴れ馴れしい、無礼な振る舞いを許して下さり本当にありがとうございました。

こんなついででなんですが、「PHPライブラリ&サンプル実践活用」著者の方々にGorillaを取り上げて頂いた御礼申し上げます。ありがとうございました。


個人的にはT-4さんとリアルで会えて繋がりが持てたことは、この滞在での素晴らしい収穫でした。遠慮無く色々アドバイスもらおうと思っています。
また、私のセッションを聞きに来てくださった方々には重ねてお詫び申し上げます。本当にすみませんでした。反省しています。(反省しているので、つまり、リベンジを考えています)
PHPカンファレンス関西2014参加者の皆さん、スタッフの皆さん、お疲れ様でした!
本当に楽しかったです!ありがとうございました!



2014年4月25日金曜日

php-eject ツイートまとめ


ここで言っている「ある御仁」というのは誰でもない架空の存在。
あっきぃさんのことではない。






2014年3月28日金曜日

2014年1月26日日曜日

PHP 拡張の開始・読み込み順について

PHP 拡張がどういう順序で読み込まれるのか気になったので調べてみた。

php_module_startup()は拡張の読み込み・開始を行う。大まかには PHP 拡張は次のような順序で読み込まれる。
  1. PHP 本体に組み込まれた(静的な)拡張
  2. php_module_startup() をコールする際に与えられる付加的な拡張
  3. php.ini に extension=extension.[so|dll] で記述された共有ライブラリ
この 1,2,3 の流れで拡張のリスト、 module_registry へ読み込む。
その後、拡張の依存関係に従い module_registry をソートし、順に拡張を開始する。

拡張開始(ソートを行い拡張を開始)
php-src/Zend/zend_API.c Line 1882 on GitHub
ソート関数
php-src/Zend/zend_API.c Line 1769 on GitHub

1 について、静的な拡張間における読み込み順は関数ポインタ php_register_internal_extensions_func が指す実体、 php_register_internal_extensions の実装による。
php-src/main/internal_functions.c.in を見ると static zend_module_entry *php_builtin_extensions[] がハードコーディングされており、読み込みはこの順に従う。

2 も 1 と同様に、 php_module_startup() の第2引数 additional_modules の順に従う。

3 の共有ライブラリの読み込み順は基本的には php.ini の extension=extension.[so|dll] の記述順となっているようだ。
main/php_ini.c 内において用いられるグローバル変数 extension_lists はこれから読み込もうとする拡張のパスを保持する。
    static php_extension_lists extension_lists;
php-src/main/php_ini.c line 71 on GitHub
    typedef struct _php_extension_lists {
        zend_llist engine;
        zend_llist functions;
    } php_extension_lists;
php-src/main/php_ini.c line 59 on GitHub

メンバ engine は Zend extension の共有ライブラリ(so, dll)へのパス文字列のリスト、 functions は PHP extension の共有ライブラリへのパス文字列のリストとなっている。

最後までソースコードを追っかけていないので、上では「ようだ」という言い方にとどめている。とはいえ、 ini ファイルをパースする際に用いる関数、 zend_parse_ini_file() に渡されるコールバック php_ini_parser_cb() 内では単純に
    extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));
    zend_llist_add_element(&extension_lists.functions, &extension_name);
php-src/main/php_ini.c line 231 on GitHub

とされているので、多分この理解で問題ない。
ちなみにコードを追っかけるのをやめた理由と、この理解で問題ないとする理由は
    ini_parser_param.ini_parser_cb = ini_parser_cb;
    ini_parser_param.arg = arg;
    CG(ini_parser_param) = &ini_parser_param;
php-src/Zend/zend_ini_parser.y line 200 on GitHub

という形で、コールバックが Compiler Globals に格納された上で ini_parse() を呼び出していたからである。 ini_parse() の実体は Bison により構文定義ファイル(Zend/zend_ini_parser.y)から生成される。

また、以下に示す拡張の読み込み時、開始時にも依存関係をチェックする。
これは PHP スクリプトからの動的な拡張の読み込みを考慮しているものと思う。

拡張の読み込み。関数 zend_register_module_ex() はグローバル変数 module_registry に拡張の情報を保存する。
php-src/Zend/zend_API.c Line 1898 on GitHub

zend_startup_module_ex() は拡張を開始し、マクロ PHP_MINIT_FUNCTION() などにより定義される拡張固有のコールバックを呼び出す。
php-src/Zend/zend_API.c Line 1712 on GitHub


PHP 拡張の読み込み順について調べたきっかけは、 PHP 実行時、というよりスクリプト実行前に常に或る PHP ファイルを読み込むような設定が無いのかを調べていたのが発端だった。
これについては php.ini にauto_prepend_file,auto_append_fileという所望の項目があった。
これの実装について見ると、 php_execute_script(zend_file_handle *primary_file TSRMLS_DC) 内で zend_execute_scripts() が以下のように呼び出される。
    zend_execute_scripts(
        ZEND_REQUIRE TSRMLS_CC, 
        NULL, 
        3, 
        prepend_file_p, primary_file, append_file_p)
php-src/main/main.c line  2507 on GitHub

zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...) は第4引数以降に渡される複数の PHP ファイル zend_file_handle* をその順に実行する。
第3引数はこれら実行するファイルの数を指定する。
php-src/Zend/zend.c line 1298 on GitHub

つまり、上の php_execute_script() 内での zend_execute_scripts() 呼び出しは
  1. prepend_file_p (auto_prepend_file で指定した PHP ファイル)
  2. primary_file (実際の目的の PHP ファイル)
  3. append_file_p (auto_append_file で指定した PHP ファイル)
という順に PHP ファイルを実行する。

2014年1月4日土曜日

PHP 拡張の Windows 向けバイナリ (DLL) を作る

手順は大雑把に以下の通り。
  1. PHP のソースをビルド、インストール
  2. インストールされた開発ツール(.h や phpize など)を用いて拡張をビルド
と、これだけ。

PHP の Windows でのビルドについては Build your own PHP on Windows や Windows で PHP を build する というエントリが非常に参考になった、というより、まんまこれらの手順に従った。
問題はビルドした DLL を頒布して、 PHP ユーザーに使ってもらう際に起こる。

拡張の DLL と、 PHP 本体のバイナリには Build ID と呼ばれる、そのバイナリの特徴を示す文字が埋め込まれる。 PHP 本体と DLL の Build ID が一致していないと、 拡張はロードされない。
つまり、 DLL を頒布しようとしたら、拡張の Build ID を PHP が公式に提供している Windows 用バイナリの Build ID と一致させねばならず、これが非常に難儀なのだ。

実際には Build ID は ZEND_MODULE_BUILD_ID として、 zend_modules.h 内で以下のように定義されている。

#define ZEND_MODULE_BUILD_ID "API" ZEND_TOSTR(ZEND_MODULE_API_NO) ZEND_BUILD_TS ZEND_BUILD_DEBUG ZEND_BUILD_SYSTEM ZEND_BUILD_EXTRA

ZEND_MODULE_API_NO
https://github.com/php/php-src/blob/b209c273027c9697f2a7628ecd950780c089d614/Zend/zend_modules.h#L36
PHP 拡張 API のバージョン番号。

ZEND_BUILD_TS
https://github.com/php/php-src/blob/b209c273027c9697f2a7628ecd950780c089d614/Zend/zend_build.h#L25
スレッドセーフでビルドされたか否かを示す。
スレッドセーフなら ",TS" 非スレッドセーフなら ",NTS" と定義される。
本体の configure 時に --disable-zts を指定すれば非スレッドセーフ、このオプションを指定しなければスレッドセーフとしてビルドされる、はずなのだが、 configure 後に生成される Makefile にはどうあがいても /D ZTS=1 というマクロ定義が入ってくるので、  NTS ビルドの際には Makefile からこのオプションを取り除かなければならない。 /D ZTS=0 ではダメ。

ZEND_BUILD_DEBUG
https://github.com/php/php-src/blob/b209c273027c9697f2a7628ecd950780c089d614/Zend/zend_build.h#L31
デバッグフラグを伴ってビルドされれば ",debug" 、そうでなければ空定義される。
この定数についてはまだ試したことがない。恐らく、本体、拡張ともに、 configure 時に --enable-debug または --enable-debug-pack を指定することでこのフラグが立つのだろう。

ZEND_BUILD_SYSTEM
https://github.com/php/php-src/blob/b209c273027c9697f2a7628ecd950780c089d614/Zend/zend_build.h#L37
ビルド環境が Windows の場合に定義される。
VisualC++ 2010 を用いた場合、この定数は ",VC10" と定義される。

ZEND_BUILD_EXTRA
https://github.com/php/php-src/blob/b209c273027c9697f2a7628ecd950780c089d614/Zend/zend_build.h#L44
for private applications とあることから、 PHP を組み込む製品やアプリ向けに用意されているのだろう。

dl.c の拡張をロードする部分を見ると、拡張の情報が格納されている zend_module_entry 構造体の build_id プロパティと、 PHP 本体の ZEND_MOUDLE_BUILD_ID が比較され一致しないなら拡張はロードされないことがわかる。

PHP 公式から提供される PHP-5.4 の非スレッドセーフ(Non-Thread Safe)のバイナリは API20100525,NTS,VC9 という Build ID を持つ。この場合、 PHP-5.4 向けに拡張の Windows 用バイナリを提供したいなら、 VC9 (VisualC++ 2008) でビルドしてね、ということである。
これが厄介なところで、 Windows の開発環境はちょっと時が経つと手に入れることが難しくなる。 Google で VC9 のダウンロードページがヒットしても、最新版の VC のダウンロードページへリダイレクトされてしまう。日頃からこのへんにアンテナを張って VC の最新版がリリースされた時にとりあえずダウンロードして開発環境を作って、それを保存しておけばいいのかもしれないが、無理。全く無理。
と、これに甘んじていては頒布できる DLL が限定的になってしまう。 5.5 向けのバイナリはあるけど、 5.4 向けのバイナリはありません、とか。

ここまで前置きが長くなったが、 ZEND_MODULE_BUILD_ID の作りが明らかなので、 PHP 本体をビルドする前にそこを好きな様に書き換えればいいだけである。
で、本当に

#define ZEND_MODULE_BUILD_ID "API20100525,NTS,VC9"

みたいにやっちゃうのは、ありっちゃありだけど、少し良心が痛む。
なので、 API バージョンや、 NTS の指定なんかは特に問題がなく一致させることが出来るから、

#define ZEND_MODULE_BUILD_ID "API" ZEND_TOSTR(ZEND_MODULE_API_NO) ZEND_BUILD_TS ZEND_BUILD_DEBUG ",VC9" ZEND_BUILD_EXTRA

という風にやってあげよう。
と、書きはしたが、この方法俺自身まだ試したことがない。以下の様にもっと回りくどいやり方をやっている。
https://github.com/oasynnoum/php-src/commit/69511fff88eedf9c737f3ae3df6bda91c46371f2

Build ID とは別で、あとひとつ PHP 拡張を Windows でビルドする際に躓いた箇所がある。
phpize 後の configure で
Microsoft JScript runtime error: 'PHP_PGI' is undefined
というようなメッセージとともに失敗する。
これに対しては深く考えずに、次のように変数を定義することで何とかなっている。
https://github.com/oasynnoum/php-src/commit/789c3346bcd0d44cd55debde8da74dba206396b2

PHP 本体の configure オプションに --enable-pgi, --with-pgo というものがあるので、このへんの指定でうまくいくのかもしれない。これらのオプションはこのエントリを書いている際に見つけた。