LoginSignup
1

More than 5 years have passed since last update.

PHPのapache_request_headersを含む処理をphpunitでtestしたい時

Last updated at Posted at 2018-03-14

はじめに

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 サーバーモジュール としてインストールされた時だけサポートされました。

どうすればいいのか

  • 方法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でもテスト実行可能になります。

参考

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
1