HipHop Virtual Machine 上にて、PHP で書いたスクリプトで file_get_contents()
の context を指定して API などにリクエストすると「400 Bad Request」エラーが返ってくる。
(@「さくらのVPS」の CentOS7 + Nginx + HHVM + Kusanagi 環境)
「HHVM file_get_contents header」でググっても、Qiita 記事に絞り込んでもドンピシャの情報がなかったので、未来の忘れん房な自分へのググラビリティとして。
TL;DR
file_get_contents()
の第2引数がfalse
になってませんか?一般的なPHPの記法$result = file_get_contents($url, false, stream_context_create($context));
HHVM では file_get_contents()
の第2引数は null
にする必要があります。
file_get_contents
関数の、第2引数「use_include_path」のデフォルト値は false
だからか、PHP5.6, PHP7.x では false
でも動作するのですが、null
でも動作するため互換を持たせたい場合は null
に統一した方がいいかもしれません。
$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
$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);
- オンラインで動作をみる @ paiza.IO
しかし、認証されていない(アクセストークンをヘッダーに含めていない)リクエストの場合、時間あたりのリクエスト数に制限がかかる API は多いため、ヘッダーに「Authorization: Bearer
」を指定してアクセストークンを添えてリクエストすることにしました。
ところが、ローカル環境や、サーバーのコンソールからは PHP で実行すると動作するのに、Nginx(on CentOS7)上の HHVM を通すと「400 Bad Request」エラーが返ってきます。切り分け的に HHVM に問題がありそうです。
<?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 では使えなかったりするので、PHP7 にシフトしたいのに、ちょいちょい違いがあり困ることがあります。おそらく今回も同様の微妙な仕様の違いである匂いがします。
PHP の場合は CRLF でも LF でも正常にヘッダーが送信されるが、HHVM の場合では LF だとヘッダーのフォーマットがおかしくリクエスト先サーバーに Bad Request を返されてしまう。
(PHPのシステムをHHVMに載せた時に動かなくて直した所一覧 @ suzuki0keiichiの日記)
ちょっと古い情報なのですが、「お!これじゃないか!」と思える上記の情報はあったのですが、残念ながらすでに CRLF で改行していました。(´・ω・`)=з
HHVM の既知の PHP 互換問題一覧や、リポジトリの Issue を探してもドンピシャのものはなかったのですが、「ん?」と思う箇所がありました。
以下の2つの Issue を見ると、file_get_contents()
の第2引数が null
になっているのです。
- #7684 file_get_contents() raises notice when using http header option
- #8046 file_get_contents() doubles stream context http headers
「あれ?第2引数は false
じゃないの?もしかして、型々言わない系?」と思い、false
を null
に変えたところ、、、動きました!
なんたルチア。
HHVM は KUSANAGI が Wordpress を最適化してるので速いから使っているのですが、PHP 7 も同じくらい速いそうですし、HHVM は PHP サポートを打ち切るので、PHP 7 にガッツり移行するためにも、今後 HHVM を使い続けるか悩むところです。
参考文献
- 「PHPのシステムをHHVMに載せた時に動かなくて直した所一覧」@ suzuki0keiichiの日記
- 「HHVM 公式リポジトリ」 @ facebook @ GitHub
- 「HHVM known minor incompatibilities of PHP5 | issues」@ facebook @ GitHub
- 「HHVM Documentation @ hhvm.com
- 「Ending Support for PHP」@ hhvm.com
- 「HipHop Virtual Machine」@ Wikipedia