From 5d5367ed00c22cc194b8a2411b2b4b828751003b Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 31 Aug 2022 23:30:53 +0900 Subject: update hugo --- .../2022-04-09/phperkaigi-2022-tokens/index.html | 388 ++++++++++----------- 1 file changed, 194 insertions(+), 194 deletions(-) (limited to 'docs/posts/2022-04-09/phperkaigi-2022-tokens') diff --git a/docs/posts/2022-04-09/phperkaigi-2022-tokens/index.html b/docs/posts/2022-04-09/phperkaigi-2022-tokens/index.html index c74be74..4d4918e 100644 --- a/docs/posts/2022-04-09/phperkaigi-2022-tokens/index.html +++ b/docs/posts/2022-04-09/phperkaigi-2022-tokens/index.html @@ -15,7 +15,7 @@ - + @@ -50,76 +50,76 @@

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

第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
+
+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 で動かせばトークンが得られる。

解説

絵文字

まず目につくのは大量の絵文字だろう。 @@ -173,18 +173,18 @@ Brainf*ck とは、難解プログラミング言語のひとつであり、こ 今回は、PHP 8.1 から追加された、0O または 0o から始まる八進数リテラルを使った。

URL

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

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

完全に合法な PHP のコードである。 +

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

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

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

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

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

[]! を適用すると true が返ってくる。それに + +

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

[]! を適用すると true が返ってくる。それに + を適用すると、bool から int ヘの型変換が走り、1 が生成される。10 はさらにトリッキーだ。まず 10 を作り、. で文字列として結合する ('10')。これに + を適用すると、string から int @@ -204,57 +204,57 @@ PHP では、型変換を利用することで任意の整数を作り出すこ あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。

第2問 riddle.php

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

-
<?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 を特定する必要がある。

ここでは、私の想定解を解説する。

読解

まずはソースコードを読んでいく。

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

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

-
  $x = $x ^ N;
-

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

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

二進数に変換して、

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

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

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

5文字ごとに区切ったあと、改行で結合している。

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

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

+
  $x = $x ^ N;
+

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

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

二進数に変換して、

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

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

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

5文字ごとに区切ったあと、改行で結合している。

ヒント

次に、ソースコードに書いてあるヒントを読んでいく。