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

0pt   2018-11-09 09:05
IT技術情報局

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 にする必要があります。

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

file_get_contents 関数の、第2引数「use_include_path」のデフォルト値は false だからか、PHP5.6, PHP7.x では false でも動作するのですが、null でも動作するため互換を持たせたい場合は null に統一した方がいいかもしれません。

検証環境
  • 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}rn"; //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 じゃないの?もしかして、型々言わない系?」と思い、false を null に変えたところ、、、動きました!

なんたルチア。

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

参考文献

Source: php

   ITアンテナトップページへ
情報処理/ITの話題が沢山。