Page 1
における
メタプログラミングの温床
Meta programming for PHP
2017-08-20 NEEC Kamata #ll2017jp ODC 2017 Tokyo/ Learn Languages (LL2017)
公開日:
by USAMI Kenta@tadsan
に東京都大田区の日本工学院専門学校 蒲田キャンパスで開催された『Learn Languages 2017』でライトニングトーク(5分)として発表しました。
Meta programming for PHP
2017-08-20 NEEC Kamata #ll2017jp ODC 2017 Tokyo/ Learn Languages (LL2017)
世の中にはいろんなクールな言語がある
PHPには毛ほども興味のなさそうな皆さまにPHPの動的な言語機能を紹介します
サンプルコードは言語機能を簡易に紹介するためのものです
利用の際は必要性と利便性を慎重に判断すること。(メタプログラミングって基本的にそーゆーものですけど…)
アジェンダ
0. 前提1. 初級篇2. 中級篇3. 上級篇
前提
PHPは、所謂LLとしては遊びが少ない
例) 原則として関数やクラスを再定義できない(致命的エラーが発生)
RubyやPythonと違ってオープンクラスではない(モンキーパッチができない)
謎なタイミングでクラス定義が変更されることが(基本的には)ない
=静的解析しやすい条件が揃ってる
その和を乱す邪悪な動的メタプログラミングテクニックが存在する
😇
初級編
callable
関数として呼出し可能な値を表す擬似型
// After$f = $cond ? 'foo' : 'bar';call_user_func($f, $var);
// Before if ($cond) {foo($var);} else {bar($var);}
// こうやって書いてもいい$f($var);
$ary = ["apple", "orange", "banana"];//=> ["Apple", "Orange", "Banana"]
$bry = [];foreach ($ary as $i => $a) {
$cry =array_map('ucfirst', $ary);
$bry[$i] => ucfirst($a);}
compact()extract()
変数テーブルにアクセスできる関数
compact()変数テーブルから取り出して配列にする
$foo = foo();$bar = bar();
hoge(compact('foo','bar'));
hoge(['foo' => $foo,'bar' => $bar]);
extract()変数テーブルから取り出して配列に
function (array $vs){
extract($vs,
EXTR_PREFIX_ALL,
$hoge_foo = $vs['foo'];$hoge_bar = $vs['bar'];
'hoge');
extract()未使用変数の検出できない
読みにくくなるので邪悪
バグの温床であるばかりか、ふつうに脆弱性の温床になる
$$var
$$var可変変数(variable variables)
$world = "こんにちは世界";$hello = "world";$v = "hello";
var_dump($v); //=> "hello"var_dump($$v); //=> "world"var_dump($$$v); //=> "こんにちは世界"// $$ はいくつ付けてもよい!!
debug_backtrace()
スタックトレースを全部取得できる(ファイル・引数含む)
(これを悪用すればどんな邪悪なことができるかは明白なので省略)
中級編
マジックメソッド
__get($name)__set($name, $value)
__get()アクセス不可プロパティを読み出そうとしたときに呼ばれる
__set()アクセス不可プロパティを書き込もうとしたときに呼ばれる
class Hoge {private $values = [];public function __set($name, $value) {$values[$name] = $value;}public function __get($name) {return $values[$name];}}
「アクセス不可」は未定義プロパティだけはなくprivate, protected含む
/*** @property-read string $name*/class Hoge {private $name;public function __construct($name) {$this->name = $name;}public function __get($name) {return $values[$name];}
public function __set($name) {throw new OutOfRangeException();}}
__call($name, $args)
__call()アクセス不可メソッドを起動しようとしたときに呼ばれる
class HogeProxy {private $hoge;public function __construct(Hoge $hoge) {$this->hoge = $hoge;}public function __call($name, $args) {return call_user_func_array($name, $args);}}
/*** @method Foo getFoo()* @method string format(string $tpl)* @method void close()*/class HogeProxy {/** @var Hoge */private $hoge;public function __construct(Hoge $hoge) {$this->hoge = $hoge;}public function __call($name, $args) {
上級編
spl_autoload_register()
クラスが定義されたファイルを自動ロードするための仕組み
(ふつうはComposerがやってくれるので自分で定義する必要ない)
spl_autoload_register(function ($class) {$file = strtr($class, '\\', '/') . '.php';$path = __DIR__ . '/' . $file;if (file_exists($path)) {require $path;}});
クラスが読み込まれる前に介入できる
if (file_exists($path)) {$evil_path = evil_path($path);if (file_exists($evil_path)) {$src = file_get_contents($path);// ファイルを魔改造$evil_src = evil_customize($src);file_put_contents($evil_path, $evil_src);}require $evil_path;}
まとめ
PHPは制限の大い言語のようだが、遊べるポイントはいろいろある
大いなる力には大いなる責任がry
PHPUnit, PsySH, そのほか一部のフレームワークなどはメタプログラミングのテクニックを活用してる
それ用のライブラリもある。(Go! AOP https://github.com/goaop/framework)
実用知識!
そなえよう
関連リンクがありません。
