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

# まとめ

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

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

# PHP

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

[PHP: exit - Manual]

>
例3 シャットダウン関数やデストラクタが実行される例
>
```php
>
<?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
<?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`の動作も相まってデストラクタの再認識をした感じがします。

* [PHPにfinallyはないけどデストラクタがあるよ: Architect Note]
* [finallyな書き方、デストラクタな書き方: Architect Note]
* [それでもPHPにfinallyが必要な理由: Architect Note]


[PHP: exit - Manual]:http://php.net/manual/ja/function.exit.php

[PHP: exit - Manual : User Contributed Notes ]:http://php.net/manual/ja/function.exit.php#117146

[4. 組み込み定数 — Python 2.7.x ドキュメント]:http://docs.python.jp/2/library/constants.html#exit

[28.1. sys — システムパラメータと関数 — Python 2.7.x ドキュメント]:http://docs.python.jp/2/library/sys.html#sys.exit

[6. 組み込み例外 — Python 2.7.x ドキュメント]:http://docs.python.jp/2/library/exceptions.html#exceptions.SystemExit

[Uchan Note: Python の exit(), sys.exit(), os._exit() の違い]:http://uchanote.blogspot.jp/2015/01/python-exit-sysexit-osexit.html

[15.1. os — 雑多なオペレーティングシステムインタフェース — Python 2.7.x ドキュメント]:http://docs.python.jp/2/library/os.html?highlight=exit#os._exit


[PHPにfinallyはないけどデストラクタがあるよ: Architect Note]:http://blog.tojiru.net/article/234566904.html
[finallyな書き方、デストラクタな書き方: Architect Note]:http://blog.tojiru.net/article/235572643.html
[それでもPHPにfinallyが必要な理由: Architect Note]:http://blog.tojiru.net/article/294530614.html