【やさしく解説】PHP8上級試験の模擬問題(8)

PHP
スポンサーリンク

PHP8 上級/準上級試験の認定模擬問題として、プライム・ストラテジー様のPRIME STUDYが認定されているようです。ただ、この模擬問題には解説がありませんので解説をまとめていこうと思います。シリーズ第8回目です。

PHP初級レベルから脱却して中級/上級レベルにいきたい人にピッタリの内容ですね。

PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/ 

問題文

エラーに関する説明の中で、誤っているものを1つ選びなさい。
なお「\」はバックスラッシュに読み替えること。
また、すべてのコードの先頭には下記のコードが書かれているものとする。

declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。

Exception implements Throwable { }
Error implements Throwable { }

(1)
PHP で「何をエラーとして出力し、何をエラーとして出力しないか」の制御は、php.ini の error_reporting で行う。
プログラム実行時に ini_set() 関数を使っても設定できるが、error_reporting() という専用の関数も存在する。
ここには定数 (E_ERROR や E_NOTICE など) を、複数指定する場合はビット和演算子等をつかって指定する事が多い。値は int のため直接数値を入れてもよいが、定数を使う事が推奨されている。
定数には色々あるが、数値で 0 を指定すると「全てのエラー出力をオフにする」事ができる。
error_reporting() 関数で -1 を渡す事があるが、これは「将来のバージョンの PHP で新しいレベルと定数が追加されたとしてもすべてのエラーを表示するようになる (2の補数で、-1は「全てのbitが立つ」値になる)」という意味で使われる。
そのため、以下のコード

error_reporting(0);
$i++;
var_dump($i);

error_reporting(-1);
$j++;
var_dump($j);


を実行すると、結果は次のとおりとなる。

int(1)
Warning: Undefined variable $j in …
int(1)

(2)
set_error_handler() を使うと、ユーザー定義のエラーハンドラ関数を設定する事ができる。
これを使うと「致命的なエラーの際になんらかの後処理が必要な場合」などにその処理を記述できる。
また、書き方によっては「PHP の標準関数のエラー時に、例外を投げる事」や「E_NOTICE であっても例外を投げる」事も可能である。
そのため、以下のコード

set_error_handler(
  function ($errno, $errstr, $errfile, $errline) {
    if (0 !== $errno & error_reporting()) {
      throw new ErrorException( $errstr, 0, $errno, $errfile, $errline);
    }
  }
);

try {
  $i++;
} catch(\Throwable $e) {
  echo 'catch Exception', PHP_EOL;
  echo $e->getMessage(), PHP_EOL;
}


を実行すると、結果は次のとおりとなる。

catch Exception
Undefined variable $i

(3)
PHP のエラーのうち、E_ERRORは「重大な実行時エラー」、E_PARSE は「コンパイル時のパースエラー」となり、いずれもスクリプトの実行は中断される。
一方で、E_WARNING「実行時の警告 (致命的なエラーではない)」とE_NOTICE「実行時の警告」は、スクリプトの実行は中断されない。
そのため、以下のコード

$i++;
var_dump($i);
echo 'fin.';


を実行すると、結果は次のとおりとなる。

Warning: Undefined variable $i in …
int(1)
fin.

(4)
PHP 5 において、Exception クラスは全ての例外の基底クラスであった。PHP 7 において、Exception クラスは「Throwable インタフェース」を基底クラスとしている。
また、PHP 7 以降において Error クラスという「PHP のすべての内部エラーの基底クラス」ができた。
そのため、以下のコード

try {
  throw new \Exception('Exception');
} catch(\Throwable $e) {
  echo 'catch ' , $e->getMessage();
}


を実行すると、結果は

catch Exception 

となる。
同様に、以下のコード

try {
  throw new \Error('Error');
} catch(\Exception $e) {
  echo 'catch ' , $e->getMessage();
}


を実行すると、結果は

catch Error

となる。

解説

選択肢1

正しいです。

PHPのプログラムを実行する際、コードに問題があると「エラー」や「警告」などが表示されます。これを エラーレポート と呼びます。例えば、変数 $i を定義せずに使うと、通常は 「未定義の変数です」 という警告(Warning)が出るといった具合です。


エラーレポートの設定方法は、(1)php.iniで設定、(2)ini_set() 関数を使う、(3)error_reporting() 関数を使う、の3つがあり、ここでは(3)について問われています。

error_reportingは以下のように定数を使う書き方があります。

