はじめに

+

本日開始された PHPerKaigi 2022 の PHPer チャレンジにおいて、弊社デジタルサーカスの問題を 3問作成した。この記事では、これらの問題の解説をおこなう。

+

リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens

+

注意: ネタバレ防止のため、2問目と 3問目はまだ解説を書いていない。

+

第1問 brainf_ck.php

+

ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。

+
<?php
+
+declare(strict_types=0O1);
+
+namespace Dgcircus\PHPerKaigi\Y2022;
+
+/**
+ * @todo
+ * Run this program to acquire a PHPer token.
+ */
+
+https://creativecommons.org/publicdomain/zero/1.0/
+
+\error_reporting(~+!'We are hiring!');
+
+$z = fn($f) => (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

+

ソースコードのライセンスを示したこの部分だが、

+
https://creativecommons.org/publicdomain/zero/1.0/
+

完全に合法な PHP のコードである。 +https: 部分はラベル、// 以降は行コメントになっている。

+

リテラルなしで数値を生成する

+

ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 +PHP では、型変換を利用することで任意の整数を作り出すことができる。

+
assert(0 === +!![]);
+assert(1 === +![]);
+assert(2 === ![]+![]);
+assert(3 === ![]+![]+![]);
+assert(10 === +(![].+!![]));
+

[]! を適用すると TRUE が返ってくる。それに + +を適用すると、bool から int ヘの型変換が走り、1 が生成される。10 +はさらにトリッキーだ。まず 10 を作り、. で文字列として結合する +('10')。これに + を適用すると、string から int +への型変換が走り、10 が生まれる (コード量に頓着しないなら、1 を 10 +個足し合わせてももちろん 10 が作れる)。

+

if 文なしで条件分岐

+

三項演算子ないし match 式を使うことで、if を一切書かずに条件分岐ができる。 +また、&& / || も使えることがある。 +遅延評価が不要なケースでは、[$t, $f][$cond] のような形で分岐することもできる。

+

whilefor 文なしでループ

+

不動点コンビネータを使う (説明は省略)。ここでは、一般に Z +コンビネータとして知られるものを使った ($z)。

+

プログラム全体

+

Brainf*ck。以上。

+

第2問 riddle.php

+

前述のとおり、ネタバレ防止のため、2問目はまだ解説を書いていない。

+

第3問 toquine.php

+

前述のとおり、ネタバレ防止のため、3問目はまだ解説を書いていない。

+