Python Developers Fest 2011.10 で発表しました

最近ネタの切れが悪いと言われます…。

Package documentation より:

This package is a port of a great Perl module called Acme::AKB48 which was created by Hideo Kimura. Although I don’t have the slightest interest in AKB48, I happened to be under the impression that Python world (almost) totally lacks “Acme” essence and feel like it being worth the first Acme to be ported.

http://github.com/moriyoshi/pyakb48
http://pypi.python.org/pypi/pyakb48

俺とPHP

なんか知らないうちにPHPを全然書いたことがないという噂が広まっていて心外すぎるので弁明しておく。長くなるかもしれない。

PHPを使い始めたのはちょうど10年くらい前のことだと思う。4.0.3 あたりが出た頃だ。当時友人たちとシラバスの情報を共有するサイトをつくろうとしていて、PHP を紹介された。データベースに接続してデータの出し入れをするような仕掛けを簡単に作れるという。

それまでは web で何か作るといったら Perl だったし、テンプレートエンジンとかマルチパートパーサとかをまとめたオレオレ web アプリケーションフレームワークを作ってお茶を濁していた。Perl はかなり好きだった。それでも、PHP の手軽さは輝いて見えた。コードをそのまま HTML に埋め込めることでさえ、「poor-man's ASP」だと思って感激していた。マニュアルを見れば、答えが書いてあったから、動くものをあっという間につくり上げることができた。のめり込んでいった。

友人のひとりから、Web 制作会社でのバイトの口を紹介された。PHP で携帯メールを活用したゲームをつくるという。当時は、なぜか頑なに海外からの情報源のみに頼っていたので、mbstring の存在を知らず、iconv 拡張モジュールのみを利用してメール送信を行うプログラムを書くなどした。iconv 関数にバグがあるのを発見して、バグ報告した。はじめてオープンソースのプロダクトに対してパッチを書いた。パッチが採用されて、コミッターにならないかと誘われた。少し迷ったが、断る理由もなかったのでそのとおりにした。

無事ゲームもリリースされ、バイトを辞めることになり、コミッターとして活動する時間を長くとれるようになった。しかし、コミッターとして何をやったらいいのか、見当もつかなかった。そこで、悉にメーリングリストの発言やバグ報告を観察した。やがて、他のコミッターの役割や力関係のようなものが分かってきた。また、プロジェクトとしての目下の問題もわかってきた。バグ報告を処理するところに人手が足りていないようだった。

新しく登録されるバグ報告にひと通り目を通し、自分で処理可能なものは率先して処理することにした。これによって、コミッターとしてのプレゼンスを高めることができ、修正内容によって実力が認められれば、信頼を得られるはずだと思った。そして、その通りになった *1。他の開発者から意見さえ求められるようになった*2。バグ報告者に感謝される喜びが乗じて、さらに作業にのめり込んでいた。今思えば、だいぶ空回りしていたように思う。世界で大勢の人たちが使うプロダクトの品質を、日本の片隅でひとりの人間が日々向上しているのだと思うと、わくわくが止まらなかった。もともとあまり顔を出していなかった大学から、また足が遠のいた。

面白いことばかりでもなかった。PHP のコミッターの一部からは、いい印象を持たれなかったようだ。IRC のコミッターが常駐するチャンネルに出入りするなどして、コミュニケーションの機会を増やすことで理解を得ようとした。ついに、このようなことが発生し、大きくモチベーションを削がれた。大学を卒業することにも力を注がなくてはならなかった。結果、開発からは足が遠のいた。PHP 自体の未来の方向性に対する疑念が生じたというのもあった。

mbstring 拡張モジュールのメンテナンスは長らく廣川さんが行っていたが、多忙のために、廣川さんに代わってバグ報告を処理する機会が多くなった。mbstring が広く使われているということは認識していたので、日常的な開発からは離れたあとも、これだけはコミットメントだと思い、続けた。ある日突然 FacebookOSS チームから GitHub のクーポンが送られてきた。Facebook も mbstring を利用しているらしく、メンテナンスに感謝したいとのことだった。すこし報われた気がした *3

(※続きを読んだらおっぱいおっぱいと書き込んでください)

*1:phpcredits()で Quality Assurance Team に名前があるのはそのため