error_reporting(E_ALL);
  • E_ERROR :致命的なエラー(プログラムが止まる)
  • E_WARNING: 警告(プログラムは動くが問題がある)
  • E_NOTICE :注意(未定義の変数を使った時など)
  • E_ALL :全てのエラーを表示する
  • E_PARSE :構文エラー(スクリプトが実行されない)

ビット演算子を使い、組み合わせて指定することもできます。

error_reporting(E_ERROR | E_WARNING); // 致命的なエラーと警告のみ表示

error_reporting(E_ALL & ~E_NOTICE); // E_NOTICE以外の全てのエラーを表示

一方で、本問のように数値で指定することもでき、error_reporting(0) はすべてのエラーを非表示に、error_reporting(-1) はすべてのエラーを表示するときに使用します。

“-1” を指定すると、将来的にPHPの新しいバージョンで、新しいレベルや定数が追加されたとしても、それらの影響を受けずに全てのエラーを表示します。
“E_ALL”は以前はそのような動きに完全には対応していませんでしたが、現在は”-1″と全く同じ挙動になります。

選択肢2

正しいです。

set_error_handler() は、PHP のエラーハンドリングをカスタマイズできる関数 です。
通常、PHP のエラーは Warning や Notice として出力されますが、この関数を使うと 自分でエラーハンドラを定義 できます。

例えば、エラーが発生したときにログを記録する、致命的なエラーのときに特別な処理をする、PHP のエラーを Exception(例外)として扱う…といったカスタマイズが可能になりますよ

コードを見ていきましょう。

set_error_handler() 関数には 「エラーが発生したときに実行する関数」 を指定します。ここでは以下のように無名関数(function (…) {})を使っています。

function ($errno, $errstr, $errfile, $errline)


この関数はエラーが発生すると自動的に、$errno(エラーの種類:E_NOTICE, E_WARNING など)、$errstr (エラーメッセージ)、$errfile(エラーが発生したファイル名)、$errline(エラーが発生した行番号)を受け取ります。

if (0 !== $errno & error_reporting())

この条件は、現在の error_reporting 設定に基づいてエラーを処理するかどうかを決める ものです。

error_reporting() は、現在有効なエラーレベルを取得する関数
$errno & error_reporting() は、$errno のエラーレベルが error_reporting() の設定で有効かどうかを判定
0 !== は、「無視すべきエラーではない場合」に処理を実行するためのチェック
つまり、「現在有効なエラーなら、処理をする」 という意味になります。

throw new ErrorException(…) は、エラーを ErrorException という例外に変換する ものです。つまり、エラーが発生すると ErrorException という例外が投げられるようになります。

throw new ErrorException($errstr, 0, $errno, $errfile, $errline);

$errstrはエラーメッセージ、0はエラーコード(今回は 0)、$errnoはエラーの種類、$errfileはエラー発生ファイル、$errlineはエラー発生行です。

try-catch文:$i++を実行しようとしますが、変数 $i は 未定義 なので、通常なら E_NOTICE の警告が出ます。ですが先ほどの set_error_handler() により、E_NOTICE のエラーは ErrorException に変換され throw されます。catch(\Throwable $e) で ErrorException をキャッチし、エラーメッセージを出力します。

よって「catch Exception」を出力した後、「Undefined variable $i」が出力されます。

選択肢3

正しいです。
E_ERROR や E_PARSE はスクリプトの実行を中断します。一方、E_WARNING や E_NOTICE は警告を出力しますが、スクリプトの実行は続行されます。

選択肢4

誤っています。
\Error の例外が \Exception で捕捉されることはありません。

PHP5では、Exception クラスが例外の基底クラスでしたが、PHP7以降、Throwable インターフェースを基底として、その派生としてException クラスと Error クラスが別々に存在しています。そのため、throw new \Error(‘Error’) を投げた場合は、catch(\Exception $e) では捕捉されません。

1つ目のコードでは、Exception は Throwable を実装しているため、catch(\Throwable $e) で捕捉できます。

ですが、2つ目のコードでは、Error は Exception のサブクラスではないため、catch(\Exception $e) では捕捉できません。Error は Throwable を実装していますが、Exception とは別系統のクラスなので catch(\Exception $e) ではキャッチされず、未処理の例外として致命的エラー(Fatal error)になります。

正解選択肢

以上より選択肢4が正解となります。

コメント

タイトルとURLをコピーしました