From 9d5ec5e3bc01c6174dea048e118edee579c36565 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 7 Feb 2026 23:06:23 +0900 Subject: fix(style): fix codeblock style for rouge --- .../phperkaigi-2023-unused-token-quiz-3/index.html | 333 ++++++++++----------- 1 file changed, 163 insertions(+), 170 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2023-01-10') diff --git a/services/nuldoc/public/blog/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html b/services/nuldoc/public/blog/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html index d21af422..ecf1e478 100644 --- a/services/nuldoc/public/blog/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html +++ b/services/nuldoc/public/blog/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html @@ -15,7 +15,7 @@ PHPerKaigi 2023: ボツになったトークン問題 その 3|REPL: Rest-Eat-Program Loop - +
@@ -121,119 +121,118 @@ 注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。

-
<?php
-try {
-  f(g() / __LINE__);
-} catch (Throwable $e) {
-  while ($e = $e->getPrevious()) printf('%c', $e->getLine() + 23);
-  echo "\n";
-}
-function f(int $i) {
-  if ($i < 0) f();
-  try {
-    match ($i) {
-      0 => 0 / 0,
-
-
-
-      15, 36 => 0 / 0,
-      14 => 0 / 0,
-      37 => 0 / 0,
-
-
-
-
-
-
-
-
-
-
-      6 => 0 / 0,
-
-      5 => 0 / 0,
-
-      22 => 0 / 0,
-
-
-
-
-      34, 35 => 0 / 0,
-
-
-
-
-
-
-
-
-      25 => 0 / 0,
-      17, 21 => 0 / 0,
-
-      24, 32 => 0 / 0,
-
-
-
-
-
-
-
-      33 => 0 / 0,
-
-      16 => 0 / 0,
-
-
-      18 => 0 / 0,
-
-
-
-
-
-
-
-
-      7 => 0 / 0,
-
-      2 => 0 / 0,
-      1, 20 => 0 / 0,
-      10, 28 => 0 / 0,
-      8, 12, 26 => 0 / 0,
-      4, 9, 13 => 0 / 0,
-
-
-
-
-
-      31 => 0 / 0,
-
-      29 => 0 / 0,
-
-      11 => 0 / 0,
-
-
-
-      3, 19, 23 => 0 / 0,
-
-
-      27 => 0 / 0,
-
-      30 => 0 / 0,
-    };
-  } finally {
-    f($i - 1);
-  }
-}
-
-
-
-
-
-
-
-function g() {
-  return __LINE__;
-}
-
+
<?php +
try { +
f(g() / __LINE__); +
} catch (Throwable $e) { +
while ($e = $e->getPrevious()) printf('%c', $e->getLine() + 23); +
echo "\n"; +
} +
function f(int $i) { +
if ($i < 0) f(); +
try { +
match ($i) { +
0 => 0 / 0, +
+
+
+
15, 36 => 0 / 0, +
14 => 0 / 0, +
37 => 0 / 0, +
+
+
+
+
+
+
+
+
+
+
6 => 0 / 0, +
+
5 => 0 / 0, +
+
22 => 0 / 0, +
+
+
+
+
34, 35 => 0 / 0, +
+
+
+
+
+
+
+
+
25 => 0 / 0, +
17, 21 => 0 / 0, +
+
24, 32 => 0 / 0, +
+
+
+
+
+
+
+
33 => 0 / 0, +
+
16 => 0 / 0, +
+
+
18 => 0 / 0, +
+
+
+
+
+
+
+
+
7 => 0 / 0, +
+
2 => 0 / 0, +
1, 20 => 0 / 0, +
10, 28 => 0 / 0, +
8, 12, 26 => 0 / 0, +
4, 9, 13 => 0 / 0, +
+
+
+
+
+
31 => 0 / 0, +
+
29 => 0 / 0, +
+
11 => 0 / 0, +
+
+
+
3, 19, 23 => 0 / 0, +
+
+
27 => 0 / 0, +
+
30 => 0 / 0, +
}; +
} finally { +
f($i - 1); +
} +
} +
+
+
+
+
+
+
+
function g() { +
return __LINE__; +
}

