• 14
    いいね
  • 0
    コメント

PHPで安全なプログラムを書くために、PHP7からの機能も合わせて型についてまとめました。
型についての深い理解は、コードやコメントや関数名や安全性に影響を与えます。

PHPの型、型安全性、弱い型付け、強い型付け、型指定と影響、について説明していきます。
前半部分は基本について、後半部分からは型安全について書いてあります。

型論争

型をつけるべきか否かではしばしば議論になっている。
型をつける派は、厳密な型付によって大きな問題になる前にエラーを特定できるようになるという主張。
型をつけない派は、型付をしないことによってコード量が減り、記述がシンプルになるという主張である。

PHPの9種類の型

スカラー型

論理値 (boolean)
整数 (integer)
浮動小数点数 (float, double [PHPだと区別なし])
文字列 (string)

複合型

配列 (array)
オブジェクト (object) [クラスなど]
callable [コールバック関数]

特別な型

リソース (resource)
ヌル (NULL)

型推論とは

PHPは型がないので実行時に型を推論しています。
なので、動的型付け言語です。
逆にコンパイル時に型付けするものを静的型付け言語と呼びます。

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"=>"tomo", "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) "tomo" ["age"]=> int(12) ["Controller"]=> object(Controller)#2 (0) { } } 
tomo
12
HELLO!!!!!

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

型安全性とは

意味ない型の食い違いや、エラーを引き起こす型の食い違いを全て検出できること。
C/C++やPHPはキャストができエラーも吐き出さないので、型安全とは言えないようです。

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

function setUserName(UserName, UserId)
function setUserName(string UserName, int UserId)
このように引数の指定を間違えてもエラーにはならない言語だと型安全性が低いと言える。
//正しいコード
setUserName("tomo", 12)
//エラーになるべきコード
setUserName(12, "tomo")
PHPだとこのエラーになるべきコードでも動き、型安全ではない。
PHP7からは、型に違った値を入れるとエラーを出すようにできる。

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

型の自動的な変換をしないもの、型によるエラーが起きることを検出できることを強い型付けと言います。例えば、Haskellなどにはキャストが存在しません。
逆に弱い型付けとは、型によるエラーを引き起こすものなどを検出しません。

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を指定することができます。

・パラメータで指定

function test(?string $name)
{

}
・返り値で指定
function test (): ?string
{

}

注意点

なんともスカラ型の指定をする方法は、PHP5とPHP7では互換性がありません。
PHP7環境だけで動かす場合に使えます。PHP5で型指定するとエラーします。
自分らの環境なら良いですが、コードを配布する時にはPHP5に対応させるならこの書き方はできません。

結論

・型安全なプログラムを書くことによって、引数の引き起こすミスを減らすことができる。
・コメント数を抑えつつ安全なプログラムが書ける。
・PHP7から型安全なプログラムを書くことができる。
・PHP7と5では、スカラ型の互換性がなく、5で型指定するとエラーする。

参考リンク

A theory of type polymorphism in programming (1978)
https://courses.engr.illinois.edu/cs421/sp2013/project/milner-polymorphism.pdf
PHP公式 型
http://php.net/manual/ja/language.types.php
PHP公式 declare
http://php.net/manual/ja/control-structures.declare.php

  • この記事は以下の記事からリンクされています
  • 【PHP】関数からリンク