Dockerの公式コンテナにPHP7.1が現れたので、マニュアルサイト見ながら動作確認してみる

みなさんこんにちは

いつの間にかDockerの公式コンテナの正式なlatestバージョンが7.1になりました。
私のようにソースコードのビルドめんどくさいマンはdockerイメージとして配布されると、とても導入しやすくなるので、嬉しい限りなのです。

そんなわけで、せっかくPHPのdockerイメージをバージョンアップしたので、動作確認をしてみようと思います。
PHPのマニュアルサイトは異様なほど充実しており、それを見ただけで、どんな変更があるかわかるのが便利です。
そこで、[マニュアルにある新機能](http://php.net/manual/ja/migration71.new-features.php)をソースコードをパクりながら動作確認していきましょう。
検証自体は[他の人が先にやってたり](http://qiita.com/rana_kualu/items/f8baf30752928346bef8)しますが、自分でも動かしてみて、実行結果を見ていくのもいいんじゃないかって思いました。

# 各新機能の動作確認
追加された新機能の中で、面白いものをピックアップしていきます。

## nullable
なんかちょっと前に流行ったnullableがPHPにも登場です。
「null安全でない言語なんてもはや言語ではないわ！」「nullable最高や！」みたいなエンジニアにも、「あ、PHPもnullableありますよ！」っていえますな。

これは関数の引数もしくは関数の返り値に型を指定した際、引数の値にnullを許すというものです。
書式は簡単で、指定した方の先頭に「?」を付け加えるだけです。
サンプルコードを上述のマニュアルから持ってきますが。。。現時点(2016/12/15)では、そのままコピると関数の二重定義で動かないので、ちょっとだけ変えて動作確認してみましょう。

```nullable.php
<?php

function testReturn(): ?string
{
    return 'elePHPant';
}

var_dump(testReturn());

function testReturn2(): ?string
{
    return null;
}

var_dump(testReturn2());

function test(?string $name)
{
    var_dump($name);
}

test('elePHPant');
test(null);
test();
```
こいつを実行すると以下の通りの挙動になります。

```
# php nullable.php
string(9) "elePHPant"
NULL
string(9) "elePHPant"
NULL

Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed in /var/dev/nullable.php on line 24 and exactly 1 expected in /var/dev/nullable.php:17
```
最後のエラーに関しては、nullは許すけど、引数に何も与えないとかは許さないって感じですね。
この辺は返り値にも当てはまるようです。

```nullable2.php
<?php

function testRet(): ?string
{
    return null;
}

function testRet2(): ?string
{

}

var_dump(testRet());
var_dump(testRet2());
```
に対して、

```
# php nullable2.php
NULL

Fatal error: Uncaught TypeError: Return value of testRet2() must be of the type string or null, none returned in /var/dev/nullable2.php:11
```

## void関数
前述のように、返却値を返すか、返却値すら返さないかは大きな違いになります。
返却値を全く返さない場合は、そういう関数であることを指定する別の方があり、それがvoidです。

```void.php
<?php

function test(): void
{
    echo '1';
}

function test2(): void
{
    echo '2';
    return;
}

function test3(): void
{
    echo '3';
    return null;
}

var_dump(test());
var_dump(test2());
var_dump(test3());
```
これを動作させてみると、

```
# php void.php

Fatal error: A void function must not return a value (did you mean "return;" instead of "return null;"?) in /var/dev/void.php on line 17
```
関数を読み込んだ時点で、エラーが出ます。
返却値はnullですら許しません。
一方でtest3の部分を削除して動作させると以下のようになります。

```
# php void.php
1NULL
2NULL
```
返却値がないので、var_dumpの値はnullを示しています。

## Symmetric array destructuring
話は変わって、多重代入ですね。
今まで`list`で書いていた多重代入が配列表記でできます。「list」って打たなくて良くなりました。
関数で複数のパラメータを返却したいとかに使うのですが、ちょっとだけ楽にかけそうです。

```list.php
<?php

$data = [
    [1, 'niisan'],
    [2, 'harukao'],
];

function retList()
{
    return [3, 'remore'];
}

// list() 形式
list($id1, $name1) = $data[0];
echo "$id1: $name1\n";
list($id2, $name2) = retList();
echo "$id2: $name2\n";

foreach ($data as list($id, $name)) {
    echo "$id: $name\n";
}

echo "////////////////\n";

// [] 形式
[$id1, $name1] = $data[0];
echo "$id1: $name1\n";
[$id2, $name2] = retList();
echo "$id2: $name2\n";

foreach ($data as [$id, $name]) {
    echo "$id: $name\n";
}
```
実行するとこんな感じになります。

```
# php list.php
1: niisan
3: remore
1: niisan
2: harukao
////////////////
1: niisan
3: remore
1: niisan
2: harukao
```
## 定数のアクセス範囲の指定
クラスなどで定数のアクセス範囲を設定できるようになりました。内部的に使っているのみで、外からは参照できない定数を作るのに便利です。

```const.php
<?php
class ConstDemo
{
    const PUBLIC_CONST_A = 1;
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;
}

echo ConstDemo::PUBLIC_CONST_A . "\n";
echo ConstDemo::PUBLIC_CONST_B . "\n";
echo ConstDemo::PROTECTED_CONST . "\n";
```
これを実行すると以下のようになります。

```
# php const.php
1
2

Fatal error: Uncaught Error: Cannot access protected const ConstDemo::PROTECTED_CONST in /var/dev/constAccessor.php:12
Stack trace:
#0 {main}
  thrown in /var/dev/const.php on line 12
```
アクセス範囲がpublicでない定数にアクセスしようとしてFatalエラーが出ています。

## iterable型
イテレータとして使える変数の擬似型としてiterableが追加されました。
iterable型はforeachで次々値を取り出せる変数なので、foreachが含まれる関数に数値とか渡されて、変なnoticeが出なくて良くなりますね。

```iterable.php
<?php

function iterableFunc(iterable $trav)
{
    foreach ($trav as $val) {
        echo "$val";
    }

    echo "\n";
}

// generator
function gen() {
    for ($i = 0; $i < 10; $i++) {
        yield $i;
    }
};

iterableFunc(['pen', 'pine', 'apple', 'pen']);
$gen = gen();
iterableFunc($gen);
iterableFunc('noiterable');
```
実行時の動作は以下のとおりです。

```
# php iterable.php
penpineapplepen
0123456789

Fatal error: Uncaught TypeError: Argument 1 passed to iterableFunc() must be iterable, string given, called in /var/dev/iterable.php on line 21 and defined in /var/dev/iterable.php:3
Stack trace:
#0 /var/dev/iterable.php(21): iterableFunc('noiterable')
#1 {main}
  thrown in /var/dev/iterable.php on line 3
```
これは関数のタイプヒントでの例ですが、返却値に対しても指定できます。

```iterable2.php
<?php

function retIterable($switch = true): iterable
{
    if ($switch) {
        return [1, 2, 3];
    }

    return 123;
}

var_dump(retIterable());
var_dump(retIterable(false));
```
結果は以下のとおりです。

```
# php iterable2.php
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

Fatal error: Uncaught TypeError: Return value of retIterable() must be iterable, integer returned in /var/dev/iterable2.php:9
Stack trace:
#0 /var/dev/iterable2.php(13): retIterable(false)
#1 {main}
  thrown in /var/dev/iterable2.php on line 9
```
## 関数やメソッドをクロージャー化する
関数やメソッドをクロージャーにすることができます。
strategyパターンとかで使えるかもしれません。

```closure.php
<?php

class Logics
{
    public function getMethod($key)
    {
        return Closure::fromCallable([$this, $key]);
    }

    private function ichi(string $str) {return $str;}
    private function ni(string $str) {return $str . $str;}
    private function san(string $str) {return $str . $str . $str;}
}

$arr = ['ichi' => 'AAA', 'ni' => 'BBB', 'san' => 'CCC'];
$logics = new Logics;
foreach ($arr as $key => $str) {
    $logic = $logics->getMethod($key);
    var_dump($logic($str));
}
```
実行すると以下のようになります。

```
# php closure.php
string(3) "AAA"
string(6) "BBBBBB"
string(9) "CCCCCCCCC"
```

# まとめ
他にも機能はあるのですが、あまり使わなそうなので、今回はここまでにしておきます。
なんだかモリモリPHPが発展しているような気がして個人的にとてもうれしいです。

今回は以上です。

# 参考
http://php.net/manual/ja/migration71.new-features.php
[PHP7.1.0の新機能](http://qiita.com/rana_kualu/items/f8baf30752928346bef8)
[PHP7.1のDocker](https://hub.docker.com/_/php/)