年末をゆるくお過ごしの皆様も、師走を身に染みて感じている皆様もこんにちは。
jof-fumiです。
この記事は、よりそうアドベントカレンダーの25日目の記事となります。
実はこの記事、さっき仕事中に見つけた事象をそのまま記事にしてみました。
PHPの関数のreturn
の型ヒント(bool
)を指定すると、暗黙変換される
以下のような関数があります。
この関数は、斎場の見学可否について、別システムから送られてくる値をコード値に変換するものです。
private function getKengakuTypeId(?string $value): bool
{
return match ($value) {
null => 1,
'可' => 2,
'不可' => 3,
'条件付き' => 4,
default => 1,
};
}
この関数に '可'
を渡しているはずなのに、データベースを見ると1
が登録されるという事象が発生しました。
原因の特定
この原因は、以下の通りです:
- match 関数は適切に 2 を返していた
- 型ヒント : bool によって、返り値が boolean 型にキャストされていた
- その結果、getKengakuTypeId は true を返していた
- DB登録時に、さらに int 型にキャストされて 1 として登録された
つまり、match 関数の結果そのものには問題がなく、return の型ヒントによるキャストが原因でした。
テスト
以下のユニットテストを書いていたのですが、これが全てパスしてしまいました。
// tests/Unit/Test.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Saijo;
class SaijoTest extends TestCase
{
public function testGetKengakuTypeId()
{
$saijo = new Saijo();
// '可' の場合
$this->assertEquals(
2,
$saijo->getKengakuTypeId('可')
);
// '不可' の場合
$this->assertEquals(
3,
$saijo->getKengakuTypeId('不可')
);
// '条件付き' の場合
$this->assertEquals(
4,
$saijo->getKengakuTypeId('条件付き')
);
// null の場合
$this->assertEquals(
1,
$saijo->getKengakuTypeId(null)
);
// その他の値の場合
$this->assertEquals(
1,
$saijo->getKengakuTypeId('その他')
);
}
}
なぜパスしてしまうのか?
これは、assertEquals
を使用しているためです。
例えば、true
と 2
は型が異なりますが、assertEquals
では等しいと判断されます。
型も含めて厳密に検査したい場合は、assertSame
を使用する必要があります。
以下の通りですね。
% php -r "var_dump(3 == true);"
bool(true)
% php -r "var_dump(3 === true);"
bool(false)
結論
assertEqualsは使うな。
以上、皆様もお気をつけください。