プログラミング

プログラミング初心者のPHP修羅の道#10〜PHP8上級問題を徹底追求する〜

PHP8上級資格取得へ向けたアウトプット記事になります。

PHP試験運営団体より公開されている模擬試験を1つ1つみていく形で事細かくみていきますが、勉強途中ゆえ間違っている部分に関してはご了承ください。

PHP上級資格のメイン教材はPHPマニュアルと指定されていますので、このマニュアルを主に踏襲した内容となっています。

主な勉強教材は以下になります。

1.独習PHP第4版

2.はじめてのPHP

 

3.PHP公式マニュアル

 

PHP8上級試験模擬問題

試験問題8

 

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

declare(strict_types=1);
error_reporting(-1);

下記はマニュアルから一部引用した内容である。

Exception implements Throwable { }
Error implements Throwable { }

 

 

選択肢①

 

PHPで「何をエラーとして出力し、何をエラーとして出力しないか」の制御は、php.inierror_reporting で行う。
プログラム実行時に ini_set() 関数を使っても設定できるが、error_reporting() という専用の関数も存在する。
ここには定数 (E_ERRORE_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)

 

 

error_reporting」と「ini_set()」に関しては以下の記事の問題文についての箇所で説明しています。

ここでのポイントは値に-1を指定した時のerror_reportingの挙動です。

下記コードの場合は、仮に将来のバージョンの PHP で新しいレベルと定数が追加されたとしてもすべてのエラーを表示するようになります。

// 全ての PHP エラーを表示する
error_reporting(E_ALL);

// 全ての PHP エラーを表示する
error_reporting(-1);

 

よって、この選択肢は合っています。

 

選択肢②

 

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

 

 

まず例外クラスについて深掘ります。

Throwableクラス」はすべての例外クラスのルートクラスであり、「Errorクラス」と「Exceptionクラス」がサブクラスとして存在しています。

PHPのクラスがThrowableインターフェイスを直接実装することはできません。そのかわりに、Exceptionを継承する必要があります。

catch句がThrowableクラスの場合、tryブロック内でスローされたErrorとExceptionが捕捉されます。

try{

}catch(¥Throwable $e){
  
}

 

またcatchブロックで渡された変数$eを介して例外に関するさまざまな情報にアクセすることができます。

メソッド 概要
getMessage( ) 例外メッセージ
getCode( ) 例外コード
getLine( ) 例外が発生した行数
getFile( ) 例外が発生したファイル名
getPrevious( ) 前の例外
getTrace( ) バックトレース(配列)
getTraceAsString( ) バックトレース(文字列)

 

また、Exceptionクラスには紛らわしいですがErrorExceptionクラスがあります。

ErrorExceptionクラスは、set_error_handler()関数と併用することで「特殊な変換処理」で変換するためだけに存在するクラスです。

旧来のエラーは「Throwable」ではないため、catch句では補足することができません。

旧来のエラーには以下のようなものがあります。

Parse Error コードが文法的に誤っている時に発生。一切の処理を行わない
Fatal Error 処理を継続することが不可能の場合に発生。発生した箇所で処理停止
Warning 処理継続は可能だが、想定外の問題が起こったときに発生
Notice 処理継続は可能だが、想定外の問題が起こったときに発生

 

以下のコードは、定義していない変数をインクリメントしようとしているために「PHP Warning: Undefined variable $i」が出力されるところをcatch句で捕捉し、set_error_handler()によってエラーメッセージをErrorExceptionに変換しています。

set_error_handler(
    function ($errno, $errstr, $errfile, $errline) {
        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
*/

 

ちなみにこちらでは「Fatal Error」を受け取ることができないため、どうしても対処したい場合は「register_shutdown_function()」を使用する必要があります。

選択肢のコードでは「E_WARNING」レベルのため本来はcatch句のThrowableでは捕捉できないですが、set_error_handler()によって捕捉されエラーメッセージを出力しています。

よって、この選択肢は合っています。

参考記事:Qiita

選択肢③

 

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

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

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

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

 

 

問題文でerror_reporting(-1)が先頭に記述されていると前書きがあるため、選択肢のコードでは「PHP Warning: Undefined variable $i in 」が出力され、スクリプトの実行は中断されません。

よって、この選択肢は合っています。

選択肢④

 

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 となる。

 

 

選択肢②の解説からErrorクラスおよびExceptionクラスはそれぞれThrowableクラスのサブクラスであって、ErrorクラスとExceptionクラスには親子関係はありません。

そのため、この選択肢は誤りです。

ABOUT ME
ヒロ
社会人4年目/25歳/食品商社で2年間営業した後、IT業界にシステムエンジニアとして転職/Java,PHP言語を扱う開発エンジニア