PHPのエラーを直して覚える
実際に出力されるメッセージを読みながら、空欄を埋めて直します。全102問。間違えた問題は翌日・3日後・7日後に自動で再出題されます。
エラー別に調べる
初級
- セミコロン忘れのParse errorの原因と直し方文の末尾にセミコロンを付け忘れたときに出る構文エラーです。PHPは次の行のトークンを見てから、初めて構文が崩れていることに気づきます。
- 変数名の $ を書き忘れたときの Parse errorPHPの変数は必ず $ から始まります。$を付け忘れると、その識別子は変数ではなく定数名として解釈され、後に続く = の時点で構文エラーになります。
- 文字列の閉じクォート忘れによる Parse error文字列を囲むクォートの片方を書き忘れると、それ以降のコードすべてが文字列の中身として読み込まれ、ファイルの終わりまで閉じクォートが見つからずエラーになります。
- 波括弧 } の閉じ忘れによる Parse error: Unclosed '{'関数やif文の波括弧を閉じ忘れると、PHPはファイルの終わりに達してから、どの行の { が対応を失っているかを教えてくれます。
- 配列リテラルや関数呼び出しでカンマを付け忘れたときの Parse error配列の要素や関数の引数を並べる際、区切りのカンマを付け忘れると、次のトークンが「予期しない」ものとして構文エラーになります。
- DivisionByZeroError: Division by zero の原因と直し方0で割り算をすると、PHP8ではDivisionByZeroErrorという例外が投げられ、プログラムが止まります。PHP7以前はWarningを出して結果はINFになるだけでしたが、8では致命的エラーに格上げされています。
- 未定義の関数を呼び出したエラーの原因と直し方存在しない名前の関数を呼び出したときに出るエラーです。関数名のタイプミスのほか、必要なファイルをrequireし忘れているときにも起こります。
- Call to a member function on null の原因と直し方nullが入った変数に対してメソッドを呼び出そうとしたときに出るエラーです。オブジェクトを取得し損ねた変数をそのまま使うと、必ずここで止まります。
- 文字列同士に + を使うと TypeError になる(. との違い)PHPの文字列連結演算子は . (ピリオド)です。+は数値の加算専用で、数値として解釈できない文字列に対して使うとTypeErrorになります。
- count()にnullを渡したときのTypeErrorの原因count()にnullを渡すと、PHP8ではTypeErrorが投げられます。関数が値を返し忘れてnullになった変数をそのままcount()に渡すと、ここで止まります。
- Warning: Undefined variable が出ても実行が止まらない理由宣言していない変数を参照するとWarningが出ますが、PHPはこれをエラーとして扱わず実行を続けます。参照した箇所の値はnullとして扱われます。
- Warning: Undefined array key の原因と直し方配列に存在しない添字でアクセスするとWarningが出ますが、プログラムは停止せず、その値はnullとして扱われます。
- シングルクォートの文字列では変数が展開されないダブルクォートの文字列内では$variableがその値に置き換わりますが、シングルクォートの文字列内では一切展開されず、そのままの文字として出力されます。
- 整数同士の割り算 / が小数になる(整数除算ではない)PHPの / 演算子は割り切れるかどうかに関わらず、割り切れない場合は自動的に小数(float)を返します。整数の商だけが欲しい場合はintdiv()を使う必要があります。
中級
- foreachで&を使った後、unsetを忘れると値が壊れるforeach ($arr as &$v) で配列を参照渡しで回すと、ループ終了後も$vは配列の最後の要素への参照を保持し続けます。unset($v)を忘れると、次に$vという名前でループを回したときに最後の要素が上書きされます。
- 配列を別の変数に代入するとコピーされる(参照ではない)PHPの配列はオブジェクトと違い、代入すると中身がまるごとコピーされます。コピー先の配列を変更しても、元の配列には影響しません。
- staticプロパティを self:: なしで参照すると別の変数として扱われるクラス内からstaticプロパティにアクセスするには self::$prop のように書く必要があります。$propとだけ書くと、staticプロパティとは無関係のローカル変数として扱われ、Warningが出ます。
- Cannot access private property の原因と直し方privateで宣言したプロパティにクラスの外からアクセスしようとすると、致命的エラーになります。外部に公開したい値はpublicにするか、getterメソッド経由で提供する必要があります。
- Cannot instantiate abstract class の原因と直し方abstractを付けたクラスは、そのままnewしてインスタンス化することができません。abstractクラスはサブクラスに実装させるための設計図であり、必ず継承先のクラスを使う必要があります。
- インターフェースのメソッドを実装し忘れると Fatal error になるimplementsで宣言したインターフェースのメソッドを1つでも実装し忘れると、そのクラス自体を読み込んだ時点で致命的エラーになります。インスタンス化する前、クラスの定義そのものが不完全と判定されます。
- declare(strict_types=1) で型の異なる引数を渡すと TypeError になるファイルの先頭でdeclare(strict_types=1)を宣言すると、関数の型宣言と異なる型の値を渡した際に自動変換されず、TypeErrorになります。strict_typesが無い場合は自動的に型変換されて実行が続きます。
- ArgumentCountError: Too few arguments passed の原因と直し方関数が必要とする数より少ない引数しか渡さなかったときに出るエラーです。デフォルト値の無い引数を省略すると、PHP8では実行時エラーとして止まります。
- match式で該当する条件が無いと UnhandledMatchError になるmatch式は該当するcaseが1つも無いと、switch文のように黙って何もしないのではなく、UnhandledMatchErrorという例外を投げます。想定していない値が来た場合の安全装置として働きます。
- ?? を使わずに存在しないキーへアクセスするとWarningが混ざる配列のキーが存在するか分からない場合、??演算子を使えばWarningを出さずにデフォルト値を指定できます。??を使わずにそのままアクセスすると、Undefined array keyのWarningが出力に混ざります。
- strpos()が0を返すと if文でfalseと同じに扱われてしまうstrpos()は文字列が先頭(添字0)で見つかった場合、戻り値の0を返します。if ($pos)のように真偽値として直接判定すると、0はfalseとして扱われるため、先頭で見つかったのに「見つからなかった」と誤判定します。
- array_merge()は文字列キーが同じだと上書きし、数値キーは振り直すarray_merge()は文字列キーが重複していると後の配列の値で上書きし、数値キーは重複を無視して0から振り直します。片方の配列のデータが消えたように見えるのは、この仕様が原因です。
上級
- Cannot modify readonly property の原因と直し方readonlyを付けたプロパティは、コンストラクタの中で一度初期化した後は二度と値を変更できません。クラスの外からはもちろん、クラスの中のメソッドからの再代入も拒否されます。
- self:: で作ったインスタンスは継承先のサブクラスにならない(static:: との違い)戻り値の型を static と宣言したメソッドの中で new self() を使うと、呼び出し元がサブクラスであっても常に定義元のクラスのインスタンスが作られます。呼び出し時点のクラスを使いたい場合は new static() が必要です。
- クロージャの use($var) は値をコピーする(参照ではない)無名関数でuse($var)と書くと、その時点の$varの値がコピーされてクロージャに閉じ込められます。クロージャの中で変更しても、外側の変数には影響しません。外側の変数と連動させたい場合はuse(&$var)と参照で渡す必要があります。
- nullになりうるプロパティへのメソッド呼び出しは ?-> で守る途中のプロパティがnullかもしれないオブジェクトの連鎖に対して、通常の->でメソッドを呼ぶと致命的エラーになります。?->を使うと、途中がnullの場合は例外を投げずnullを返して処理を続けられます。
- __toString未実装のオブジェクトを文字列化した原因__toString()メソッドを持たないオブジェクトを文字列と連結したり、文字列として出力しようとしたりすると致命的エラーになります。オブジェクトを文字列として扱いたい場合は__toString()を実装する必要があります。
- 同じ名前付き引数を2回指定すると Named parameter overwrites エラーになるPHP8の名前付き引数は、同じパラメータ名を2回指定すると致命的エラーになります。位置引数と名前付き引数を混ぜて書いた際に、同じパラメータへ二重に値を渡してしまうミスで起こります。
- enumのfrom()に存在しない値を渡すと ValueError になるバックドenum(値を持つenum)のfrom()メソッドは、対応するcaseが存在しない値を渡すとValueErrorを投げます。外部からの入力値をそのままfrom()に渡す場合は、事前の検証やtryFrom()の利用が必要です。
- staticを付け忘れると、インスタンス間でプロパティが共有されないインスタンスをまたいで値を共有したい場合はstaticプロパティが必要です。staticを付け忘れると、そのプロパティは各インスタンスに独立して属するため、期待した共有ができません。