ぐらめぬ・ぜぷつぇんのはてダ(2007 to 2011)

2007年~2011年ごろまで はてなダイアリー に書いてた記事を引っ越してきました。

出力バッファリング関数を用いた疑似ブロックの実証実験

PHPスクリプトは、

<?php ... ?>

の枠の「外」については、HTMLを想定してそのまま出力する。つまり、使いようによってはPerlRubyのDATAセクションのような使い道ができるはず。これを逆手に取り、出力バッファリング関数で意図的にこの枠外の文字列を取得してしまう。

論より証拠。

hoge1.php : 
<?php
$i = 0;

ob_start();
?>
global $i;
$i++;
return "Hello, World! + [$i]";
<?php
$_code = ob_get_contents();
ob_end_clean();

echo eval($_code)."\n";
echo eval($_code)."\n";
echo eval($_code)."\n";
?>

実行結果(PHP5.2.4 on WinXP) :

> php hoge1.php
Hello, World! + [1]
Hello, World! + [2]
Hello, World! + [3]

イイ感じだ!!

create_functionではどうだ?

hoge2.php : 
<?php
ob_start();
?>
static $c = 0;
$c++;
return $a + $b + $c;
<?php
$_code = ob_get_contents();
ob_end_clean();

$lambda = create_function('$a, $b', $_code);

echo $lambda(1, 2)."\n";
echo $lambda(3, 4)."\n";
?>

実行結果:

> php hoge2.php
4   ... 1 + 2 + 1($c)
9   ... 3 + 4 + 2($c)

イイ感じだ!!

後はこれをラップするクラスを用意すればよい。

実は、これだけではイマイチ味付けが足りない。例えばPerlでは

eval {
    my $a = 0;  # lexical変数
    local $SIG{ALRM} = 'IGNORE';   # スタックPushでコードブロックに入る直前の$SIG{ALRM}を退避

の様に、コードブロックの中で独特の変数スコープを活用している。

えーっと、つまり、その、アレです。特にevalを使おうとした場合、どうやって「外の」変数を引き継ぐかも考えないといけません。
単純に、globalなスコープであれば$GLOBALSを利用できますが、例えば関数内でこのevalテクニックを使うとして、関数ローカルな変数をシームレスに扱うにはどうすれば良いか?

その解決策?というか妥協案の一つとして、あまり知名度が高くない(と自分は思っている)extract()関数の利用を思いつきました。