これは何?
- 昔の有名な殺人事件を、Exception の try~catch で再現してみます。
- Exceptionの握りつぶしについて「完全に握りつぶす」「1つ以外を握りつぶす」「一切握りつぶさない」の3パターンを使って、徐々に事件の解決に近づきます。
- ソースコード(portpia.php)は最後に全部掲載します。
はじまり
事件発生!
山川社長が何者かに殺害された。
刑事のヤスが事件解決の為、捜査を開始した。
山川社長、文江、ヤスを呼び出す。
new でオブジェクトを作ります。ここではクラス定義は秘密にしておきます。(最後に掲載します)
$objs = [];
$objs[] = new yamakawa();
$objs[] = new fumie();
$objs[] = new yasu();
聴取1回目:自己紹介させます。
Exceptionに重要発言を持たせています。ここでは、自己紹介するだけなので、Exceptionを完全に握りつぶします。
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 吐かないよう握りつぶす
;
}
}
実行結果
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
聴取2回目:事情を聴いてみます。
重要発言(ようはException)を1つだけ聴いてみます。
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 1つだけ吐かせる
$obj->say($e->getMessage());
}
}
実行結果
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人) >>> 実は、昔、詐欺をやっていたという過去があります
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書) >>> 実は、ヤスは私の兄です
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
聴取3回目:真実を吐かせてみる
全ての重要発言、ようは全てのExceptionを聴いてみます。ネストしたExceptionを全て展開しているだけです。
コード
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 全部吐かせる
do {
$obj->say($e->getMessage());
} while($e = $e->getPrevious());
}
}
実行結果
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人) >>> 実は、昔、詐欺をやっていたという過去があります
山川(殺害された人) >>> 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書) >>> 実は、ヤスは私の兄です
文江(山川の秘書) >>> 実は・・・兄の犯罪に加担していました
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
ヤス(捜査している人) >>> 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました
聴取の総括:どこで吐いたのか
事件は解決したんですが、ソースコード中のどこで重要発言があったのか(つまりExceptionが発生したのか)全部出力してみます。
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// どこで吐いたのかも含めて、全部吐かせる
// print $e."\n"; でいいのだけど、出力が見づらいので行頭を少し加工
print preg_replace('/^/m', ' |', $e)."\n";
}
}
実行結果
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
|Exception: 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました in /home/ubuntu/20190227/portopia.php:31
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(23): yamakawa->deepListening()
|#1 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
|#2 {main}
|
|Next Exception: 実は、昔、詐欺をやっていたという過去があります in /home/ubuntu/20190227/portopia.php:25
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
|#1 {main}
:
(以降長いので省略、最後に全部載せてます)
まとめ
- 犯人はヤス
- 重要発言、つまり Exception は聞き漏らさないようにしましょう。
-
catch(Exception $e){ ; }
すると、重要発言は何も聞けない。 - 重要発言を聞くために
catch(Exception $e){ print $e->getMessage(); }
としても、1つしか聞けない。 - 重要発言を聞き漏らさない為には
catch(Exception $e){ print $e; }
とする。
-
- PHP版で力尽きました。もし気に入ってもらえたなら、ほかの言語でも事件を解決してみてください
添付:全ソースコード
portpia.php
<?php
abstract class investigation
{
abstract function whatName();
abstract function listening();
function say($msg) {
print $this->whatName() . " >>> " . $msg . "\n";
}
}
class yamakawa extends investigation
{
function whatName()
{
return '山川(殺害された人) ';
}
function listening()
{
$this->say("社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!");
try {
$this->deepListening();
} catch (Exception $e) {
throw new Exception("実は、昔、詐欺をやっていたという過去があります", 0, $e);
}
}
function deepListening()
{
throw new Exception("実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました");
}
}
class fumie extends investigation
{
function whatName()
{
return '文江(山川の秘書) ';
}
function listening()
{
$this->say("山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!");
try {
$this->deepListening();
} catch (Exception $e) {
throw new Exception("実は、ヤスは私の兄です", 0, $e);
}
}
function deepListening()
{
throw new Exception("実は・・・兄の犯罪に加担していました");
}
}
class yasu extends investigation
{
function whatName()
{
return 'ヤス(捜査している人)';
}
function listening()
{
$this->say("山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!");
try {
$this->deepListening();
} catch (Exception $e) {
throw new Exception("実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります", 0, $e);
}
}
function deepListening()
{
throw new Exception("実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました");
}
}
$objs = [];
$objs[] = new yamakawa();
$objs[] = new fumie();
$objs[] = new yasu();
print "\n";
print "***\n";
print "*** 聴取1回目:自己紹介(Exceptionを完全に握りつぶす)\n";
print "***\n";
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 吐かないよう握りつぶす
;
}
}
print "\n";
print "***\n";
print "*** 聴取2回目:事情を聴く(直近のExceptionのメッセージ1つだけ吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 1つだけ吐かせる
$obj->say($e->getMessage());
}
}
print "\n";
print "***\n";
print "*** 聴取3回目:真実を吐かせる(ネストしているExceptionのメッセージも含め全部吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// 全部吐かせる
do {
$obj->say($e->getMessage());
} while($e = $e->getPrevious());
}
}
print "\n";
print "***\n";
print "*** 聴取の総括:どこで吐いたのか(Exceptionのメッセージ以外の情報も全部吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
print "\n";
try {
$obj->listening();
} catch (Exception $e) {
// どこで吐いたのかも含めて、全部吐かせる
// print $e."\n"; でいいのだけど、出力が見づらいので行頭を少し加工
print preg_replace('/^/m', ' |', $e)."\n";
}
}
添付:全実行結果
PHP7.2で動作確認しましたが、他のバージョンでも動くと思います。
$ php --version
PHP 7.2.15-0ubuntu0.18.04.1 (cli) (built: Feb 8 2019 14:54:22) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.15-0ubuntu0.18.04.1, Copyright (c) 1999-2018, by Zend Technologies
$ php portopia.php
***
*** 聴取1回目:自己紹介(Exceptionを完全に握りつぶす)
***
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
***
*** 聴取2回目:事情を聴く(直近のExceptionのメッセージ1つだけ吐かせる)
***
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人) >>> 実は、昔、詐欺をやっていたという過去があります
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書) >>> 実は、ヤスは私の兄です
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
***
*** 聴取3回目:真実を吐かせる(ネストしているExceptionのメッセージも含め全部吐かせる)
***
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人) >>> 実は、昔、詐欺をやっていたという過去があります
山川(殺害された人) >>> 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書) >>> 実は、ヤスは私の兄です
文江(山川の秘書) >>> 実は・・・兄の犯罪に加担していました
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
ヤス(捜査している人) >>> 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました
***
*** 聴取の総括:どこで吐いたのか(Exceptionのメッセージ以外の情報も全部吐かせる)
***
山川(殺害された人) >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
|Exception: 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました in /home/ubuntu/20190227/portopia.php:31
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(23): yamakawa->deepListening()
|#1 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
|#2 {main}
|
|Next Exception: 実は、昔、詐欺をやっていたという過去があります in /home/ubuntu/20190227/portopia.php:25
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
|#1 {main}
文江(山川の秘書) >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
|Exception: 実は・・・兄の犯罪に加担していました in /home/ubuntu/20190227/portopia.php:54
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(46): fumie->deepListening()
|#1 /home/ubuntu/20190227/portopia.php(137): fumie->listening()
|#2 {main}
|
|Next Exception: 実は、ヤスは私の兄です in /home/ubuntu/20190227/portopia.php:48
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(137): fumie->listening()
|#1 {main}
ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
|Exception: 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました in /home/ubuntu/20190227/portopia.php:77
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(69): yasu->deepListening()
|#1 /home/ubuntu/20190227/portopia.php(137): yasu->listening()
|#2 {main}
|
|Next Exception: 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります in /home/ubuntu/20190227/portopia.php:71
|Stack trace:
|#0 /home/ubuntu/20190227/portopia.php(137): yasu->listening()
|#1 {main}