読者です 読者をやめる 読者になる 読者になる

PHPのnewに再チャレンジ PHP5.3

PHP

PHPのnewはこれで置き換えだ - なんたらノート 第二期 で書いたのは、どうもPHP5.3ではダメっぽいです。

PHP5.3では、evalの実行コンテキストが、関数内ではなくグローバルになる(でも変数はローカルのをレキシカルに束縛してる)というトリビアがありました。

<?php
function hoge(){
  eval('return "hoge called with " . func_get_arg(0);');
}
echo hoge("a");
?>

で、

Warning:  func_get_arg():  Called from the global scope - no function context in ***[local\path\to\php]*** : eval()'d code(2) : eval()'d code on line 1

だ、そうです。

たぶん、クロージャで変数スコープに自由が利くようになったからやったんだろうけど、何のメリットがあってこんなことを?って感じ。どうせローカル変数は丸見えで破壊操作ありありなんだし、実行コンテキストが関数外になっても何も意味ありません。むしろ、古いコードの移植性が下がるだけ。eval文字列以外のスタティックな実装コードがバイトコード化されるとかなら、それも仕方ないけど、まあそんなの、バージョン5のうちはないでしょ。

仕方ないので、「PHPのnewはこれで置き換えだ」は、可変長引数の値をいったんローカル変数に取り込んで、evalでそのローカル変数を参照するように変えました。

<?php
function newobj($class) {
   $seppos = strrpos($class, '/');
   if($seppos !== FALSE) {
       require_once substr($class, 0, $seppos);
       $class = substr($class, $seppos + 1);
   }
   $argsvals = func_get_args();
   $code = '$obj = new ' . $class . '(';
   for($i = 1; $i < func_num_args(); $i++) {
       if($i != 1) { $code .= ', '; }
       $code .= '$argsvals[' . $i . ']';
   }
   $code .= ');';
   eval($code);
   return $obj;
}

//便利なつかいかた
newobj('./lib/my_module.php/MyClass', 1, 2, 3)->myMethod(4, 5, 6);

//もちろん普通にこれでもいい
require_once './lib/my_module.php';
newobj('MyClass', 1, 2, 3)->myMethod(4, 5, 6);
?>