PHP headers_list() 函数

headers_list() 函数用于返回已发送(或准备发送)的响应头列表。

该函数返回一个包含所有已添加到响应的 HTTP 头的数组。这些头可以是使用 header() 函数设置的,也可以是 PHP 自动生成的。

提示:使用 headers_list() 可以调试和检查当前页面发送了哪些 HTTP 头信息。

语法

headers_list ( ) : array

参数

该函数没有参数。

返回值

返回一个索引数组,包含已发送(或准备发送)的响应头列表。

每个数组元素都是一个字符串,格式为 "Header-Name: value"。

如果没有头信息要发送,则返回一个空数组。

示例

示例 1:基本使用 - 获取所有头信息

以下示例展示了如何获取当前响应的所有 HTTP 头信息。

<?php
// 设置一些 HTTP 头
header("Content-Type: text/html; charset=UTF-8");
header("X-Powered-By: PHP/7.4");
header("X-Custom-Header: MyValue");

// 获取所有头信息
$headers = headers_list();

echo "<h1>已发送的 HTTP 头信息:</h1>";
echo "<ul>";
foreach ($headers as $header) {
    echo "<li>" . htmlspecialchars($header) . "</li>";
}
echo "</ul>";

// 显示数组的原始格式
echo "<h2>原始数组格式:</h2>";
echo "<pre>";
print_r($headers);
echo "</pre>";
?>

示例 2:检查特定的头信息是否已设置

检查特定的 HTTP 头是否已经设置。

<?php
// 设置一些头
header("Content-Type: application/json");
header("Cache-Control: no-cache");

/**
 * 检查特定的 HTTP 头是否已设置
 * @param string $header_name 要检查的头名称(不区分大小写)
 * @return bool 如果头已设置返回 true,否则返回 false
 */
function is_header_set($header_name) {
    $headers = headers_list();
    $header_name_lower = strtolower($header_name);

    foreach ($headers as $header) {
        // 分割头名称和值
        $parts = explode(':', $header, 2);
        if (count($parts) === 2) {
            $current_header_name = strtolower(trim($parts[0]));
            if ($current_header_name === $header_name_lower) {
                return true;
            }
        }
    }

    return false;
}

// 检查特定头是否已设置
if (is_header_set('Content-Type')) {
    echo "Content-Type 头已设置\n";
} else {
    echo "Content-Type 头未设置\n";
}

if (is_header_set('X-Custom-Header')) {
    echo "X-Custom-Header 头已设置\n";
} else {
    echo "X-Custom-Header 头未设置\n";
}

// 也可以直接检查返回值
$headers = headers_list();
echo "总共设置了 " . count($headers) . " 个头信息\n";
?>

示例 3:调试和日志记录

将头信息记录到日志文件中,用于调试目的。

<?php
/**
 * 记录 HTTP 头信息到日志文件
 * @param string $log_file 日志文件路径
 */
function log_headers($log_file = 'headers.log') {
    $headers = headers_list();
    $log_entry = "[" . date('Y-m-d H:i:s') . "] HTTP Headers:\n";

    foreach ($headers as $header) {
        $log_entry .= "  " . $header . "\n";
    }

    $log_entry .= str_repeat("-", 40) . "\n";

    // 写入日志文件
    file_put_contents($log_file, $log_entry, FILE_APPEND);

    return count($headers);
}

// 设置一些头
header("Content-Type: text/html; charset=UTF-8");
header("X-Debug-Id: " . uniqid());
header("Cache-Control: max-age=3600");

// 记录头信息到日志
$header_count = log_headers();

echo "已记录 $header_count 个 HTTP 头到日志文件\n";

// 也可以在页面中显示头信息用于调试
if (isset($_GET['debug'])) {
    echo "<h2>调试信息 - HTTP 头:</h2>";
    echo "<pre>";
    print_r(headers_list());
    echo "</pre>";
}
?>

示例 4:与 headers_sent() 结合使用

在发送头信息之前检查并获取头信息列表。

<?php
// 检查头信息是否已经发送
if (headers_sent()) {
    echo "头信息已经发送,无法修改\n";

    // 获取已发送的头信息
    $sent_headers = headers_list();
    echo "已发送的头信息数量: " . count($sent_headers) . "\n";
} else {
    echo "头信息尚未发送,可以设置新的头信息\n";

    // 设置一些头
    header("Content-Type: text/plain");
    header("X-Generated-At: " . date('Y-m-d H:i:s'));

    // 获取当前准备发送的头信息
    $pending_headers = headers_list();
    echo "准备发送的头信息:\n";
    foreach ($pending_headers as $header) {
        echo "  - " . $header . "\n";
    }
}

// 更复杂的例子:只在头信息未发送时添加新头
function safe_add_header($header, $value = '', $replace = true) {
    if (!headers_sent()) {
        if ($value === '') {
            header($header, $replace);
        } else {
            header("$header: $value", $replace);
        }
        return true;
    } else {
        error_log("无法添加头信息 '$header',头信息已发送");
        return false;
    }
}

// 使用安全添加头信息函数
safe_add_header('X-Custom-Header', 'MyValue');
safe_add_header('Cache-Control', 'no-cache');

// 显示所有头信息
echo "\n当前所有头信息:\n";
print_r(headers_list());
?>

示例 5:REST API 响应头管理

在 REST API 中管理和检查响应头。

<?php
/**
 * REST API 响应类
 */
class ApiResponse {
    private $headers = [];

    /**
     * 添加响应头
     */
    public function addHeader($name, $value, $replace = true) {
        if (!headers_sent()) {
            header("$name: $value", $replace);
            $this->headers[] = "$name: $value";
            return true;
        }
        return false;
    }

