[模擬問題] PHP8上級 – エラー(CTO古庄道明からの出題)

こんにちは。PHP技術者認定機構 CTOの古庄道明です。
今日もPHP8上級試験の模擬問題と解説を紹介します。
以下を解いて出題のイメージと知識を補っていただけると幸いです。

目次

問題

エラーに関する説明の中で、誤っているものを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
となる。

回答と解説

問題は「エラー」についてです。
公式マニュアルの「エラー」のページを読んでおきましょう。
https://www.php.net/manual/ja/language.errors.basics.php

選択肢1を見ていきましょう。

PHPで、エラー全体を「出力するか(する場合はstdout なのか stderr なのか)、あるいは出力しないか」を制御するのはdisplay_errorsディレクティブですが、選択肢にあるような「何をエラーとして出力し、何をエラーとして出力しないか」は、error_reporting ディレクティブで設定をします。
error_reporting ディレクティブはini_set()関数でも設定できますが、error_reporting() という関数もあるので、こちらで設定することもできます。
設定可能な値については
https://www.php.net/manual/ja/errorfunc.constants.php
に定義済みの定数の一覧があるので、一度は目を通しておくことをおすすめします。

この定数はいわゆる「ビットマスク」で設定をするため、数値の0を指定すると「すべてのビットが寝ている」状態となり、結果的に「全てのエラー出力をオフにする」と等価な状態になります。
また同様に、数値の-1を指定すると、これは二の補数で「すべてのビットが立っている」状態となるため、結果的に「全ての PHP エラーを表示する」と等価な状態になります。

そのため、選択肢にあるとおり
$iについては「未定義の変数のインクリメント」だが、error_reporting が0のため、Warningは出力されない
$jについては「未定義の変数のインクリメント」で、error_reporting が-1のため、Warningが出力される
となり、この選択肢には正しいことが書かれています。

選択肢2を見ていきましょう。

set_error_handler()関数を使うと、エラー処理を行うユーザー関数 (callback)を設定することができます。
https://www.php.net/manual/ja/function.set-error-handler.php

そのため、選択肢のコードのように set_error_handler() が呼ばれると
if (0 !== ($errno & error_reporting())) { なので、「$errno & error_reporting()により、今回のエラー種別がerror_reporting()の設定に含まれているなら」
throw new ErrorException($errstr, 0, $errno, $errfile, $errline); なので「ErrorException 例外が投げられる」という動きとなります。

そのため、選択肢の実行部分は
・前提として、 error_reporting(-1); はすべてのコードの先頭に書かれているものとされている
$i++; で「未定義変数に対するインクリメント」なのでWarningが発生する
・Warning が発生するため、set_error_handler()で定義したユーザ関数が呼ばれる
error_reporting() は-1のため今回のエラー(Warning)は処理対象に該当する
・ErrorException例外が投げられる
・catch句にある通りの出力がなされる
という動きになります。

したがってこの選択肢には正しいことが書かれています。
なお、ここでいう「致命的なエラー」には、例えば trigger_error() によって発生させる E_USER_ERRORなどがあります。
一方、E_ERRORE_PARSEE_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING は、set_error_handler() で設定したユーザー定義のエラーハンドラでは扱えません。
また、set_error_handler()がコールされたファイルで発生した大半のE_STRICTも扱えませんので、留意しましょう。

選択肢3を見ていきましょう。
こちらは、改めて「定義済み定数」を確認しておくのがよいでしょう。
https://www.php.net/manual/ja/errorfunc.constants.php

E_ERRORは「重大な実行時エラー。 これは、メモリ確保の問題といった復帰できないエラーを表します。 スクリプトの実行は中断されます。」とあります。
また、 E_PARSEは「コンパイル時のパースエラー。 パースエラーはパーサーでのみ生成されます。」とあります。
一方で、 E_WARNINGは「実行時の警告 (致命的でないエラー)。 スクリプトの実行は中断されません。」とあります。
E_NOTICEは「実行時の注意。 エラーを示しているかもしれない何かに遭遇したことを表します。 ただし、スクリプトを問題なく実行しているときに起こることもあり
ます。」とあります。

上述のうち、E_ERRORは明示的に「スクリプトの実行は中断されます」とあるので、そこで処理は中断されます。
E_PARSEは「そもそもスクリプトが適切に解釈できない」ので、処理ができません。
一方で、E_WARNINGは気になるエラーですが「スクリプトの実行は中断されません」と明示されているため、処理は続行します。
またE_NOTICEは「エラーを示しているかもしれない」一方で「スクリプトを問題なく実行しているときに起こることもあります」とあるので、処理は続行されます。

そのため、選択肢のコードについては
E_WARNINGが発生しているケース
なので、
error_reporting(-1)であることから、Warningが出力される・処理は続行する
ので、記述通りの動きとなります。

したがってこの選択肢には正しいことが書かれています。

選択肢4を見ていきましょう。
この選択肢では「例外」についての記載があるので、マニュアルのこの部分を読んでおくとよいでしょう。
https://www.php.net/manual/ja/reserved.exceptions.php#127214
https://www.php.net/manual/ja/spl.exceptions.php#spl.exceptions.tree
https://www.php.net/manual/ja/language.errors.php7.php

また、Exception と Error についての内容なので、以下も一読をお勧めします。
https://www.php.net/manual/ja/class.exception.php
https://www.php.net/manual/ja/class.error.php

ここから簡単に見て取れるのは
・どちらも、Throwable インタフェースを実装している
ということになります。

そのため、選択肢のうち

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

の部分については「Exception は Throwable を実装(implements)している」ため、例外をキャッチすることができます。
この部分は正しいことが書かれています。

しかし一方で

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

の部分については、「Error は Exception を継承していない」ため、catch (\Exception $e) では Error をキャッチできません。
選択肢内の「PHP 7 以降において Error クラスという「PHP のすべての内部エラーの基底クラス」ができた」の部分自体は正しいのですが、それは「内部エラーの基底
クラス」であって、「すべての例外の基底クラス」ではありません。

したがってこの選択肢には誤りがあります。

選択肢4に誤りがあるため、解答は4になります。

PHP8上級試験と公式問題集に興味がある方は、以下のページをご覧のうえ、この機会に是非受験ください。

PHP8上級試験
https://www.phpexam.jp/summary/expert8

公式問題集:PHP8技術者認定上級試験公式問題集上巻
https://www.amazon.co.jp/dp/B0FQ1LTTQ4/

公式問題集:PHP8技術者認定上級試験公式問題集下巻ベータ
https://www.amazon.co.jp/dp/B0F7F4H8FP/

また、試験対策セミナーは毎月開催しています。興味がある方は以下のページをご覧いただき是非セミナーにもご参加ください。(フォローいただけるとセミナーのご案内が届くようになります)

PHP技術者認定機構の公式Peatixグループ
https://peatix.com/group/43424
www.phpexam.jp

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次