[PHP] 型宣言(PHP7)

# はじめに。

PHP5から「[タイプヒンティング(型宣言)](https://qiita.com/kazu56/items/50b35c9ee0e5c6c4e75e)」は存在したが、
ようやくPHP7で機能拡張されたことによってスカラ型の宣言も行えるようになった。
また、PHP7.2から[object型](http://php.net/manual/ja/migration72.new-features.php)も扱えるようになっている。

※PHP5の頃はタイプヒンティングと呼ばれていたが、PHP7から型宣言という呼び名に統一された。

以下が、これまでの経緯である。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/198971/d9f42a43-5834-da7a-a3a5-9442e7d05054.png)

## 型宣言の特徴

- 関数の引数/返り値に対して設定できる。
- 通常の変数に型宣言する事は出来ない。
- 想定外の値を受け取ると、TypeErrorをスロー

## 型宣言が必要な理由

そもそも型宣言は、関数制作者のためにある。
型宣言を行うことによるメリットとして、以下の点が挙げられる。

- 想定外の引数を受け取った時にエラーとなり、不具合を検知できる。
- 特に複数人開発において、関数製作者が引数に求める意図を明示できる。
- 型が保証されているので、関数の引数チェックが簡単になる。

そのため、余計な心配をしないためにも、型宣言を活用して、関数をシンプルかつ安全に保つべきである。


## 引数で型宣言

関数の引数が不正な型であった場合エラーとなる。
また、PHP5ではこのエラーは recoverable fatal error だったが、PHP7よりTypeErrorとなった。

```php

// 型宣言
function sampleA(int $userId) {
    // 処理
}

// (PHP7.1)からnullableが指定できる。
// 引数にNULLもしくはintを許容する。
function sampleB(?int $userId) {
    // 処理
}
```

[PHP公式] [型宣言](http://php.net/manual/ja/functions.arguments.php#functions.arguments.type-declaration)
[PHP公式] [PHP 7.0.x から PHP 7.1.x への移行](http://php.net/manual/ja/migration71.new-features.php)

## 返り値の型指定

引数の型宣言と同様に、返り値の型宣言も行える。
また、デフォルトの弱い型付けでは、戻り値の型が正しくなくとも、自動的に型変換を行っている。
そのため後述の[厳密な型チェック ](http://php.net/manual/ja/functions.arguments.php#functions.arguments.type-declaration.strict)を行い、返り値の型が違っている場合はTypeErrorを発生させるようにする。

```php
// 厳密な型チェックモードに設定する
declare(strict_types=1);

// このように返り値をint指定できる
function sampleA($userId): int {
    // 処理
}

// 以下はErrorになる。intで指定しているのに、stringで返している
function sampleB($userId): int {
    return '田中太郎';
}

// (PHP7.1)からnullableが指定できる。
// 返り値にNULLもしくはintを許容する。
function sampleC($userName): ?int {
    // 処理...
}
```

[PHP公式] [戻り値の型宣言](http://php.net/manual/ja/functions.returning-values.php#functions.returning-values.type-declaration)

### 強い型付け

よく知られているように、PHPは弱い動的型付けと呼ばれている。
例えば、引数をstrで想定した関数にintが渡されても、その値をstrとして受け取る。
このように関数が実行されてしまうのを避けるために登場するのが「強い型付け」である。

```php
// 厳密な型チェックを有効
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

sum(1, 2);
// >> 3

// 引数にfloatを渡すとTypeError
sum(1.5, 2.5);
// >> TypeError

```

ちなみに、厳密な方チェックを有効にしていないファイルから、厳密な方チェックを有効にしたファイル内で定義された関数を呼び出した場合は、呼び出し元の設定(弱い型チェック)が適応されてしまうので注意。

[PHP公式] [厳密な型チェック ](http://php.net/manual/ja/functions.arguments.php#functions.arguments.type-declaration.strict)

## TypeError

TypeError がスローされるのは、以下の三つの場合。 

- 関数に渡された引数の型が、関数の宣言時に指定された型と一致しない場合。
- 関数の戻り値の型が、関数の宣言時に指定された型と一致しない場合。
- PHP 組み込みの関数に渡す引数の数を間違えた場合 (これは、strict モードの場合に限ります)。

[PHP公式] [TypeError](http://php.net/manual/ja/class.typeerror.php)

またTypeErrorを例外処理として扱う場合は、以下のように書いてあげる。

```php
try {

} catch (TypeError $e){
    // TypeErrorもしくはErrorでキャッチ
    // TypeErrorは通常のExceptionではないので注意
}
```

ちなみに、例外処理における複数の例外のcatchも出来るようになった。

```php
try {

} catch (Exception | TypeError $e) {
    // Exceptionと TypeError をこのブロックで処理
}
```

# まとめ

- 関数で型宣言が使えるようになった
- 型安全な関数が書けるので、ミスを減らせて可読性もUP
- PHP7と5でスカラ型の互換性がないので、PHP5では使えない
- そのうち変数宣言と同時に型宣言できるようになりそう

### 参考

[PHP公式](http://php.net/)
[PHP中級者を目指す_言語を使いこなすための本](https://www.amazon.co.jp/dp/B07HYR3R2C)
[PHPの型と型安全について(PHP7からのPHPプログラミング)](https://qiita.com/bokotomo/items/1ead446ef689dc45b487)