型宣言(タイプヒンティング)
PHPには型宣言に落とし穴があります。例外のテストの方法を勉強していて遭遇した現象です。
すごく簡素ですが、こんな感じのコードとテストを書いて再現してみます。
実行コード
<?php
class TestClass{
public function typeError(string $data)
{
echo $data;
}
}
テストコード
<?php
require "/hoge/TestClass.php";
class Test extends PHPUnit_Framework_TestCase
{
/**
* @expectedException TypeError
**/
public function testType()
{
$data = 1;
TestClass::typeError($data);
}
}
コードを見ていただければわかるかと思いますが、typeError
関数の引数はPHP7から導入されたスカラー型の型宣言をしています。string
を宣言しているので、文字列のみが引数として許されることになるはずです。
PHP7からは型宣言に抵触すると、TypeError
という例外が投げられることになります。
テストコードでは@expectedException
アノテーションにてTypeErrorを指定して補足する準備をしています。ですのでこのテストはTypeError
が補足できたら成功というテストです。
$data
にint
型である数字の1
を格納しtypeError
に渡しています。
typeError
の引数はstring
型で型宣言しているのですから、int
型の値を与えるとTypeError
がスローされ、このテストは成功するはずです。
しかし、成功しません。
テストでは以下の出力がされます。
Failed asserting that exception of type "TypeError" is thrown.
これはTypeError
の補足に失敗したことを意味します。
array型ではどうだろう
ではテストを成功させるために別な型を使ってみます。
テストコード
<?php
require "/hoge/TestClass.php";
class Test extends PHPUnit_Framework_TestCase
{
/**
* @expectedException TypeError
**/
public function testType()
{
$data = [1, 2, 3];
TestClass::typeError($data);
}
}
値を配列(array
型)にしてみました。これでテストをしてみると
PHPUnit 5.1.7 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 30 ms, Memory: 4.00Mb
OK (1 tests, 1 assertions)
成功しました。ちゃんとTypeError
を補足できたということですね。
う〜む。どういうことなんだろう?
知らないうちに「暗黙の型変換」
よくよく調べて見ると、PHPの型宣言は弱い型検査らしく、型宣言をしてもint
、string
は可能な限り型変換が行われるようです。
int
、string
だけでなくint
、float
、string
、bool
は互いに暗黙の型変換が行われるようですよ。
それだと型宣言の意味あるのかな〜。
ちゃんと型検査したい時には
この暗黙の型変換を回避するにはstrict
(厳密)モードにする必要があります。 以下をファイルの先頭にの記述します。
declare(strict_types=1)
これでint
、float
以外の暗黙の型変換は行われなくなるようです。
実際strict
モードにて再テスト(引数にint
型を与える)をしてみると…
実行コード
<?php
declare(strict_types=1);
class TestClass{
public function typeError(string $data)
{
echo $data;
}
}
テストコード
<?php
require "/hoge/TestClass.php";
class Test extends PHPUnit_Framework_TestCase
{
/**
* @expectedException TypeError
**/
public function testType()
{
$data = 1;
TestClass::typeError($data);
}
}
ちゃんとテストが成功します。型変換は行われずTypeError
がスローされたということです。
PHPUnit 5.1.7 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 35 ms, Memory: 4.00Mb
OK (1 test, 1 assertion)
型変換の意義
型変換に関してやっぱりPHPという感じです。もともと動的型付けの言語ですし、型を意識しない言語でもあります。弱い型検査はPHPの使用感を保つには理にかなっているかもしれません。
他の静的型付け言語の様に厳密をデフォルトとするのはPHPにはそぐわない気もします。
切り替えができるようにしてくれたのは吉と出るか凶とでるか…。
ともかくも型宣言を厳密に行いたいときはdeclare(strict_types=1)
を忘れずに。
ということでシタ。
まとめ
- PHPの型宣言は弱い型検査
- デフォルトでは
int
、float
、string
、bool
は互いに暗黙の型変換が行われる - 型変換を厳密にするにはstrictモードにする必要がある
- 厳密モードは
declare(strict_types=1)
をファイルの先頭に記述する