From 7b6319986030fc8a2fb5f851a431b5113e774f60 Mon Sep 17 00:00:00 2001
From: nsfisis
<?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 {
+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,
+ 0 => 0 / 0,
- 15, 36 => 0 / 0,
- 14 => 0 / 0,
- 37 => 0 / 0,
+ 15, 36 => 0 / 0,
+ 14 => 0 / 0,
+ 37 => 0 / 0,
@@ -110,16 +110,16 @@
- 6 => 0 / 0,
+ 6 => 0 / 0,
- 5 => 0 / 0,
+ 5 => 0 / 0,
- 22 => 0 / 0,
+ 22 => 0 / 0,
- 34, 35 => 0 / 0,
+ 34, 35 => 0 / 0,
@@ -128,10 +128,10 @@
- 25 => 0 / 0,
- 17, 21 => 0 / 0,
+ 25 => 0 / 0,
+ 17, 21 => 0 / 0,
- 24, 32 => 0 / 0,
+ 24, 32 => 0 / 0,
@@ -139,12 +139,12 @@
- 33 => 0 / 0,
+ 33 => 0 / 0,
- 16 => 0 / 0,
+ 16 => 0 / 0,
- 18 => 0 / 0,
+ 18 => 0 / 0,
@@ -153,37 +153,37 @@
- 7 => 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,
+ 2 => 0 / 0,
+ 1, 20 => 0 / 0,
+ 10, 28 => 0 / 0,
+ 8, 12, 26 => 0 / 0,
+ 4, 9, 13 => 0 / 0,
- 31 => 0 / 0,
+ 31 => 0 / 0,
- 29 => 0 / 0,
+ 29 => 0 / 0,
- 11 => 0 / 0,
+ 11 => 0 / 0,
- 3, 19, 23 => 0 / 0,
+ 3, 19, 23 => 0 / 0,
- 27 => 0 / 0,
+ 27 => 0 / 0,
- 30 => 0 / 0,
+ 30 => 0 / 0,
};
- } finally {
+ } finally {
f($i - 1);
- }
- }
+ }
+}
@@ -191,9 +191,9 @@
- function g() {
- return __LINE__;
- }
+function g() {
+ return __LINE__;
+}
"Catchline" と名付けた作品。実行するとトークン#base64_decode('SGVsbG8sIFdvcmxkIQ==')が得られる。
@@ -239,18 +239,18 @@
<?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
- }
+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
+}
この知識を元に、トークンの出力部を解析してみる。 @@ -264,14 +264,14 @@
<?php
- try {
- f(g() / __LINE__);
- } catch (Throwable $e) {
- while ($e = $e->getPrevious()) {
- printf('%c', $e->getLine() + 23);
- }
- echo "\n";
- }
+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で出力している。
@@ -281,7 +281,7 @@
フォーマット指定子%cは、整数を ASCII コードと見做して印字する。トークン#base64_decode('SGVsbG8sIFdvcmxkIQ==')のbであれば、ASCII コード98なので、75 行目で発生したエラー、
1, 20 => 0 / 0,
+ 1, 20 => 0 / 0,
によって表現されている。エラーを起こす方法はいろいろと考えられるが、今回はゼロ除算を使った。 @@ -299,9 +299,9 @@
function f(int $i) {
- if ($i < 0) f();
- try {
- match ($i) {
+ if ($i < 0) f();
+ try {
+ match ($i) {
0 => 0 / 0, // 12 行目
@@ -313,23 +313,23 @@
// (略)
30 => 0 / 0, // 97 行目
- };
- } finally {
- f($i - 1);
- }
- }
+ };
+ } finally {
+ f($i - 1);
+ }
+}
前述のように、finally節でエラーを投げると PHP 処理系が$previousを設定する。ここでは、エラーを繋げるためにf()を再帰呼び出ししている。最初にf()を呼び出している箇所を確認すると、
<?php
- try {
- f(g() / __LINE__); // 3 行目
+try {
+ f(g() / __LINE__); // 3 行目
function g() {
- return __LINE__; // 111 行目
- }
+ return __LINE__; // 111 行目
+}
f()には111 / 3で37が渡されることがわかる。そこから 1 ずつ減らして再帰呼び出ししていき、0 より小さくなったらf()を引数なしで呼び出す。引数の数が足りないと呼び出しに失敗するので、再帰はここで止まる。
--
cgit v1.2.3-70-g09d2