LoginSignup
9

More than 5 years have passed since last update.

PHPとPythonのfinallyとexitの関係の違い

Last updated at Posted at 2016-09-01

まとめ

  • Pythonのsys.exit()exitは組み込み例外SystemExitを送出する。
    よってtry-catchの一部として振る舞いfinallyにも入る。
    • 通常の例外やエラーとして誤ってcatchに捉えられないようにSystemExitBaseExceptionを継承している。(意識してcatchすることもできる。)
      つまり、シャットダウン処理は上位のSystemExitを受け取った場所で行われていると考えればいい。
  • PHPのexit()はシャットダウン処理なので、デストラクタやシャットダウン関数は実行されるが、例外機構としてのfinallyは通過しない。

exitを例外として処理するかしないかの違いですね。

PHP

exitの解説やユーザーノートにあります。

PHP: exit - Manual

例3 シャットダウン関数やデストラクタが実行される例


<?php
class Foo
{
    public function __destruct()
    {
        echo 'Destruct: ' . __METHOD__ . '()' . PHP_EOL;
    }
}

function shutdown()
{
    echo 'Shutdown: ' . __FUNCTION__ . '()' . PHP_EOL;
}

$foo = new Foo();
register_shutdown_function('shutdown');

exit();
echo 'これは出力されません。';
?>

上の例の出力は以下となります。

 Shutdown: shutdown()
 Destruct: Foo::__destruct()

PHP: exit - Manual : User Contributed Notes

A side-note for the use of exit with finally: if you exit somewhere in a try block, the finally won't be executed. Could not sound obvious: for instance in Java you never issue an exit, at least a return in your controller; in PHP instead you could find yourself exiting from a controller method (e.g. in case you issue a redirect).

Here follows the POC:

<?php
echo "testing finally wit exit\n";

try {
    echo "In try, exiting\n";

    exit;
} catch(Exception $e) {
    echo "catched\n";
} finally {
    echo "in finally\n";
}

echo "In the end\n";
?>

This will print:

testing finally wit exit
In try, exiting

exitの代わりに例外をthrowして、catchで終了処理を書け、というものもありますが、完璧な代案ではないような。

PHP7で動かしてみても当然ですが同様の結果でした。

Python

Uchan Note: Python の exit(), sys.exit(), os._exit() の違い
を見れば一目です。

os._exit()以外は例外の送出なので、finallyも関連します。
os._exit()がPHPのexit()相当かと言われると、またちょっと違うような説明に見えます。

exit

4. 組み込み定数 — Python 2.7.x ドキュメント

表示されたときに “Use quit() or Ctrl-D (i.e. EOF) to exit” のようなメッセージを出力し、呼び出されたときには指定された終了コードを伴ってSystemExitを送出するオブジェクトです。

sys.exit()

28.1. sys — システムパラメータと関数 — Python 2.7.x ドキュメント

Pythonを終了します。exit()SystemExitを送出するので、tryステートメントのfinally節に終了処理を記述したり、上位レベルで例外を捕捉してexit処理を中断したりすることができます。
(中略)
究極には、exit()は例外を送出する “だけ” なので、これがメインスレッドから呼び出されたときは、プロセスを終了するだけで、例外は遮断されません。

6. 組み込み例外 — Python 2.7.x ドキュメント

この例外は sys.exit() 関数によって送出されます。この例外が処理されなかった場合、スタックのトレースバックを全く表示することなく Python インタプリタは終了します。関連値が通常の整数であれば、システム終了ステータスを表します (exit() 関数に渡されます)。値が None の場合、終了ステータスは 0 です。 (文字列のような) 他の型の場合、そのオブジェクトの値が表示され、終了ステータスは 1 になります。

この例外のインスタンスは属性 code を持ちます。この値は終了ステータスまたはエラーメッセージ (標準では None) に設定されます。また、この例外は厳密にはエラーではないため、 StandardError ではなく BaseException から派生しています。

sys.exit() は、クリーンアップのための処理 (try 文の finally 節) が実行されるようにするため、またデバッガが制御不能になるリスクを冒さずにスクリプトを実行できるようにするために例外に翻訳されます。即座に終了することが真に強く必要であるとき (例えば、 fork() を呼んだ後の子プロセス内) には os._exit() 関数を使うことができます。

この例外は Exception を捕まえるコードに間違って捕まえられないように、 StandardError や Exception からではなく BaseException を継承しています。これにより、この例外は着実に呼出し元の方に伝わっていってインタプリタを終了させます。

バージョン 2.5 で変更: BaseException を継承するように変更されました。

まさしく!ですね。

os._exit()

15.1. os — 雑多なオペレーティングシステムインタフェース — Python 2.7.x ドキュメント

終了ステータス n でプロセスを終了します。この時クリーンアップハンドラーの呼び出しや、標準入出力バッファーのフラッシュなどは行いません。

関係記事

同一ブログから。

finally無い時代から実装後の時代への移り変わりですけど、
今回のexitの動作も相まってデストラクタの再認識をした感じがします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9