From 7b6319986030fc8a2fb5f851a431b5113e774f60 Mon Sep 17 00:00:00 2001
From: nsfisis
20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n'
- if true puts 'Hello, World!' end
- ^~~~
- 20:1: syntax error, unexpected `end', expecting end-of-input
- ...f true puts 'Hello, World!' end
+if true puts 'Hello, World!' end
+ ^~~~
+20:1: syntax error, unexpected `end', expecting end-of-input
+...f true puts 'Hello, World!' end
二つ目のメッセージは無視して一つ目を読むと、thenか;か改行が来るはずのところ変数だかメソッドだかが現れたことによりエラーとなっているようだ。
diff --git a/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html b/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
index 4d63296..480c2eb 100644
--- a/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
+++ b/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
@@ -126,79 +126,79 @@
#\
- i\
- n\
- c\
- l\
- u\
- d\
- e\
- <\
- s\
- t\
- d\
- i\
- o\
- .\
- h\
- >\
- /*
- */
- i\
- n\
- t\
- /*
- */
- m\
- a\
- i\
- n(
- ){
- f\
- o\
- r(
- i\
- n\
- t\
- /*
- */
- i=
- 1;
- i<
- 1\
- 0\
- 0;
- i\
- +\
- +)
- if
- (i
- %\
- 15
- ==
- 0)
- p\
- r\
- i\
- n\
- t\
- f(
- "\
- F\
- i\
- z\
- z\
- B\
- u\
- z\
- z\
- %\
- c\
- ",
- 10
- );
+i\
+n\
+c\
+l\
+u\
+d\
+e\
+<\
+s\
+t\
+d\
+i\
+o\
+.\
+h\
+>\
+/*
+*/
+i\
+n\
+t\
+/*
+*/
+m\
+a\
+i\
+n(
+){
+f\
+o\
+r(
+i\
+n\
+t\
+/*
+*/
+i=
+1;
+i<
+1\
+0\
+0;
+i\
++\
++)
+if
+(i
+%\
+15
+==
+0)
+p\
+r\
+i\
+n\
+t\
+f(
+"\
+F\
+i\
+z\
+z\
+B\
+u\
+z\
+z\
+%\
+c\
+",
+10
+);
- /* あとは同じように普通のプログラムを変形するだけなので省略 */
+/* あとは同じように普通のプログラムを変形するだけなので省略 */
バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。 @@ -280,11 +280,11 @@ a' まずは普通に書くとしよう。
-<?php
+ <?php
- for ($i = 1; $i < 100; $i++) {
- echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
- }
+for ($i = 1; $i < 100; $i++) {
+ echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
+}
素直に書いた fizzbuzz とは言い難いが、このくらいは普通だということにしておかないと、この先がやっていられないので許してほしい。 @@ -299,12 +299,12 @@ a'
<?php
- $s = range(1, 100);
- array_walk(
+$s = range(1, 100);
+array_walk(
$s,
fn($i) =>
- printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
- );
+ printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+);
array_walkやrange、printfといったforよりも長いトークンが現れてしまったが、これは次節で直すことにする。なお、echoは文 (statement) であり式 (expression) ではないので、式であるprintfに置き換えた。
@@ -319,16 +319,16 @@ a'
<?php
- $r = 'range';
- $w = 'array_walk';
- $p = 'printf';
+$r = 'range';
+$w = 'array_walk';
+$p = 'printf';
- $s = $r(1, 100);
- $w(
+$s = $r(1, 100);
+$w(
$s,
fn($i) =>
- $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
- );
+ $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+);
これで関数を呼び出している所は短くなった。では、$rや$wや$p、また'Fizz'や'Buzz'はどうやって 1行2文字に収めるのか。次のテクニックへ移ろう。
@@ -470,153 +470,153 @@ echo "$r\n";
<?php
- $x
- =#
- 'i
- S'
- ;;
- $y
- ='
- b!
- ';
- $c
- =#
- $x
- ^#
- $y
- ;#
- $x
- =#
- 'x
- Om
- ';
- $y
- ='
- k!
- o'
- ;#
- $r
- =#
- $x
- ^#
- $y
- ;#
- $x
- =#
- 'k
- Sk
- ~}
- Ma
- ';
- $y
- ='
- x!
- s!
- k!
- ';
- $w
- =#
- $x
- ^#
- $y
- ;#
- $x
- =#
- 'z
- Hd
- G'
- ;#
- $y
- ='
- x!
- ~!
- ';
- $p
- =#
- $x
- ^#
- $y
- ;#
- $x
- =#
- 'L
- [p
- ';
- $y
- ='
- c!
- ';
- $f
- =#
- $x
- ^#
- $y
- ;#
- $x
- =#
- 'H
- [p
- ';
- $y
- ='
- _!
- ';
- $b
- =#
- $x
- ^#
- $y
- ;#
- $b
- [1
- ]=
- $c
- (#
- 13
- *9
- );
- $s
- =#
- $r
- (1
- ,(
- 10
- **
- 2)
- );
- $w
- (#
- $s
- ,#
- fn
- (#
- $i
- )#
- =>
- $p
- ((
- (#
- $i
- %3
- ?#
- ''
- :#
- $f
- ).
- (#
- $i
- %5
- ?#
- ''
- :#
- $b
- )?
- :#
- $i
- )#
- .'
- ')
- );
+$x
+=#
+'i
+S'
+;;
+$y
+='
+b!
+';
+$c
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'x
+Om
+';
+$y
+='
+k!
+o'
+;#
+$r
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'k
+Sk
+~}
+Ma
+';
+$y
+='
+x!
+s!
+k!
+';
+$w
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'z
+Hd
+G'
+;#
+$y
+='
+x!
+~!
+';
+$p
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'L
+[p
+';
+$y
+='
+c!
+';
+$f
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'H
+[p
+';
+$y
+='
+_!
+';
+$b
+=#
+$x
+^#
+$y
+;#
+$b
+[1
+]=
+$c
+(#
+13
+*9
+);
+$s
+=#
+$r
+(1
+,(
+10
+**
+2)
+);
+$w
+(#
+$s
+,#
+fn
+(#
+$i
+)#
+=>
+$p
+((
+(#
+$i
+%3
+?#
+''
+:#
+$f
+).
+(#
+$i
+%5
+?#
+''
+:#
+$b
+)?
+:#
+$i
+)#
+.'
+')
+);
<?php
- printf(`
- e\
- c\
- h\
- o\
- \
- 1\
- 2\
- 3\
- `);
+printf(`
+e\
+c\
+h\
+o\
+\
+1\
+2\
+3\
+`);
なお、ここでは簡単のため出力にprintfをそのまま使っているが、実際にはprintfという文字列を合成して可変関数で呼び出す。
diff --git a/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html b/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html
index dcc0de8..be78585 100644
--- a/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html
+++ b/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html
@@ -72,7 +72,8 @@
注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
+ <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -81,7 +82,8 @@
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
- <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+ <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
"And Then There Were None" (そして誰もいなくなった) と名付けた作品。変則 quine (自分自身と同じソースコードを出力するプログラム) になっている。 @@ -94,7 +96,8 @@ 実行してみると、次のような出力が得られる。
-#
+
+ #
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -103,13 +106,15 @@
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
- <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+ <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
1 行目を除き、先ほどのコードとほぼ同じものが出てきた。もう一度実行してみる。
-#
+
+ #
W
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -118,22 +123,25 @@ W
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
-<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+ <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
今度は 2 行目が書き換えられた。すべての行が変化するまで繰り返すと次のようになる。
-#
-W
-E
-L
-O
-V
-E
-P
-H
-P
+
+ #
+ W
+ E
+ L
+ O
+ V
+ E
+ P
+ H
+ P
+
トークン「#WELOVEPHP」が手に入った。 @@ -150,7 +158,9 @@ P Vim で開くと次のようになる (1 行目を抜粋)。
-<?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
+ <?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
+
<200b>と表示されているのは、Unicode の U+200b で、ゼロ幅スペースである。
@@ -175,13 +185,17 @@ P
続いて、トークンへの変換ロジックを解析する。注目すべきはこの部分だ。以下、ゼロ幅スペースは Vim での表示に合わせて<200b>と記載する。
fn($s)=>chr(strlen($s)/3)
+
+ fn($s)=>chr(strlen($s)/3)
+
PHP のstrlen()は文字列のバイト数を返す。1 行目の$sは以下の内容となっており、
$s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>'
+
+ $s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>'
+
このソースコードは UTF-8 で書かれているので、105 バイトになる。それを 3 で割ると 35 となり、これは#の ASCII コードと一致する。他の行も、同様にしてゼロ幅スペースを詰めることで文字列長を調整し、トークンをエンコードしている。
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 6896530..a7f86b3 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
@@ -83,23 +83,23 @@
<?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