HHVM（PHP）で file_get_contents 時に context header を指定すると 400 Bad Request

[HipHop Virtual Machine](https://ja.wikipedia.org/wiki/HipHop_Virtual_Machine) 上にて、PHP で書いたスクリプトで [`file_get_contents()` の context ](http://php.net/manual/ja/function.file-get-contents.php)を指定して **API などにリクエストすると「400 Bad Request」エラーが返ってくる**。
（@「さくらのVPS」の CentOS7 + Nginx + <font color=red>HHVM</font> + [Kusanagi](https://kusanagi.tokyo/about/) 環境）

「HHVM file_get_contents header」で[ググって](https://www.google.co.jp/search?q=HHVM+file_get_contents+header)も、[Qiita 記事に絞り込んで](https://www.google.co.jp/search?q=site%3Aqiita.com+HHVM+file_get_contents+header)もドンピシャの情報がなかったので、未来の忘れん房な自分へのググラビリティとして。

## TL;DR

> `file_get_contents()` の第２引数が `false` になってませんか？
> 
> ```php:一般的なPHPの記法
> $result = file_get_contents($url, false, stream_context_create($context));
> ```

**HHVM では `file_get_contents()` の第２引数は `null` にする**必要があります。

[`file_get_contents` 関数の、第２引数「use_include_path」のデフォルト値は `false`](http://php.net/manual/ja/function.file-get-contents.php) だからか、**PHP5.6, PHP7.x では `false` でも動作する**のですが、`null` でも動作するため互換を持たせたい場合は `null` に統一した方がいいかもしれません。

```php:HHVM互換の記法
$result = file_get_contents($url, null, stream_context_create($context));
```

### 検証環境

- CentOS Linux release 7.5.1804 (Core)
- HipHop VM 3.19.2-dev (rel)
    - PHP Version => 5.6.99-hhvm
    - Zend Version => 2.4.99
- PHP 5.6.36 (cli)
- PHP 7.2.11 (cli)

## TS;DR

Qiita の API など、`file_get_contents` を使ったシンプルに取得するスクリプトは PHP／HHVM どちらでも動きました。

```php:PHP,HHVMどちらも動くシンプルなコード
<?php
$url_api = 'https://qiita.com/api/v2/items/e5f1668444898465ea97';

// リクエスト開始
$http_response_header = null;
$result = file_get_contents($url_api);

if (false === $result) {
    //エラー処理
    print_r($http_response_header);
}

//通常処理
print_r($result);
```
- [オンラインで動作をみる](https://paiza.io/projects/bhpJqEOfWoPs0aermPDb4w) @ paiza.IO

しかし、認証されていない（アクセストークンをヘッダーに含めていない）リクエストの場合、時間あたりのリクエスト数に制限がかかる API は多いため、ヘッダーに「`Authorization: Bearer`」を指定してアクセストークンを添えてリクエストすることにしました。

ところが、ローカル環境や、サーバーのコンソールからは PHP で実行すると動作するのに、Nginx（on CentOS7）上の HHVM を通すと「400 Bad Request」エラーが返ってきます。切り分け的に HHVM に問題がありそうです。


```php:問題が起きるコード
<?php
$url_api      = 'https://qiita.com/api/v2/items/e5f1668444898465ea97';
$access_token = 'xxxxxxx...xxxxx'
$header       = "Authorization: Bearer ${access_token}\r\n"; //CRLF

$context = [
    'http' => [
        'method'        => 'GET',
        'ignore_errors' => true,
        'header'        => $header
    ],
];

// リクエスト開始
$http_response_header = null;
$result = file_get_contents($url_api, false, stream_context_create($context));

if (false === $result) {
    //エラー処理
    print_r($http_response_header);
}

//通常処理
print_r($result);
```

PHP7 で使えている[関数の引数の String 型宣言は HHVM 3.6 では使えなかった](https://github.com/Qithub-BOT/Qithub-ORG/issues/166)りするので、PHP7 にシフトしたいのに、ちょいちょい違いがあり困ることがあります。おそらく今回も同様の微妙な仕様の違いである匂いがします。

> PHP の場合は CRLF でも LF でも正常にヘッダーが送信されるが、HHVM の場合では LF だとヘッダーのフォーマットがおかしくリクエスト先サーバーに Bad Request を返されてしまう。
>（[PHPのシステムをHHVMに載せた時に動かなくて直した所一覧](http://suzuki0keiichi.hatenablog.com/entry/2014/10/12/005032) @ suzuki0keiichiの日記）

ちょっと古い情報なのですが、「お！これじゃないか！」と思える上記の情報はあったのですが、残念ながらすでに CRLF で改行していました。(´･ω･`)=з

HHVM の[既知の PHP 互換問題一覧](https://github.com/facebook/hhvm/issues?q=is%3Aopen+is%3Aissue+label%3A%22php5+incompatibility%22)や、[リポジトリの Issue を探して](https://www.google.co.jp/search?q=site%3Ahttps%3A%2F%2Fgithub.com%2Ffacebook%2Fhhvm%2Fissues+file_get_contents+header)もドンピシャのものはなかったのですが、「ん？」と思う箇所がありました。

以下の２つの Issue を見ると、`file_get_contents()` の第２引数が `null` になっているのです。

- #7684 [file_get_contents() raises notice when using http header option](https://github.com/facebook/hhvm/issues/7684)
- #8046 [file_get_contents() doubles stream context http headers](https://github.com/facebook/hhvm/issues/8046)

「あれ？第２引数は `false` じゃないの？もしかして、型々言わない系？」と思い、`false` を `null` に変えたところ、、、動きました！

なんたルチア。

HHVM は [KUSANAGI](https://kusanagi.tokyo/about/) が Wordpress を最適化してるので速いから使っているのですが、PHP 7 も同じくらい速いそうですし、[HHVM は PHP サポートを打ち切る](https://hhvm.com/blog/2018/09/12/end-of-php-support-future-of-hack.html)ので、PHP 7 にガッツり移行するためにも、今後 HHVM を使い続けるか悩むところです。

## 参考文献

- 「[PHPのシステムをHHVMに載せた時に動かなくて直した所一覧](http://suzuki0keiichi.hatenablog.com/entry/2014/10/12/005032)」@ suzuki0keiichiの日記
- 「[HHVM 公式リポジトリ](https://github.com/facebook/hhvm)」 @ facebook @ GitHub
- 「[HHVM known minor incompatibilities of PHP5](https://github.com/facebook/hhvm/issues?q=is%3Aopen+is%3Aissue+label%3A%22php5+incompatibility%22) | issues」@ facebook @ GitHub
- 「[HHVM Documentation](https://docs.hhvm.com/hhvm/) @ hhvm.com
- 「[Ending Support for PHP](https://hhvm.com/blog/2018/09/12/end-of-php-support-future-of-hack.html)」@ hhvm.com
- 「[HipHop Virtual Machine](https://ja.wikipedia.org/wiki/HipHop_Virtual_Machine)」@ Wikipedia