PHPにおけるメタプログラミングの温床

公開日:

東京都大田区日本工学院専門学校 蒲田キャンパスで開催された『LLイベント(Learn Languages 2017)』でライトニングトーク(5分)として発表しました。

Download PDF

スライドテキスト

Page 1

PHPにおけるメタプログラミングの温床

2017-08-20 NEEC Kamata #ll2017jp
ODC 2017 Tokyo/ Learn Languages (LL2017)

Page 2

お前誰よ
うさみけんた (@tadsan) / Zonu.EXE

  • GitHub/Packagistでは id: zonuexe
    • Emacs Lisper, PHPer
  • Qiitaに記事を書いたり変なコメントしてるよ
  • 動的言語はコンピューティングの真髄

Page 3

世の中にはいろんな クールな言語がある

Page 4

PHPには毛ほども興味のなさそうな皆さまにPHPの動的な言語機能を紹介します

Page 5

サンプルコードは言語機能を簡易に紹介するためのものです

Page 6

利用の際は必要性と利便性を慎重に判断すること。
(メタプログラミングって基本的にそーゆーものですけど…)

Page 7

アジェンダ

Page 8

0. 前提
1. 初級篇
2. 中級篇
3. 上級篇

Page 9

前提

Page 10

PHPは、所謂LLとしては遊びが少ない

Page 11

例) 原則として関数やクラスを再定義できない
(致命的エラーが発生)

Page 12

RubyやPythonと違って オープンクラスではない
(モンキーパッチができない)

Page 13

謎なタイミングで
クラス定義が変更される
ことが ない
(基本的には)

Page 14

=静的解析しやすい
条件が揃ってる

Page 15

その和を乱す邪悪な
動的メタプログラミング
テクニックが存在する

Page 16

😇

Page 17

初級編

Page 18

callable

Page 19

関数として呼出し可能な値を表す擬似型

Page 20

callable

// Before
if ($cond) {
foo($var);
} else {
bar($var);
}

// After
$f = $cond ? 'foo' : 'bar';
call_user_func($f, $var);

// こうやって書いてもいい$f($var);

Page 21

map

$ary = ["apple", "orange", "banana"];
//=> ["Apple", "Orange", "Banana"]

$bry = [];

$cry =

foreach ($ary as $i => $a) {

array_map('ucfirst', $ary);

$bry[$i] => ucfirst($a);
}

Page 22

compact()

extract()

Page 23

変数テーブルにアク
セスできる関数

Page 24

compact()

変数テーブルから
取り出して配列にする

Page 25

compact()

$foo = foo(); $bar = bar();

hoge([ hoge(compact('foo','bar'));
'foo' => $foo, 'bar' => $bar
]);

Page 26

extract()

変数テーブルから 取り出して配列に

Page 27

extract()

function (array $vs)
{

$hoge_foo = $vs['foo'];

extract($vs,

EXTR_PREFIX_ALL,

$hoge_bar = $vs['bar'];

'hoge');

本当はissetでチェックすべき

Page 28

extract()
未使用変数の検出できない
読みにくくなるので邪悪
バグの温床であるばかりか、 ふつうに脆弱性の温床になる

Page 29

$$var

Page 30

$$var

可変変数

(variable variables)

Page 31

可変変数

$world = "こんにちは世界";

$hello = "world";
$v = "hello";

var_dump($v); //=> "hello" var_dump($$v); //=> "world"

var_dump($$$v); //=> "こんにちは世界"

//

$$ はいくつ付けてもよい!!

Page 32

debug_backtrace()

Page 33

スタックトレースを

全部取得できる
(ファイル・引数含む)

Page 34

(これを悪用すればどんな邪悪なことができるかは明白なので省略)

Page 35

中級編

Page 36

マジックメソッド

Page 37

__get($name)

__set($name, $value)

Page 38

__get()
アクセス不可プロパティ を読み出そうとしたとき
に呼ばれる

Page 39

__set()
アクセス不可プロパティ を書き込もうとしたとき
に呼ばれる

Page 40

__get(), __set()

class Hoge {

private $values = [];
public function __set($name, $value) {

$values[$name] = $value;

}
public function __get($name) {

return $values[$name];

}

}

Page 41

「アクセス不可」は未定義プロパティだけはなくprivate, protected含む

Page 42

読み出し専用プロパティ

/**
* @property-read string $name

class Hoge {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function __get($name) {
return $values[$name];
}

Page 43

書き込み禁止プロパティ

public function __set($name) {

throw new OutOfRangeException();

}

}

Page 44

__call($name, $args)

Page 45

__call()
アクセス不可メソッドを 起動しようとしたときに
呼ばれる

Page 46

Proxyパターン

class HogeProxy {

private $hoge;
public function __construct(Hoge $hoge) {

$this->hoge = $hoge;

}
public function __call($name, $args) {

return call_user_func_array($name, $args);

}

}

Page 47

Proxyパターン (PHPDoc)

/**

* @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) {

Page 48

上級編

Page 49

spl_autoload _register()

Page 50

クラスが定義された ファイルを自動ロー ドするための仕組み

Page 51

(ふつうはComposerがやってくれるので自分で定義する必要ない)

Page 52

ふつうのクラスローダー

spl_autoload_register(function ($class) {
$file = strtr($class, '\\', '/') . '.php';
$path = __DIR__ . '/' . $file;
if (file_exists($path)) {
require $path;
}
});

Page 53

クラスが読み込まれ
る前に介入できる

Page 54

邪悪クラスローダー

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;
}

Page 55

まとめ

Page 56

PHPは制限の大い言語のようだが、遊べるポイントはいろいろある

Page 57

大いなる力には大いなる責任がry

Page 58

PHPUnit, PsySH, そのほか一部のフレームワークなどはメタプログラミングのテクニックを活用してる

Page 59

それ用のライブラリもある。

(Go! AOP https://github.com/goaop/framework)

Page 60

実用知識!

Page 61

そなえよう