はじめに
PHPのApache関数の一つ、apache_request_headersを用いた実装をした後、phpunitでテストを実行すると下記のようなFatal Errorが発生する場合があります。その際の回避方法です。
PHP Fatal error: Call to undefined function apache_request_headers() in
apache_request_headersとは
http://php.net/manual/ja/function.apache-request-headers.php
すべての HTTP リクエストヘッダを取得するメソッドです。
このメソッドを実行すると下記のような返り値が得られます。
array (
'Host' => 'localhost',
'Cache-Control' => 'max-age=0',
'Origin' => 'https://localhost',
'Upgrade-Insecure-Requests' => '1',
'Content-Type' => 'application/x-www-form-urlencoded',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36 BASE',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer' => 'https://localhost',
'Accept-Encoding' => 'gzip, deflate, br',
'Accept-Language' => 'en-US,en;q=0.9,ja;q=0.8',
'Cookie' => '__utma=178864993.1558540938.1511488875.1513045856.1513050312.3; .....(省略)',
'X-Forwarded-Proto' => 'https',
'X-Forwarded-For' => '127.0.0.1',
'X-Forwarded-Host' => 'localhost',
'X-Forwarded-Server' => 'localhost',
'Connection' => 'Keep-Alive',
'Content-Length' => '2410',
)
変更履歴を参照する限り、CLIサーバ・FastCGI・Apacheモジュール・NSAPIサーバーモジュールとしてphpを動かす際に使える関数と説明されています。
バージョン | 説明 |
---|---|
5.5.7 | CLI サーバーでもこの関数が使えるようになりました。 |
5.4.0 | この関数は、FastCGI で使用可能になりました。 以前は、PHP が Apache モジュールあるいは Netscape/iPlanet/SunONE の NSAPI サーバーモジュール としてインストールされた時だけサポートされました。 |
http://php.net/manual/ja/function.apache-request-headers.php |
どうすればいいのか
- 方法1: phpunit実行時のみ定義する
- 方法2: バイパスを作る
方法1: phpunit実行時のみ定義する
phpunit実行時のみ、apache_request_headersが未定義だった場合に、独自定義する方法です。
step1: Test/bootstrap.php
テスト実行時に最初に実行する処理をbootstrap.phpに定義します。今回は、apache_request_headersの関数定義をしたいため、下記のように書きます。
<?php
// phpunitで実行した際は、apache_request_headersは未定義のため同じふるまいの関数を定義する
if (!function_exists('apache_request_headers')) {
function apache_request_headers() {
// apache_request_headersの振る舞いを実装する
// @link http://php.net/manual/ja/function.apache-request-headers.php#116343
$arh = array();
$rx_http = '/\AHTTP_/';
foreach($_SERVER as $key => $val) {
if(preg_match($rx_http, $key)) {
$arh_key = preg_replace($rx_http, '', $key);
// do some nasty string manipulations to restore the original letter case
// this should work in most cases
$rx_matches = explode('_', strtolower($arh_key));
if(count($rx_matches) > 0 and strlen($arh_key) > 2) {
foreach($rx_matches as $ak_key => $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
$arh_key = implode('-', $rx_matches);
} else {
// go to next header
continue;
}
$arh[$arh_key] = $val;
}
}
if(isset($_SERVER['CONTENT_TYPE'])) $arh['Content-Type'] = $_SERVER['CONTENT_TYPE'];
if(isset($_SERVER['CONTENT_LENGTH'])) $arh['Content-Length'] = $_SERVER['CONTENT_LENGTH'];
return $arh;
}
}
同様の振る舞いを定義するにあたり、php.netの下記のコメントを参考にしています。
http://php.net/manual/ja/function.apache-request-headers.php#116343
中では、$_SERVER
から HTTP_
を含むキーを取得・apache_request_headersで取得できる文字列形式に置換する処理を行っています。
置換例)
-
HTTP_USER_AGENT
->User-Agent
-
CONTENT_TYPE
->Content-Type
step2: phpunit.xmlの設定
phpunitの設定ファイルである、phpunit.xmlにbootstrapという項目を追加し、作成したTest/bootstrap.phpを設定して下さい。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/3.7/phpunit.xsd"
colors="true"
backupGlobals="false"
bootstrap="./Test/bootstrap.php"
verbose="true">
</phpunit>
以上の設定をすることで、phpunitでもapache_request_headersを利用することができるようになります。
Configuration read from /home/www/public_html/thebase.in/current/phpunit.xml
方法2: バイパスを作る
apache_request_headersが関数として定義されている場合のみapache_request_headersを実行し、未定義の場合は同様の振る舞いを行うメソッドを用意します。
private function request_headers()
{
// apache関数が使えてかつapache_request_headersでの値取得に成功した場合
if (function_exists('apache_request_headers')) {
if ($headers = apache_request_headers()) {
return $headers;
}
}
// apache_request_headersの振る舞いを実装する
// @link http://php.net/manual/ja/function.apache-request-headers.php#116343
$arh = array();
$rx_http = '/\AHTTP_/';
foreach($_SERVER as $key => $val) {
if(preg_match($rx_http, $key)) {
$arh_key = preg_replace($rx_http, '', $key);
// do some nasty string manipulations to restore the original letter case
// this should work in most cases
$rx_matches = explode('_', strtolower($arh_key));
if(count($rx_matches) > 0 and strlen($arh_key) > 2) {
foreach($rx_matches as $ak_key => $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
$arh_key = implode('-', $rx_matches);
} else {
// go to next header
continue;
}
$arh[$arh_key] = $val;
}
}
if(isset($_SERVER['CONTENT_TYPE'])) $arh['Content-Type'] = $_SERVER['CONTENT_TYPE'];
if(isset($_SERVER['CONTENT_LENGTH'])) $arh['Content-Length'] = $_SERVER['CONTENT_LENGTH'];
return $arh;
}
そして、apache_request_headers()の代わりにrequest_headers()を実行するようにすれば、phpunitでもテスト実行可能になります。