STAGE 3
途中で止まる処理
エラーと後片付け
🎯 ミッション
エラーで処理が途中で止まるとやりかけの準備が残ることを説明し、必ず後片付けをする書き方(finally)と、準備が全部終わってから「できた」の印をつける順番が大切な理由を説明できれば合格。
未達成

ねこ博士
前回、メモリ不足になるとPHPはリクエストを途中で打ち切る、という話をしたね。今日は「途中で止まる」とき何が起きるかを考えよう。メモリ不足に限らず、プログラムはいろいろなエラーで途中で止まる。

うさ美
たとえば、どんなエラーですか?

ねこ博士
読もうとしたファイルがない、0で割り算をした、時間がかかりすぎて打ち切られた、などだ。こういうとき、プログラムは続きの命令を実行せずに、エラーを処理する場所まで一気に戻る。料理の途中で火災報知器が鳴って、作業を放り出して非常口へ向かうようなものだ。

うさ美
放り出された作業は、どうなるんですか?

ねこ博士
そこが問題なんだ。具体的に考えよう。お客さんの名簿を作る処理があるとする。手順は3つだ。
① 名簿用の箱を借りる
② 箱に名前を1人ずつ書く
③ 書いた人数を「人数欄」に書く
そして最後に、使い終わった箱を返す。
① 名簿用の箱を借りる
② 箱に名前を1人ずつ書く
③ 書いた人数を「人数欄」に書く
そして最後に、使い終わった箱を返す。
名簿を作る3つの手順の途中でエラーが起きると、処理は残りの手順をとばしてエラーを処理する場所へ戻る。最後に書いてあった「箱を返す」も実行されず、借りた箱が返されないまま残る

うさ美
②の途中でエラーが起きたら、③と「箱を返す」がとばされてしまいますね。箱は借りっぱなしになります。

ねこ博士
そう。借りっぱなしの箱が少しずつたまっていくのをメモリリーク(漏れ)という。漏れが続くと、いずれ棚が足りなくなる。だから、多くのプログラミング言語には「エラーが起きても起きなくても、必ず最後にこれを実行する」という書き方が用意されている。PHPでは
finally と書く。try {
// ① 箱を借りる ② 名前を書く ③ 人数を書く
名簿を作る();
} finally {
// エラーで途中終了しても、ここは必ず実行される
箱を返す();
}

うさ美
finally に書いておけば、非常口に向かう前に必ず箱を返してくれるんですね。

ねこ博士
その通り。それに、PHPには前に話した「リクエストが終わったら区画の箱をまとめて返す」しくみもある。だから、返し忘れの漏れはリクエストの終わりに回収される。ここまではいいね。

うさ美
はい。

ねこ博士
では、もう1つの問題を考えよう。片付けの係は、名簿を片付けるときに「人数欄」を見て、何人分の箱を片付ければいいかを決めるとする。②の途中でエラーが起きたら、人数欄には何が入っているかな?

うさ美
③は実行されていないので、人数欄にはまだ何も書かれていません。でも、前回の話だと、借りた箱には前に使った人の中身が残っているんでした。ということは、人数欄には前の誰かが残した数が入っているということですか?

ねこ博士
そう。まだ書いていない欄には、意味のない古い数が入っている。これをゴミと呼ぶことが多い。片付けの係がそのゴミの数を「人数」だと信じたら、どうなると思う?

うさ美
実際より多い人数分を片付けようとして、名簿ではない、ほかの箱まで触ってしまうかもしれません。

ねこ博士
そうなんだ。片付けの係が、ほかの処理の箱を書き換えたり返したりしてしまう。これがメモリの中身が壊れる、ということだ。プログラムの動きは、そこから先、予想できなくなる。

うさ美
どうすれば防げるんですか?

ねこ博士
2つの考え方がある。1つは、箱を借りたらすぐに空っぽの初期値(たとえば人数0)を書いてから使い始めること。もう1つは、準備が全部終わってから「できた」の印をつけ、片付けの係は印のついたものだけを片付けることだ。

うさ美
準備が半分のものは、「できた」の印がないから、片付けの係は中身を信じない、ということですか?

ねこ博士
そう。準備の途中でエラーが起きても、片付けの係が判断に使う情報だけは、いつも正しい状態にしておく。これはプログラムを安全に作るための大事な原則なんだ。
💡 C言語の「非常口」PHP自体は C言語というプログラミング言語で書かれている。C言語には finally のような「必ず後片付けをする」しくみがなく、エラーのときに処理の途中から一気に別の場所へ飛ぶ setjmp/longjmp という道具がある。速くて便利だが、飛ぶ前にやりかけていた準備や片付けは、すべてプログラマーが自分で面倒を見なければならない。
【このステージの成果】
エラーが起きると、プログラムは残りの命令をとばしてエラーを処理する場所へ戻る
とばされた「箱を返す」は実行されない → 借りっぱなし=メモリリーク。
finally で必ず後片付けをする
まだ書いていない欄には前の中身(ゴミ)が入っている。片付けの係がそれを信じると、ほかの箱を壊す
原則:借りたらすぐ初期値を書く/準備が全部終わってから「できた」の印をつける
確認クイズ
Q1. 処理の途中でエラーが起きたとき、ふつうのプログラムの動きは?
正解! 残りの命令はとばされる。そのため、最後に書いた「箱を返す」なども実行されないことがある。
Q2. finally に書いた処理は、いつ実行される?
正解! どんな終わり方でも必ず実行されるので、後片付けを書くのに向いている。
Q3. 準備の途中でエラーが起きても、片付けの係が間違えないようにするには?
正解! まだ書いていない欄にはゴミが入っている。判断に使う情報を、いつも正しい状態にしておくのが原則。