*2:このときの議論がPHP stream filterの実装に反映している

*3:以前 id:IwamotoTakashi さんにはてなスターを頂いたのが 7 年間活動してきた中での最初の報酬です。大切に使わせてもらっています。

pixiv Tech Meeting #1 にて発表してきました。

ひょんなことで pixiv といろいろ縁があったので発表に至りました。
PHP を JS に変換する「Phjosh (仮)」についてです。

誰にも聞かれなかったのであえて書くと、Phjoshの開発者の脳内発音は「fjosh」みたいな感じです。

GitHubでソースは公開しています。
まだできないことの方が多いので、暇を見て作り込んでいきます。

http://github.com/moriyoshi/phjosh

12/22 追記:
PHP/Parser/Base.php がないと言われますが、これは

kmyacc-forked をインストールして、
http://github.com/moriyoshi/kmyacc-forked

phjosh のディレクトリで make を実行すると生成されます。

java-ja.js #2 で『AAなゲームをJSで』を発表しました

動画をAAに変換するデモは以下で観れます。 (動画がTheoraなのでSafariは非対応)

http://dl.dropbox.com/u/673207/aalibtest/index.html

紹介したサンプルコードその1:

<html>
<body>
<pre id="screen" style="width:80ex; height:25em; line-height:100%; color:white; background-color:black"></pre>
<script type="text/javascript">
function repeat(c, n) { return new Array(n+1).join(c); }
var scr = document.getElementById("screen");
var lines = new Array(25);
var lineChars = repeat(" ", 80);
for (var i = 0; i < lines.length; i++) {
    var line = document.createTextNode(lineChars);
    lines[i] = line;
    scr.appendChild(line);
    scr.appendChild(document.createTextNode("\r\n"));
}
function update(x, y, c) {
    var line = lines[y];
    line.nodeValue = line.nodeValue.substring(0, x) + c + line.nodeValue.substring(x + c.length);
}

var x = 0, y = 0;
update(x, y, "*");
setInterval(function() {
    if (x < 80) {
        update(x, y, " ");
        x++;
        update(x, y, "*");
    }
}, 500);
</script>
</body>
</html>

サンプルコードその2:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<pre id="screen" style="width:80ex; height:25em; line-height:100%; color:white; background-color:black; font-family: Osaka, MS Gothic"></pre>
<script type="text/javascript">
function repeat(c, n) { return new Array(n+1).join(c); }
var scr = document.getElementById("screen");
var lines = new Array(25);
var lineChars = repeat(" ", 40);
for (var i = 0; i < lines.length; i++) {
    var line = document.createTextNode(lineChars);
    lines[i] = line;
    scr.appendChild(line);
    scr.appendChild(document.createTextNode("\r\n"));
}
function update(x, y, c) {
    var line = lines[y];
    line.nodeValue = line.nodeValue.substring(0, x) + c + line.nodeValue.substring(x + c.length);
}

function updateMultiple(x, y, c) {
    for (var i = 0; i < c.length; i++)
        update(x, y + i, c[i]);
}

var characterE = [
    "       ",
    "       ",
    "       ",
    "       ",
    "       "
];

var character = [
    " \ | / ",
    "  / ̄\  ",
    "─(゜∀゜)─",
    "  \_/  ",
    " / | \ "
];

var x = 0, y = 0;
updateMultiple(x, y, character);
setInterval(function() {
    if (x < 40 - 7) {
        updateMultiple(x, y, characterE);
        x++;
        updateMultiple(x, y, character);
    }
}, 200);
</script>
</body>
</html>

サンプルコードその3:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<pre id="screen" style="width:80ex; height:25em; line-height:100%; color:white; background-color:black; font-family: Osaka, MS Gothic"></pre>
<script type="text/javascript">
function repeat(c, n) { return new Array(n+1).join(c); }
var scr = document.getElementById("screen");
var lines = new Array(25);
var lineChars = repeat(" ", 40);
for (var i = 0; i < lines.length; i++) {
    var line = document.createTextNode(lineChars);
    lines[i] = line;
    scr.appendChild(line);
    scr.appendChild(document.createTextNode("\r\n"));
}
function update(x, y, c) {
    var line = lines[y];
    line.nodeValue = line.nodeValue.substring(0, x) + c + line.nodeValue.substring(x + c.length);
}

