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