PHP7でタイプセーフなPHPを書きたい時

## PHP7

とりあえず動くPHPが大好きな皆さんこんにちは。
そんなゆるふわなPHPをタイプセーフ（型安全）に書けたらもっと好きになるのになーと思ったことはありませんか？僕はあります。

そんな熱い要望に応えた（と勝手に思っている）PHP7が正式にリリースされて、はや半年以上。
既に試されている方も多いと思いますが、PHPをもっと好きになるためにPHP7の三大新機能を使って、このたび僕もタイプセーフなPHPを書いてみましたので、その結果をまとめさせていただきます。

ちなみに、PHP7の三大新機能とは…

* スカラー型宣言
  * `int`, `string`, `bool` がタイプヒンティングに使えるようになった。 
* 戻り値の型宣言 
  * 引数と同様に関数の戻り値でも型宣言出来るようになった。
* Null合体演算子
  * 三項演算子と `isset()` 地獄から解放される。 `??` と書く。

のことです。（僕が勝手に呼んでいる）

## 実験

いろいろ書いてみたので、順番に紹介させていただきます。
なお、PHPのバージョンは `7.0.7` です。

### 検証①

サンプルは英語、数学、国語の点数を合算して、合計点を表示するだけのプログラムです。
早速、書いてみます。

```php:seven1.php
<?php

function calculate_total_score(array $scores): int
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0; 

    $totalScore = $english + $math + $japanese;
    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

```sh:実行結果
$ php seven1.php
合計点は210でした。
```

まず、関数の引数にスカラー型が利用出来るようになったので `(array $scores)` と書きます。
そして、関数の戻り値の型を指定するためには引数の後に `: int` と書きます。
これでタイプセーフなPHPが書けます！タイプセーフなPHPとか最強かよ！

### 検証②

では、ここで本当にタイプセーフなのか検証していきましょう。

```php:seven2.php
<?php

function calculate_total_score(int $scores): int
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0;

    $totalScore = $english + $math + $japanese;

    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

引数の型指定を `array` から `int` に変更しました。
実行結果は以下です。

```sh:実行結果（抜粋）
$ php seven2.php
Fatal error: Uncaught TypeError: Argument 1 passed to calculate_total_score() must be of the type integer, array given
```

きちんとエラーになりました！PHP7最高かよ！

### 検証③

同じように戻り値も変えてみましょう。

```php:seven3.php
<?php

function calculate_total_score(array $scores): array
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0;

    $totalScore = $english + $math + $japanese;

    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

```sh:実行結果（抜粋）
$ php seven3.php
Fatal error: Uncaught TypeError: Return value of calculate_total_score() must be of the type array, integer returned
```

ちゃんと怒られますね。やっぱりPHP7最高だわ。
これでめでたしめでたし…と思いきや…。

### 検証④

念のため、もう少し検証してみましょう。
検証③では戻り値の型指定を`array` にしていましたが `string` にしてみましょう。

```php:seven4.php
<?php

function calculate_total_score(array $scores): string
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0;

    $totalScore = $english + $math + $japanese;

    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

これを実行すると…

```sh:実行結果
$ php seven4.php
合計点は210でした。
```

／(^o^)＼ﾅﾝﾃｺｯﾀｲ
さすがPHPだぜ。。一筋縄ではいかない。でも、これぐらいならまだいいですよ。

### 検証⑤

では、戻り値の型指定を `bool` にしてみましょう。

```php:seven5.php
<?php

function calculate_total_score(array $scores): bool
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0;

    $totalScore = $english + $math + $japanese;

    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

これを実行すると…

```
$ php seven5.php
合計点は1でした。
```

＼(^o^)／ｵﾜﾀ
ちゃんと実行された上に1点になってしまいました。
もうPHPやだ。。Rubyしよ。。

### 検証⑥

PHPにタイプセーフは早かったのか…と半ば諦めながら、マニュアルを虚ろな目で眺めていると、こんな記述が。

> スカラー 型宣言 には二つの方式があります。デフォルトの自動変換 (coercive) モードと、 厳密に判断する strict モードです。

なんだってー！！
もしかして `strict` モードにすればよいのでは？？

ということでやってみました。

```php:seven6.php
<?php
declare(strict_types=1);

function calculate_total_score(array $scores): bool
{
    $english = $scores['english'] ?? 0;
    $math = $scores['math'] ?? 0;
    $japanese = $scores['japanese'] ?? 0;

    $totalScore = $english + $math + $japanese;

    return $totalScore;
}

$scores = array();
$scores['english'] = 70;
$scores['math'] = 80;
$scores['japanese'] = 60;

$totalScore = calculate_total_score($scores);

echo '合計点は' . $totalScore . 'でした。' . "\n";
```

`strict` モードにするには `declare(strict_types=1);` と書きます。

これで実行すると、

```sh:実行結果（抜粋）
$ php seven6.php
PHP Fatal error:  Uncaught TypeError: Return value of calculate_total_score() must be of the type boolean, integer returned
```

ｷﾀ━━━━(ﾟ∀ﾟ)━━━━!!

ちゃんとエラーが出ました。

## 結論

* PHPでタイプセーフなコードを書きたいときは　strict モードにしましょう。
* PHPのせいにせずちゃんとマニュアルを読みましょう（自戒）
* PHP7最高！

## 参考サイト
『PHP: 新機能 - Manual』
http://php.net/manual/ja/migration70.new-features.php