Edited at

PHPの型と型安全について(PHP7からのPHPプログラミング)

PHPで安全なプログラムを書くために、PHP7からの機能も合わせて型についてまとめました。

型についての深い理解は、コード,コメント,関数名,安全性に影響を与えます。

PHPの型、型安全性、弱い型付け、強い型付け、型指定と影響、について説明していきます。

前半部分は基本について、後半部分からは型安全について書いてあります。


記事範囲

PHP7.2まで


型論争

型をつけるべきか否かではしばしば議論になっている。

型をつける派

=> 厳密な型付によって大きな問題になる前にエラーを特定できるようになる/メソッドの安全性が増える、という主張。

型をつけない派

=> 型付をしないことによってコード量が減り、記述がシンプルになるし、他の部分で補えるという主張である。


PHPの9種類の型


スカラー型

boolean: 論理値

integer: 整数

float: 浮動小数点数

string: 文字列


複合型

array: 配列

object: オブジェクト [クラスなど]

callable: [コールバック関数]


特別な型

resource: リソース

NULL: ヌル


擬似型

iterable


型推論とは

修正中


PHPの型の特徴


変数の型を確認する

変数の型を確認したい場合はgettype、var_dumpで取得できます。

echo gettype($val);
var_dump($val);


PHPの型の確認

$arr = [

true,
1,
0.3,
"OK",
[1, 2],
["a"=>1, "b"=>2],
new Controller,
fopen("test.txt", "r"),
NULL
];
foreach($arr as $val){
var_dump($val);
echo "<br>";
}

bool(true) 

int(1)
float(0.3)
string(2) "OK"
array(2) { [0]=> int(1) [1]=> int(2) }
array(2) { ["a"]=> int(1) ["b"]=> int(2) }
object(Controller)#1 (0) { }
resource(3) of type (stream)
NULL

それぞれ型を確認できました。


PHPの自動型変換

オペランドのどれかがfloatの場合は全てのオペランドはfloatとして評価される。

$v = 3 + 1 * 0.1


キャストで無理やり作るクラスインスタンスの作成方法

配列などを(object)でキャストすると、オブジェクトにすることができる。

class Controller

{
function echoText()
{
echo "HELLO!!!!!";
}
}
$obj = (object)["name"=>"name", "age"=>12, "Controller"=>new Controller];

var_dump($obj);
echo "<br>";
echo $obj->name . "<br>";
echo $obj->age . "<br>";
echo $obj->Controller->echoText();

object(stdClass)#3 (3) { ["name"]=> string(4) "name" ["age"]=> int(12) ["Controller"]=> object(Controller)#2 (0) { } } 

name
12
HELLO!!!!!


一応、この方法を使えば構造体もどきが作れる。


型安全性とは

意味ない型の食い違いや、エラーを引き起こす型の食い違いを全て検出できること。

C/C++やPHPはキャストができエラーも吐き出さないので、型安全とは言えないようです。

具体的には、この2つの関数の違いで説明できます。

function setUserName($UserName, $UserId)
function setUserName(string $UserName, int $UserId)
このように引数の指定を間違えてもエラーにはならない言語だと型安全性が低いと言える。

//正しいコード

setUserName("name", 12)
//エラーになるべきコード
setUserName(12, "name")
PHPだとこのエラーになるべきコードでも動き、型安全ではない。

PHP7からは、型に違った値を入れるとエラーを出すようにできる。


弱い型付け、強い型付けとは

型の自動的な変換をしないもの、型によるエラーが起きることを検出できることを強い型付けと言います。

逆に弱い型付けとは、型によるエラーを引き起こすものなどを検出しません。


PHP7からできるようになったこと

PHP7からはスカラー型(boolean, string, integer, float)の型指定ができるようになりました。

オブジェクト型の指定はPHP5.0から可能です。(配列もPHP5.1から可能)


厳密な型チェックモードに設定する

declare(strict_types=1)を記述することによって型指定をして、間違った値が入るとエラーを出します。

declare(strict_types=1);


関数やメソッドの戻り値の型を指定できる

declare(strict_types=1);

function Func(): int {
return 1;
}
戻り値に"1"という整数型以外を返すとエラーが起きる。
declare(strict_types=1);

//エラーになる
function Func(): int {
return "1";
}


引数に型を指定する

declare(strict_types=1);

function Func(int $v): int {
return 1;
}

//受け取る側がint型なのにstring型を渡しているので、エラーになる
Func("12");

このように、PHP7からは型安全なプログラムを記述できるようになりました。


型指定のコメントへの影響

型指定できることによって、コメントに型を必ずしも書く必要は無くなりました。

以下に関数定義の違いを書きました。

型指定していないコード

/**

* ユーザIDからユーザのフルネームを取得する
*
* @param string $userId. user's id
* @return string response user's name
* /
function getUserFullName($userId){
return $this->getUserData($userId)["userName"];
}
型指定したコード
/**

* ユーザIDからユーザのフルネームを取得する
*
* @param $userId user's id
* @return response user's name
* /
function getUserFullName(string $userId): string {
return $this->getUserData($userId)["userName"];
}
関数の名前から推測できるべき派ならば、安全な状態でコメントを消せます

function getUserFullName(string $userId): string {

return $this->getUserData($userId)["userName"];
}


nullableな型(PHP7.1)

パラメータと返り値の型宣言で、nullableの指定ができます。

nullableを指定すると、値にnullを指定することができます。

逆にこれを指定しないとnullが入った時にエラーが出ます。

パラメータで指定と返り値で指定の例

function setText(?string $text): ?string {

}


注意点

なんともスカラ型の指定をする方法は、PHP5とPHP7では互換性がありません。

PHP7環境だけで動かす場合に使えます。PHP5で型指定するとエラーします。

自分らの環境なら良いですが、コードを配布する時にはPHP5に対応させるならこの書き方はできません。


結論

・型安全なプログラムを書くことによって、引数の引き起こすミスを減らすことができる。

・コメント数を抑えつつ安全なプログラムが書ける。

・PHP7から型安全なプログラムを書くことができる。

・PHP7と5では、スカラ型の互換性がなく、5で型指定するとエラーする。


参考リンク

・A theory of type polymorphism in programming (1978)

・PHP公式 型

・PHP公式 declare

・PHP 7.1.x から PHP 7.2.x への移行

・PHP 7.0.x から PHP 7.1.x への移行

・PHP 5.6.x から PHP 7.0.x への移行