    /**
     * 设置 JSON 响应头
     */
    public function setJsonResponse() {
        $this->addHeader('Content-Type', 'application/json');
        $this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
        $this->addHeader('Pragma', 'no-cache');
        $this->addHeader('Expires', '0');
    }

    /**
     * 设置 CORS 头
     */
    public function setCorsHeaders($origin = '*') {
        $this->addHeader('Access-Control-Allow-Origin', $origin);
        $this->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
        $this->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
        $this->addHeader('Access-Control-Allow-Credentials', 'true');
    }

    /**
     * 获取已设置的头信息
     */
    public function getHeaders() {
        return headers_list();
    }

    /**
     * 显示头信息(用于调试)
     */
    public function debugHeaders() {
        $headers = $this->getHeaders();

        if (php_sapi_name() === 'cli') {
            // 命令行模式
            echo "HTTP Headers:\n";
            foreach ($headers as $header) {
                echo "  $header\n";
            }
        } else {
            // Web 模式
            echo "<h3>HTTP Headers:</h3>";
            echo "<ul>";
            foreach ($headers as $header) {
                echo "<li><code>" . htmlspecialchars($header) . "</code></li>";
            }
            echo "</ul>";
        }

        return $headers;
    }

    /**
     * 发送 JSON 响应
     */
    public function sendJson($data, $status_code = 200) {
        $this->setJsonResponse();
        http_response_code($status_code);

        echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

        // 记录响应头(用于调试)
        if (isset($_GET['debug'])) {
            echo "\n\n<!-- Debug Headers -->\n";
            $this->debugHeaders();
        }
    }
}

// 使用示例
$response = new ApiResponse();

// 设置 CORS 头
$response->setCorsHeaders('*');

// 模拟 API 请求处理
$request_method = $_SERVER['REQUEST_METHOD'] ?? 'GET';

if ($request_method === 'GET') {
    // 返回 JSON 数据
    $data = [
        'status' => 'success',
        'message' => 'API is working',
        'timestamp' => time(),
        'headers_count' => count($response->getHeaders())
    ];

    $response->sendJson($data);

} elseif ($request_method === 'OPTIONS') {
    // 处理预检请求
    $response->addHeader('Access-Control-Max-Age', '86400');
    http_response_code(200);
    exit;

} else {
    http_response_code(405);
    $response->sendJson(['error' => 'Method not allowed'], 405);
}
?>

注意事项

  • 头信息发送时机:headers_list() 返回的是已经通过 header() 函数设置但可能尚未发送到头信息列表。实际的发送发生在 PHP 开始输出内容时。
  • 与 headers_sent() 的区别:headers_sent() 检查头信息是否已经发送,而 headers_list() 返回准备发送的头信息列表。
  • 性能影响:频繁调用 headers_list() 可能会有轻微的性能影响,但通常可以忽略不计。
  • 输出缓冲:当使用输出缓冲时,头信息可能不会立即发送。headers_list() 仍然会返回已设置的头信息。
  • PHP 自动添加的头:PHP 可能会自动添加一些头信息,如 Content-Type(如果未显式设置)和 X-Powered-By(取决于 PHP 配置)。
  • 头信息格式:返回的头信息字符串包含头名称和值,格式为 "Header-Name: value"。
  • 调试用途:这个函数在调试 HTTP 头相关问题(如缓存控制、CORS 问题等)时非常有用。

常见问题

问题 解决方案
返回空数组 这可能是因为还没有设置任何 HTTP 头,或者头信息已经发送且无法获取。检查是否在输出之后调用此函数。
无法获取某些头信息 某些头信息(如 Set-Cookie)可能有多个值,需要检查 $replace 参数或使用 header_remove() 后再重新设置。
头信息顺序问题 headers_list() 返回的头信息顺序可能与设置顺序不同。如果需要特定顺序,需要手动管理。
与输出缓冲的交互 当使用 ob_start() 时,头信息可能不会立即发送。使用 headers_list() 可以查看已设置但未发送的头信息。
区分已发送和待发送头信息 使用 headers_sent() 判断头信息是否已发送,结合 headers_list() 获取头信息列表。

实用技巧

1. 检查特定头的值
<?php
function get_header_value($header_name) {
    $headers = headers_list();
    $header_name_lower = strtolower($header_name);

    foreach ($headers as $header) {
        list($name, $value) = explode(':', $header, 2);
        if (strtolower(trim($name)) === $header_name_lower) {
            return trim($value);
        }
    }

    return null;
}

// 使用示例
header("X-Custom: MyValue");
$value = get_header_value("X-Custom"); // 返回 "MyValue"
?>
2. 防止重复头信息
<?php
function add_header_if_not_exists($header_name, $header_value) {
    $headers = headers_list();
    $header_name_lower = strtolower($header_name);

    foreach ($headers as $header) {
        list($name, $value) = explode(':', $header, 2);
        if (strtolower(trim($name)) === $header_name_lower) {
            return false; // 头信息已存在
        }
    }

    // 头信息不存在,添加它
    header("$header_name: $header_value");
    return true;
}
?>
3. 调试模式显示头信息
<?php
function debug_headers() {
    if (!isset($_GET['debug'])) {
        return;
    }

    $headers = headers_list();
    echo "<div style='background:#f0f0f0;padding:10px;margin:10px 0;'>";
    echo "<h3>HTTP Headers Debug:</h3>";
    echo "<ul>";
    foreach ($headers as $header) {
        echo "<li><code>" . htmlspecialchars($header) . "</code></li>";
    }
    echo "</ul>";
    echo "</div>";
}

// 在页面适当位置调用
debug_headers();
?>

相关函数