PHPのrangeが値を返さない場合がある

最近は業務でPHPを触っている毎日です。暗黙の型変換等々に苦しめられながら頑張っています。

今回は`range()`にやられました。

# `range()`とは

http://php.net/manual/ja/function.range.php

```
array range ( mixed $start , mixed $end [, number $step = 1 ] )
ある範囲の整数を有する配列を作成します。
```

よくある関数です。
始めの値と終わりの値とステップ数を指定して、それにあった配列を返してくれます。
`foreach()`と組み合わせて使ったりするナイスガイです。

# 何が起きたのか

次のようなサンプルがあったとして、実行してみます。

```sample.php
foreach(range(1, 16, 2) as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count(range(1, 16, 2)));
$logger->info('range array 0: ' . range(1, 16, 2)[0]);
```

```sample.php結果
[2018-06-29 11:51:24] app.INFO: range index: 1 [] []
[2018-06-29 11:51:24] app.INFO: range index: 3 [] []
[2018-06-29 11:51:24] app.INFO: range index: 5 [] []
[2018-06-29 11:51:24] app.INFO: range index: 7 [] []
[2018-06-29 11:51:24] app.INFO: range index: 9 [] []
[2018-06-29 11:51:24] app.INFO: range index: 11 [] []
[2018-06-29 11:51:24] app.INFO: range index: 13 [] []
[2018-06-29 11:51:24] app.INFO: range index: 15 [] []
[2018-06-29 11:51:24] app.INFO: range array count: 8 [] []
[2018-06-29 11:51:24] app.INFO: range array 0: 1 [] []
```

期待通りの結果ですね。素晴らしいです。

では次のサンプルはどうでしょうか。

```sample2.php
foreach(range(8, 10, 3) as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count(range(8, 10, 3)));
$logger->info('range array 0: ' . range(8, 10, 3)[0]);
```

```sample2.php結果
[2018-06-29 11:55:09] app.INFO: range array count: 1 [] []
[2018-06-29 11:55:09] app.INFO: range array 0:  [] []
```

**……えっ。なにこれ。**

`count()`が`1`ということは値は返ってきているのでしょうか。
いえいえ`range(8, 10, 3)[0]`の結果が空なので、そもそもうまく動いている感じがしません。
ちょっといろいろ調べてみましょう。

# 調査

単一の値の配列の場合、そもそも`foreach()`はループしてくれるのでしょうか。

```sample3.php
foreach([8] as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count([8]));
$logger->info('range array 0: ' . [8][0]);
```
```sample3.php結果
[2018-06-29 12:07:48] app.INFO: range index: 8 [] []
[2018-06-29 12:07:48] app.INFO: range array count: 1 [] []
[2018-06-29 12:07:48] app.INFO: range array 0: 8 [] []
```

ループしてくれますね。ということは`sample2.php`はやはり値が返ってない感じがします。

ちょっと`range()`の値を変えて試してみましょう。
開始と終わりの数を同じにしてみます。


```sample4.php
foreach(range(8, 8, 1) as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count(range(8, 8, 1)));
$logger->info('range array 0: ' . range(8, 8, 1)[0]);
```
```sample4.php結果
[2018-06-29 12:09:32] app.INFO: range index: 8 [] []
[2018-06-29 12:09:32] app.INFO: range array count: 1 [] []
[2018-06-29 12:09:32] app.INFO: range array 0: 8 [] []
```

これは問題ありませんね。
では次。終わりを`9`に、ステップ数を`2`に変えます。

```sample5.php
foreach(range(8, 9, 2) as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count(range(8, 9, 2)));
$logger->info('range array 0: ' . range(8, 9, 2)[0]);
```
```sample5.php結果
[2018-06-29 12:14:55] app.INFO: range array count: 1 [] []
[2018-06-29 12:14:55] app.INFO: range array 0:  [] []
```

発生しましたね。
次は終わりを`8`に変えてみましょう。

```sample6.php
foreach(range(8, 8, 2) as $index)
{
    $logger->info('range index: ' . $index);
}
$logger->info('range array count: ' . count(range(8, 8, 2)));
$logger->info('range array 0: ' . range(8, 8, 2)[0]);
```
```sample6.php結果
[2018-06-29 12:18:20] app.INFO: range index: 8 [] []
[2018-06-29 12:18:20] app.INFO: range array count: 1 [] []
[2018-06-29 12:18:20] app.INFO: range array 0: 8 [] []
```

発生しません。なんとなく発生する条件がわかってきました。

# 結果

どうやら、次の条件を満たしていると`range()`がうまく働かなさそうなことがわかりました。

`array range ( mixed $start , mixed $end [, number $step = 1 ] )`

- \$end > \$start
- \$start + \$step > \$end

PHP5.6で試したので、もしかしたら7だと解決しているのかもしれません。

ちょっとこれでひどい目にあったので、共有まで。