LoginSignup
4
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-09

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 に統一した方がいいかもしれません。

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,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);

しかし、認証されていない(アクセストークンをヘッダーに含めていない)リクエストの場合、時間あたりのリクエスト数に制限がかかる 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 になっているのです。

「あれ?第2引数は false じゃないの?もしかして、型々言わない系?」と思い、falsenull に変えたところ、、、動きました!

なんたルチア。

HHVM は KUSANAGI が Wordpress を最適化してるので速いから使っているのですが、PHP 7 も同じくらい速いそうですし、HHVM は PHP サポートを打ち切るので、PHP 7 にガッツり移行するためにも、今後 HHVM を使い続けるか悩むところです。

参考文献

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1