はじめに
- YUZURIHAの松村です。
- 先日Laravelを
v11.37.0
へアップデートした際にハマったポイントがあったので、紹介します。
Collection::hasAny
メソッドとは?
- まず、今回ハマってしまった
Collection::hasAny
とはどんなメソッドなのかを解説します。 - 一言で言うと、引数に指定したキーのいずれかがCollection内に存在するかどうかを判断するメソッドです。
- 具体的にどのように使うかは、Laravel公式の以下のページをご参照ください。
Laravel v11.37.0
でのCollection::hasAny
メソッドの変更
- そしてLaravel
v11.37.0
には、こちらのPRの変更が含まれています。
- PRの説明としては以下の通り記載されているため、あくまでも
Collection::hasAny
メソッドにおける型ヒントの修正とリファクタリングのように見えます。Update hasAny's type hint to match has. Also inlines the call to has with array_key_exists.
和訳: hasAnyの型ヒントをhasと一致するように更新します。また、hasの呼び出しをarray_key_existsでインライン化します。
- 「破壊的な変更がある」や「挙動が変わる」という旨の記述はどこにもありません。
- しかし!実際に
v11.37.0
にアップデートしてCollection::hasAny
メソッドを呼び出すと、挙動が変わっていました!😱ギャー
どのように挙動が変わったのか?
- 挙動が変わってしまうのは、第一引数に二次元配列を指定した場合でした。
- 具体的にどう変わったのか
Tinker
を使って見て行きます。
v11.36.0
-
false
が返ります。> collect(['test1' => 'hoge'])->hasAny(['test1' => ['test1-1'], 'test2' => ['test2-1']]) = false
v11.37.0
-
array_key_exists
関数の引数でTypeError
となってしまいます。> collect(['test1' => 'hoge'])->hasAny(['test1' => ['test1-1'], 'test2' => ['test2-1']]) TypeError array_key_exists(): Argument #1 ($key) must be a valid array offset type.
なぜ変わったのか?
v11.36.0
-
v11.36.0
までは、Collection::hasAny
メソッドの内部でCollection::has
メソッドを呼び出していました。
-
hasAny
メソッド内でループしてhas
メソッドを呼び出し、更にhas
メソッド内でもループしてarray_key_exists
関数を呼び出しています。 - この処理によって、二次元配列でも正常に処理されて
false
が返されていました。
v11.37.0
-
v11.37.0
では、has
メソッドを使わずに直接array_key_exists
関数を呼び出すようになりました。
- これよって
array_key_exists
関数の第一引数に配列が入ってしまい、TypeError
となってしまいました。
正しい挙動
- 冒頭でも述べたように、
Collection::hasAny
は「指定したキーのいずれかがCollection内に存在するかどうか」を判定するメソッドです。 - そのため、一次元配列でキーを指定することが前提となっており、二次元配列を指定するのはイレギュラーな使い方と考えられます。
- そう考えると
v11.36.0
までの「エラーにならずfalse
が返る」という挙動はおかしくて、v11.37.0
以降の「エラーになる」という挙動が正しいとも言えます。 - ただLaravel公式のドキュメントやPR・CHANGELOGにはその旨の説明は無いので、説明が欲しかったなと思ってしまいますね😅
最後に
- 今回エラーになった
Collection::hasAny
の呼び出し箇所については、「意図して第一引数に二次元配列を指定していた」わけではなく「一次元配列を指定しているつもりが、ある条件の時だけ二次元配列になってしまう」というものでした。 - テストについては、一次元配列になる正常系のテストケースはあったものの、二次元配列になってしまう異常系のテストケースはありませんでした。
- そのため、テスト実行時にはこのエラーに気付くことができませんでした。
- やはり「異常系も含めたテストカバレッジを上げておいて、アップデート時にはテストがオールグリーンになるようにする」というのは大切ですね。