おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

E_ERRORレベルエラーでの終了を抑制してもいいものか

あいかわらず脳みそオーバーフローさせながら、PHP Extensionを書いている。同じことをするstaticメソッドを別のクラスで2つ作っているのに気づいたり (クラスが定まらない時点でクラスが破綻してるのが分かる)、自分の少ないバッファ且つ割り込み命令が多い環境なこのごろは、どうもダメダメ。

今作ってた機能はこんなの。複雑な判定ロジックをC言語で書くと鬱なので、PHPスクリプトで簡単に判定して、その結果をExtensionでキャッシュして利用しようというもの。呼び出し元がExtensionとなる。(前に出力バッファリングの記事で書いた)

  • PHPスクリプトでExtensionのクラスを呼び出す
  • ExtensionのC言語レベルから、zend_eval_stringでコールバックでファイルをincludeする
  • 定数をreturnさせる
  • 定数以外のものが帰ってきたり、evalに失敗したり、ファイルがなかったりしたら、エラーとして無視
  • 成功したら戻り値をキャッシュする。(includeしたファイルからの出力はバッファして破棄する)

これをデータが必要になったときに、自動で行う。ユーザレベルでのPHPスクリプトとは別に判定時のみこのロジックが裏で動くように見せて、互いが影響を与えないように見せる。「見せる」と強調してるのは、実際は見えるだけで、ほんとは中でevalしてるのと同じ状態で、かろうじて変数スコープを差し替えているだけだから。
だいたいうまく行くのだけれど、気になるところは、includeしたスクリプトの中でE_ERRORなど実行継続NGなエラーがあがったとき。ほんとは、本編のコードに一切影響を与えたくないので、出力を破棄して、何事もなかったように本編に戻りたいのだ。
これは、PHPスクリプトだとできないのだけれど、Extensionレベルだと可能。C言語なのに例外ちっくなzend_tryブロックで囲めば、そのまんま捕まえられる。のだけれど、捕まえてしまって何事もなかったように実行を継続していいものかというのが問題となる。
この答えは、「だいたい大丈夫」か、「基本大丈夫」かどちらかだと思う。もちろんすべきではないのだろうけど。
実装時、E_ERRORとE_WARNINGの違いはエラー関数に渡す引数のエラーレベルの違いだけ。その呼び出しの中でE_ERRORは終了へと入っていく。RSHUTDOWN処理は走るし、メモリも適切に解放されるべき。なのだけれど、ここでエラーで終了したモジュールの解放処理がわるいと、解放済みや処理に失敗して壊れたオブジェクトに対して呼び出しを行ったりする可能性がある。
ただ、「互いが影響を与えない」ということでコードが分離してるので壊れたオブジェクトのメソッドを叩くとか、そういうこともないのかな?と考えてるわけです。

ま、いっか、とりあえずその方向で実装して様子みよう