--- title: "PHPerKaigi 2022 トークン問題の解説" date: 2022-04-09T21:50:19+09:00 draft: false --- # はじめに 本日開始された [PHPerKaigi 2022](https://phperkaigi.jp/2022/) の PHPer チャレンジにおいて、弊社デジタルサーカスの問題を 3問作成した。この記事では、これらの問題の解説をおこなう。 リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens 注意: ネタバレ防止のため、2問目と 3問目はまだ解説を書いていない。 # 第1問 brainf_ck.php ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。 ```php (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); $id = \spl_object_id(...); $put = fn($c) => \printf('%c', $c); $mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); $👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; $👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; $👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; $👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; $📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; $🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { $b => $loop(++$pc, ++$n), $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), default => $loop(++$pc, $n), })($pc, -![])], default => [$mp, ++$pc], }; $🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { +!![] => [$mp, ++$pc], default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { $e => $loop(--$pc, ++$n), $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), default => $loop(--$pc, $n), })($pc, -![])], }; $🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) )($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); $🐘([ $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $🤡, $👉, $👍, $👍, $👍, $👉, $👍, $👍, $👍, $👍, $👍, $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👈, $👈, $👈, $👈, $👎, $🎪, $👉, $👍, $👍, $👍, $👍, $👍, $📝, $👎, $👎, $📝, $👉, $👎, $👎, $👎, $📝, $👉, $👎, $👎, $👎, $📝, $👎, $👎, $📝, $👎, $📝, $👈, $📝, $👉, $👉, $👎, $👎, $📝, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, $👈, $👎, $👎, $👎, $👎, $📝, $👈, $📝, $👉, $👍, $👍, $📝, $👉, $👎, $📝, $👈, $📝, ]); ``` この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。 ## 解説 ### 絵文字 まず目につくのは大量の絵文字だろう。 PHP は識別子に使用できる文字の範囲が広く、絵文字も使うことができる。 ### URL ソースコードのライセンスを示したこの部分だが、 ```php https://creativecommons.org/publicdomain/zero/1.0/ ``` 完全に合法な PHP のコードである。 `https:` 部分はラベル、`//` 以降は行コメントになっている。 ### リテラルなしで数値を生成する ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。 ```php assert(0 === +!![]); assert(1 === +![]); assert(2 === ![]+![]); assert(3 === ![]+![]+![]); assert(10 === +(![].+!![])); ``` `[]` に `!` を適用すると `TRUE` が返ってくる。それに `+` を適用すると、`bool` から `int` ヘの型変換が走り、`1` が生成される。`10` はさらにトリッキーだ。まず `1` と `0` を作り、`.` で文字列として結合する (`'10'`)。これに `+` を適用すると、`string` から `int` への型変換が走り、`10` が生まれる (コード量に頓着しないなら、`1` を 10 個足し合わせてももちろん 10 が作れる)。 ### `if` 文なしで条件分岐 三項演算子ないし `match` 式を使うことで、`if` を一切書かずに条件分岐ができる。 また、`&&` / `||` も使えることがある。 遅延評価が不要なケースでは、`[$t, $f][$cond]` のような形で分岐することもできる。 ### `while`、`for` 文なしでループ 不動点コンビネータを使う (説明は省略)。ここでは、一般に Z コンビネータとして知られるものを使った (`$z`)。 ### プログラム全体 Brainf\*ck。以上。 # 第2問 riddle.php 前述のとおり、ネタバレ防止のため、2問目はまだ解説を書いていない。 # 第3問 toquine.php 前述のとおり、ネタバレ防止のため、3問目はまだ解説を書いていない。