From 4681e861efcdf9b0b73e3803e70712bc55162863 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 10:38:18 +0900 Subject: feat(slides): improve slide controller --- .../2022-04-09/phperkaigi-2022-tokens/index.html | 419 +++++++++++---------- 1 file changed, 217 insertions(+), 202 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2022-04-09/phperkaigi-2022-tokens/index.html') diff --git a/services/nuldoc/public/blog/posts/2022-04-09/phperkaigi-2022-tokens/index.html b/services/nuldoc/public/blog/posts/2022-04-09/phperkaigi-2022-tokens/index.html index 7cc11fc6..cfdec959 100644 --- a/services/nuldoc/public/blog/posts/2022-04-09/phperkaigi-2022-tokens/index.html +++ b/services/nuldoc/public/blog/posts/2022-04-09/phperkaigi-2022-tokens/index.html @@ -15,7 +15,7 @@ PHPerKaigi 2022 トークン問題の解説|REPL: Rest-Eat-Program Loop - +
@@ -166,75 +166,76 @@
brainf_ck.php
-
<?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
+
+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 で動かせばトークンが得られる。 @@ -259,28 +260,29 @@ なお、brainf*ck プログラムを普通の書き方で書くと、次のようになる。

-
+ + + + + + + + + + -
[ -
> + + + -
> + + + + + -
> + + + + + + + + + + + + -
> + + + + + + + + + + -
< < < < - -
] -
> + + + + + . -
- - . -
> - - - . -
> - - - . -
- - . -
- . -
< . -
> > - - . -
+ + + + + + + . -
< - - - - . -
< . -
> + + . -
> - . -
< .
+
+ + + + + + + + + +
+[
+  > + + +
+  > + + + + +
+  > + + + + + + + + + + + +
+  > + + + + + + + + + +
+  < < < < -
+]
+> + + + + + .
+- - .
+> - - - .
+> - - - .
+- - .
+- .
+< .
+> > - - .
++ + + + + + + .
+< - - - - .
+< .
+> + + .
+> - .
+< .
+

実行結果はこちら: https://ideone.com/22VWmb @@ -336,7 +338,8 @@ ソースコードのライセンスを示したこの部分だが、

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

完全に合法な PHP のコードである。 https: 部分はラベル、// 以降は行コメントになっている。 @@ -348,11 +351,12 @@ ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。PHP では、型変換を利用することで任意の整数を作り出すことができる。

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

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

riddle.php
-
<?php -
-
/********************************************************* -
* This program displays a PHPer token. * -
* Guess 'N'. * -
* * -
* Hints: * -
* - N itself has no special meaning, e.g., 42, 8128, * -
* it is selected at random. * -
* - Each element of $token represents a single letter. * -
* - One letter consists of 5x5 cells. * -
* - Remember, the output is a complete PHPer token. * -
* * -
* License: * -
* https://creativecommons.org/publicdomain/zero/1.0/ * -
*********************************************************/ -
const N = 0 /* Change it to your answer. */; -
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); -
-
$token = [ -
0x14B499C, -
0x0BE34CC, 0x01C9C69, -
0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69, -
0x01C1C66, 0x0FC1C47, 0x01C1C66, -
0x10C5858, 0x1E4E3B8, 0x1A2F2F8, -
]; -
foreach ($token as $x) { -
$x = $x ^ N; -
-
$x = sprintf('%025b', $x); -
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -
$x = implode("\n", str_split($x, length: 5)); -
echo "{$x}\n\n"; -
}
+
<?php
+
+/*********************************************************
+ * This program displays a PHPer token.                  *
+ * Guess 'N'.                                            *
+ *                                                       *
+ * Hints:                                                *
+ * - N itself has no special meaning, e.g., 42, 8128,    *
+ *   it is selected at random.                           *
+ * - Each element of $token represents a single letter.  *
+ * - One letter consists of 5x5 cells.                   *
+ * - Remember, the output is a complete PHPer token.     *
+ *                                                       *
+ * License:                                              *
+ *   https://creativecommons.org/publicdomain/zero/1.0/  *
+ *********************************************************/
+const N = 0 /* Change it to your answer. */;
+assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
+
+$token = [
+  0x14B499C,
+  0x0BE34CC, 0x01C9C69,
+  0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69,
+  0x01C1C66, 0x0FC1C47, 0x01C1C66,
+  0x10C5858, 0x1E4E3B8, 0x1A2F2F8,
+];
+foreach ($token as $x) {
+  $x = $x ^ N;
+
+  $x = sprintf('%025b', $x);
+  $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+  $x = implode("\n", str_split($x, length: 5));
+  echo "{$x}\n\n";
+}
+

さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。トークンを得るためには、ソースコードを読み、定数 N を特定する必要がある。 @@ -437,33 +442,38 @@ まずはソースコードを読んでいく。

-
$token = [ -
// 略 -
];
+
$token = [
+  // 略
+];
+

数値からなる $token があり、各要素をループしている。

-
$x = $x ^ N;
+
$x = $x ^ N;
+

まずは排他的論理和 (xor) を取り、

-
$x = sprintf('%025b', $x);
+
$x = sprintf('%025b', $x);
+

二進数に変換して、

-
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+

0 を空白に、1 を # にし、

-
$x = implode("\n", str_split($x, length: 5));
+
$x = implode("\n", str_split($x, length: 5));
+

5文字ごとに区切ったあと、改行で結合している。 @@ -501,49 +511,52 @@ N は高々

-
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
+
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
+

なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。

-
<?php -
-
$x = 0x14B499C; -
-
$x = $x ^ N; -
-
$x = sprintf('%025b', $x); -
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -
$x = implode("\n", str_split($x, length: 5)); -
-
assert($x === -
" # # \n" . -
"#####\n" . -
" # # \n" . -
"#####\n" . -
" # # ");
+
<?php
+
+$x = 0x14B499C;
+
+$x = $x ^ N;
+
+$x = sprintf('%025b', $x);
+$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+$x = implode("\n", str_split($x, length: 5));
+
+assert($x ===
+" # # \n" .
+"#####\n" .
+" # # \n" .
+"#####\n" .
+" # # ");
+

この一連の変換に対する逆変換を考えると、次のようになる。

-
<?php -
-
$x = -
" # # \n" . -
"#####\n" . -
" # # \n" . -
"#####\n" . -
" # # "; -
-
$x = implode('', explode("\n", $x)); -
$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x); -
$x = bindec($x); -
-
$n = $x ^ 0x14B499C; -
-
echo "N = $n\n";
+
<?php
+
+$x =
+" # # \n" .
+"#####\n" .
+" # # \n" .
+"#####\n" .
+" # # ";
+
+$x = implode('', explode("\n", $x));
+$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x);
+$x = bindec($x);
+
+$n = $x ^ 0x14B499C;
+
+echo "N = $n\n";
+

これを実行すると、N が得られる。 @@ -559,41 +572,43 @@

toquine.php
-
<?php -
-
// License: https://creativecommons.org/publicdomain/zero/1.0/ -
// This is a quine-like program to generate a PHPer token. -
// Execute it like this: php toquine.php | php | php | php | ... -
-
$s = <<<'Q' -
<?cuc -
// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/ -
// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra. -
// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ... -
%f$f = %f; -
$f = fge_ebg13($f); $kf = [ -
%f, -
]; -
$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr -
$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a"; -
$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); -
cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); -
Q; -
$s = str_rot13($s); $xs = [ -
0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, -
0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, -
0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, -
]; -
$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else -
$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; -
$ws = array_map(fn($w) => implode(', ', $w), array_chunk(array_map(fn($x) => sprintf('0x' . chr(37) . '07X', $x), $xs), 10)); -
printf($s, $t, str_rot13("<<<'D'\n{$s}\nD"), implode(",\n", $ws));
+
<?php
+
+// License: https://creativecommons.org/publicdomain/zero/1.0/
+// This is a quine-like program to generate a PHPer token.
+// Execute it like this: php toquine.php | php | php | php | ...
+
+$s = <<<'Q'
+<?cuc
+// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/
+// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra.
+// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ...
+%f$f = %f;
+$f = fge_ebg13($f); $kf = [
+%f,
+];
+$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr
+$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], ['  ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a";
+$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10));
+cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf));
+Q;
+$s = str_rot13($s); $xs = [
+0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7,
+0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F,
+0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7,
+];
+$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else
+$t .= implode("\n", str_split(str_replace(['0','1'], ['  ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n";
+$ws = array_map(fn($w) => implode(', ', $w), array_chunk(array_map(fn($x) => sprintf('0x' . chr(37) . '07X', $x), $xs), 10));
+printf($s, $t, str_rot13("<<<'D'\n{$s}\nD"), implode(",\n", $ws));
+

コメントにもあるとおり、次のようにして実行すれば答えがでてくる。

-
$ php toquine.php | php | php | php | ...
+
$ php toquine.php | php | php | php | ...
+

実際にはもう少しパイプで繋げなければならない。 -- cgit v1.3.1