function updateMultiple(x, y, c) {
    for (var i = 0; i < c.length; i++)
        update(x, y + i, c[i]);
}

var characterE = [
    "       ",
    "       ",
    "       ",
    "       ",
    "       "
];

var character = [
    " \ | / ",
    "  / ̄\  ",
    "─(゜∀゜)─",
    "  \_/  ",
    " / | \ "
];

var c = [];
for (var i = 10; --i >= 0;) {
    var e = { x: Math.random() * (40 - 7) | 0, y: Math.random() * (25 - 5) | 0 }
    c.push(e);
    updateMultiple(e.x, e.y, character);
}
setInterval(function() {
    for (var i = c.length; --i >= 0; )
        updateMultiple(c[i].x, c[i].y, characterE);
    for (var i = c.length; --i >= 0; ) {
        c[i].x++;
        updateMultiple(c[i].x, c[i].y, character);
    }
}, 200);
</script>
</body>
</html>

Python Hack-a-thon で発表してきました!!

今回は資料の作成が後手に回ってしまい、運営の方々や発表者の皆様には大変迷惑をおかけしました。

さて、今回は ctypes 拡張モジュールに関する内容を発表させていただきました。

php-in-python の使い方

PHP-in-Pythonには、まだドキュメントがありません。インストール方法を簡潔に説明します。

a. Windowsの場合
  1. windows.php.netよりThread-safe版のPHPのバイナリをダウンロードします。
  2. ダウンロードしたアーカイブを適当なディレクトリに展開します。
  3. 展開された中身に含まれているphp5ts.dllの場所がPATH環境変数に含まれるようにします。
b. Linux等Free Unixの場合
  1. php.netよりPHPソースコードをダウンロードします。
  2. ソースコードを configure に --enable-maintainer-zts および --enable-embed=shared を渡す形でビルドします。
  3. make install します。
  4. インストールされた ${PREFIX}/lib/libphp5.so が共有ライブラリとして読み込めるように、${PREFIX}/lib が LD_LIBRARY_PATH に含まれるようにします。

以上の手順を踏んだ後で、

import php
print php.eval("1 + 2")

のようにすると、1 + 2 が PHPインタプリタで評価され、その結果が Python に返されます。

import php
print php.run("test.php")

のようにすると、test.php が実行されます。

リクエストパラメータを PHP の実行環境に渡したい場合は

import php
php.eval("var_dump($_GET['aaa'], $_POST['bbb']);", query_string="aaa=GET", content_type="application/x-www-form-urlencoded", post_data="bbb=POST")

のようにします。

おまけ

Mac OS X 上で、ctypes拡張モジュールを使ってダイアログボックスを出すコード。

import ctypes

kCFUserNotificationStopAlertLevel = 0
kCFUserNotificationNoteAlertLevel = 1
kCFUserNotificationCautionAlertLevel = 2
kCFUserNotificationPlainAlertLevel= 3
kCFUserNotificationNoDefaultButtonFlag = (1 << 5)
kCFUserNotificationUseRadioButtonsFlag = (1 << 6)
kCFStringEncodingUTF8 = 0x08000100

dll = ctypes.CDLL('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')

dll.CFStringCreateWithCString.restype = ctypes.c_void_p
dll.CFStringCreateWithCString.argtypes = (
    ctypes.c_void_p,
    ctypes.c_char_p,
    ctypes.c_int)

dll.CFRelease.argtypes = (ctypes.c_void_p,)

dll.CFUserNotificationDisplayNotice.argstype = (
    ctypes.c_double,
    ctypes.c_ulong,
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_void_p)

dll.CFStringCreateWithCString.restype = ctypes.POINTER(ctypes.c_int)
title = dll.CFStringCreateWithCString(None, 'Hey', kCFStringEncodingUTF8)
message = dll.CFStringCreateWithCString(None, 'Hello, binary world.', kCFStringEncodingUTF8)

try:
    dll.CFUserNotificationDisplayNotice(
        ctypes.c_double(0.), kCFUserNotificationNoteAlertLevel,
        None, None, None, title, message, None)
finally:
    dll.CFRelease(title)
    dll.CFRelease(message)