From 98682c7a8792e7e79e487fea5024d25cee5aa310 Mon Sep 17 00:00:00 2001
From: nsfisis contained unnecessary whitespaces inside it
---
.../phperkaigi-2023-unused-token-quiz-3/index.html | 40 +++++++---------------
1 file changed, 13 insertions(+), 27 deletions(-)
(limited to 'public/posts/2023-01-10')
diff --git a/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html b/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html
index 3100b8e..81d673f 100644
--- a/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html
+++ b/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html
@@ -82,8 +82,7 @@
注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。
- <?php
+ <?php
try {
f(g() / __LINE__);
} catch (Throwable $e) {
@@ -194,8 +193,7 @@
function g() {
return __LINE__;
- }
-
+ }
"Catchline" と名付けた作品。実行するとトークン#base64_decode('SGVsbG8sIFdvcmxkIQ==')が得られる。
@@ -239,8 +237,7 @@
このうち 1つ目のケースは、finally節の中でエラーを投げると PHP 処理系が勝手に$previousを設定してくれる。
- <?php
+ <?php
try {
try {
@@ -253,8 +250,7 @@
// => Error 2
echo $e->getPrevious()->getMessage() . PHP_EOL;
// => Error 1
- }
-
+ }
この知識を元に、トークンの出力部を解析してみる。 @@ -267,8 +263,7 @@ 出力部をコメントや改行を追加して再掲する:
-
- <?php
+ <?php
try {
f(g() / __LINE__);
} catch (Throwable $e) {
@@ -276,8 +271,7 @@
printf('%c', $e->getLine() + 23);
}
echo "\n";
- }
-
+ }
出力をおこなうcatch節を見てみると、Throwable::getPrevious()を呼び出してエラーチェインを辿り、Throwable::getLine()でエラーが発生した行数を取得している。その行数に23なるマジックナンバーを足し、フォーマット指定子%cで出力している。
@@ -287,9 +281,7 @@
フォーマット指定子%cは、整数を ASCII コードと見做して印字する。トークン#base64_decode('SGVsbG8sIFdvcmxkIQ==')のbであれば、ASCII コード98なので、75 行目で発生したエラー、
- 1, 20 => 0 / 0,
-
+ 1, 20 => 0 / 0,
によって表現されている。エラーを起こす方法はいろいろと考えられるが、今回はゼロ除算を使った。
@@ -306,8 +298,7 @@
f()の定義を再掲する (エラーオブジェクトの行数を利用しているので、一部分だけ抜き出すと値が変わることに注意):
- function f(int $i) {
+ function f(int $i) {
if ($i < 0) f();
try {
match ($i) {
@@ -326,24 +317,19 @@
} finally {
f($i - 1);
}
- }
-
+ }
前述のように、finally節でエラーを投げると PHP 処理系が$previousを設定する。ここでは、エラーを繋げるためにf()を再帰呼び出ししている。最初にf()を呼び出している箇所を確認すると、
- <?php
+ <?php
try {
- f(g() / __LINE__); // 3 行目
-
+ f(g() / __LINE__); // 3 行目
-
- function g() {
+ function g() {
return __LINE__; // 111 行目
- }
-
+ }
f()には111 / 3で37が渡されることがわかる。そこから 1 ずつ減らして再帰呼び出ししていき、0 より小さくなったらf()を引数なしで呼び出す。引数の数が足りないと呼び出しに失敗するので、再帰はここで止まる。
--
cgit v1.2.3-70-g09d2