“Catchline” と名付けた作品。実行するとトークン #base64_decode('SGVsbG8sIFdvcmxkIQ==') が得られる。 @@ -267,21 +266,20 @@ このうち 1つ目のケースは、 finally 節の中でエラーを投げると PHP 処理系が勝手に $previous を設定してくれる。

-
<?php
-
-try {
-  try {
-    throw new Exception("Error 1");
-  } finally {
-    throw new Exception("Error 2");
-  }
-} catch (Exception $e) {
-  echo $e->getMessage() . PHP_EOL;
-  // => Error 2
-  echo $e->getPrevious()->getMessage() . PHP_EOL;
-  // => Error 1
-}
-
+
<?php +
+
try { +
try { +
throw new Exception("Error 1"); +
} finally { +
throw new Exception("Error 2"); +
} +
} catch (Exception $e) { +
echo $e->getMessage() . PHP_EOL; +
// => Error 2 +
echo $e->getPrevious()->getMessage() . PHP_EOL; +
// => Error 1 +
}

この知識を元に、トークンの出力部を解析してみる。 @@ -293,16 +291,15 @@ 出力部をコメントや改行を追加して再掲する:

-
<?php
-try {
-  f(g() / __LINE__);
-} catch (Throwable $e) {
-  while ($e = $e->getPrevious()) {
-    printf('%c', $e->getLine() + 23);
-  }
-  echo "\n";
-}
-
+
<?php +
try { +
f(g() / __LINE__); +
} catch (Throwable $e) { +
while ($e = $e->getPrevious()) { +
printf('%c', $e->getLine() + 23); +
} +
echo "\n"; +
}

出力をおこなう catch 節を見てみると、 Throwable::getPrevious() を呼び出してエラーチェインを辿り、 Throwable::getLine() でエラーが発生した行数を取得している。その行数に 23 なるマジックナンバーを足し、フォーマット指定子 %c で出力している。 @@ -311,8 +308,7 @@ フォーマット指定子 %c は、整数を ASCII コード[1] と見做して印字する。トークン #base64_decode('SGVsbG8sIFdvcmxkIQ==')b であれば、ASCII コード 98 なので、75 行目で発生したエラー、

-
1, 20 => 0 / 0,
-
+
1, 20 => 0 / 0,

によって表現されている。エラーを起こす方法はいろいろと考えられるが、今回はゼロ除算を使った。 @@ -327,42 +323,39 @@ f() の定義を再掲する (エラーオブジェクトの行数を利用しているので、一部分だけ抜き出すと値が変わることに注意):

-
function f(int $i) {
-  if ($i < 0) f();
-  try {
-    match ($i) {
-      0 => 0 / 0, // 12 行目
-
-
-
-      15, 36 => 0 / 0,
-      14 => 0 / 0,
-      37 => 0 / 0,
-
-      // (略)
-
-      30 => 0 / 0, // 97 行目
-    };
-  } finally {
-    f($i - 1);
-  }
-}
-
+
function f(int $i) { +
if ($i < 0) f(); +
try { +
match ($i) { +
0 => 0 / 0, // 12 行目 +
+
+
+
15, 36 => 0 / 0, +
14 => 0 / 0, +
37 => 0 / 0, +
+
// (略) +
+
30 => 0 / 0, // 97 行目 +
}; +
} finally { +
f($i - 1); +
} +
}

前述のように、 finally 節でエラーを投げると PHP 処理系が $previous を設定する。ここでは、エラーを繋げるために f() を再帰呼び出ししている。最初に f() を呼び出している箇所を確認すると、

-
<?php
-try {
-  f(g() / __LINE__); // 3 行目
-
+
<?php +
try { +
f(g() / __LINE__); // 3 行目
-
function g() {
-  return __LINE__; // 111 行目
-}
-
+
function g() { +
return __LINE__; // 111 行目 +
}

f() には 111 / 337 が渡されることがわかる。そこから 1 ずつ減らして再帰呼び出ししていき、0 より小さくなったら f() を引数なしで呼び出す。引数の数が足りないと呼び出しに失敗するので、再帰はここで止まる。 -- cgit v1.3-1-g0d28