使ってはいけないPHPメタプログラミングの温床

公開日:

Download PDF

スライドテキスト

Page 1

使ってはいけない
   メタプログラミング 初級篇

Dangerous PHP Meta programming Tutorial

#phpstudy

2017-08-30 PHP勉強会@東京

Page 2

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

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

Page 3

はじめに

Page 4

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

Page 5

利用の際は必要性と利便性
慎重に判断すること。

Page 6

アジェンダ

Page 7

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

Page 8

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

Page 9

Page 10

メタプロ
グラミング

Page 11

非常に雑にまとめる と、一見して意味の わかりにくい謎テク

Page 12

前提

Page 13

メタプログラミング(metaprogramming)と はプログラミング技法の一種で、ロジックを 直接コーディングするのではなく、あるパター ンをもったロジックを生成する高位ロジック によってプログラミングを行う方法、またそ
の高位ロジックを定義する方法のこと。

メタプログラミング

https://ja.wikipedia.org/wiki/

Page 14

直接コーディングするのではなく

メタプログラミング

https://ja.wikipedia.org/wiki/

Page 15

突き詰めていくと

関数呼び出しすら
「直接コーディング」 しないと解釈できる

Page 16

それよりはちょっと

複雑なものが
メタプログラミング

Page 17

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

Page 18

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

Page 19

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

Page 20

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

Page 21

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

Page 22

変数スコープが
(比較的)シンプル

Page 23

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

Page 24

😇

Page 25

「一見してわかりにくい言語機能」

「ユーザー定義関数では不可能な マジックを実現してくる謎関数」

Page 26

初級編

Page 27

callable

Page 28

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

Page 29

map

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

//

配列の中身を一個一個変更する

$bry = [];
foreach ($ary as $i => $a) { $bry[$i] = ucfirst($a);
}

Page 30

map

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

//

配列の中身を一個一個変更する

$cry = array_map('ucfirst', $ary);

Page 31

map

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

$bry = [];

$cry =

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

array_map('ucfirst', $ary);

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

Page 32

callable

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

Page 33

callable

//
After

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

//
こうやって書いてもいい

$f($var);

Page 34

callable

// //
Before After

if ($cond) { $f = $cond ? 'foo' : 'bar'; foo($var); call_user_func($f, $var);
} else {
bar($var); //

こうやって書いてもいい

}

$f($var);

Page 35

使ってはいけない度
★★☆☆☆

僕はコードレビューで「foreachの方が読

みやすい」ってマジレスすることもある
(★が多いほど邪悪)

Page 36

(メタプログラミングと
呼ぶかは議論がある)

Page 37

PHPの「ふつう」は ひとによって境界が
異なる

Page 38

compact()

extract()

Page 39

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

Page 40

compact()

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

Page 41

compact()

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

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

Page 42

使ってはいけない度
★☆☆☆☆

何が起こるか把握して

書くぶんには罠が少ない
(★が多いほど邪悪)

Page 43

extract()

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

Page 44

extract()

function hoge(array $vs)
{

$hoge_foo = $vs['foo'];

extract($vs,

EXTR_PREFIX_ALL,

$hoge_bar = $vs['bar'];

'hoge');

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

Page 45

使ってはいけない度
★★★★★

何が起こるか把握して

書くぶんには罠が少ない
(★が多いほど邪悪)

Page 46

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

Page 47

$$var

Page 48

$$var

可変変数

(variable variables)

Page 49

可変変数

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

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

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

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

//

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

Page 50

使ってはいけない度
★★★★★

純粋に読みにくい……。error_reporing

のレベルが低いと警告なしでnullになる。
(★が多いほど邪悪)

Page 51

debug_backtrace()

Page 52

スタックトレースを

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

Page 53

Baguette\FriendsClass\CalledByOutsiderException: It is not allowed for
Baguette\Ore to call in ~/friends/src/functions.php on line 35

Call Stack:
0.0003 359288 1. {main}() ~/friends/tests/test.php:0
0.0020 552824 2. Baguette\Omae->callFriend() ~/friends/tests/
test.php:33
0.0020 552864 3. Baguette\Ore->__construct() ~/friends/tests/
test.php:20
0.0020 552864 4. Baguette\assert_called_by_friend() ~/friends/
tests/test.php:12

Page 54

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

Page 55

超べんりな用途 (ロギング)

function db($query, array $param) {

$trace = implode('; ', array_map(function ($t) {
return "{$t['file']}({$t['line']})";
}, debug_backtrace()));

$stmt = PDO::query($query.' -- '.$trace); // SQLスロークエリログの末尾にトレースが

// 残るので、原因の特定しやすくなる!

Page 56

邪悪な用途

function hoge($a, $b, $c) {

$trace = debug_backtrace(true, 2);
$class = $trace[1]['class'] ?? false;

$func = $trace[1]['func'] ?? false;

if ($class === "Foo" && $func === "bar") {
//

Foo::bar() はバグってるので値を上書き

$b = "piyo";

}

Page 57

使ってはいけない度
★★★☆☆

ロギングは問題なし。

ワークアラウンドはギルティ。
(★が多いほど邪悪)

Page 58

まとめ

Page 59

今回は初級篇

Page 60

プログラミングのおも しろテクを知ったら使っ てみたくなるのが人情

Page 61

大いなる力には
大いなる責任が伴う

Page 62

メタプログラミング の採用で劇的に便利
になることもある

Page 63

単に初心者殺しな

意味不明なコードに
なることがある

Page 64

(経験豊富な先輩に「若 いなあ」となまあた たかく見られるかも)

Page 65

用法容量を守って

たのしい
プログラミングを

To Be Continued