本文目录导读:

在 PHP 中,缓存控制主要涉及 浏览器端缓存(通过 HTTP 头)和 服务端缓存(如 Opcode、数据缓存)两个方面,下面从这三个维度详细说明。
浏览器端缓存控制(HTTP 头)
通过设置 header() 函数控制浏览器或代理如何缓存页面。
禁止缓存(动态页面常用)
header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP/1.1
header("Pragma: no-cache"); // HTTP/1.0
header("Expires: 0"); // 设置过期时间为过去
允许缓存,但需验证
// 缓存30秒,之后必须向服务器验证是否修改
header("Cache-Control: private, max-age=30, must-revalidate");
设置强缓存(静态资源常用)
// 缓存1年(31536000秒)
$expires = 60 * 60 * 24 * 365;
header("Cache-Control: public, max-age=" . $expires);
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expires) . " GMT");
ETag + Last-Modified 配合 304 状态码
$lastModified = filemtime(__FILE__);
$etag = md5_file(__FILE__);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $lastModified) . " GMT");
header("ETag: \"$etag\"");
// 检查客户端是否发送了 If-Modified-Since 或 If-None-Match
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : '';
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') : '';
if (($ifModifiedSince && strtotime($ifModifiedSince) >= $lastModified) ||
($ifNoneMatch && $ifNoneMatch === $etag)) {
header("HTTP/1.1 304 Not Modified");
exit;
}
}
// 如果未命中缓存,输出实际内容
echo file_get_contents(__FILE__);
服务端输出缓存(ob_ 系列函数)
用于捕获 PHP 输出并延迟发送,或实现页面片段缓存。
基本用法:输出缓冲
// 开启输出缓冲 ob_start(); echo "Hello "; echo "World"; // 获取缓冲区内容并清空 $content = ob_get_clean(); echo strtoupper($content); // 输出: HELLO WORLD
回调函数处理输出
function compress_output($buffer) {
// 返回压缩后的内容(示例:移除多余空白)
return preg_replace('/\s+/', ' ', $buffer);
}
ob_start("compress_output");
echo "This is a test string";
ob_end_flush();
实现页面缓存(简单示例)
$cacheFile = '/tmp/cache_' . md5($_SERVER['REQUEST_URI']) . '.html';
$cacheTime = 60; // 缓存60秒
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
readfile($cacheFile);
exit;
}
ob_start();
// ... 正常页面输出 ...
echo "<h1>动态内容</h1>";
$content = ob_get_clean();
file_put_contents($cacheFile, $content);
echo $content;
Opcode 缓存(Zend Opcache)
PHP 5.5+ 内置 Opcache,无需额外安装,在 php.ini 中配置即可。
常用配置(php.ini)
[opcache] opcache.enable=1 ; 启用 opcache.memory_consumption=128 ; 共享内存大小(MB) opcache.max_accelerated_files=4000 ; 最大缓存文件数 opcache.revalidate_freq=60 ; 检查文件修改时间间隔(秒) opcache.validate_timestamps=1 ; 启用验证时间戳
通过函数控制(如清除缓存)
// 清除整个 Opcache
opcache_reset();
// 清除指定脚本的缓存
opcache_invalidate('/path/to/file.php', true);
用户态数据缓存(内存/文件)
使用文件缓存
class FileCache {
private $cacheDir;
private $ttl = 3600;
public function __construct($dir = '/tmp/cache') {
$this->cacheDir = $dir;
if (!is_dir($dir)) mkdir($dir, 0755, true);
}
public function get($key) {
$file = $this->cacheDir . '/' . md5($key);
if (file_exists($file) && (time() - filemtime($file) < $this->ttl)) {
return unserialize(file_get_contents($file));
}
return null;
}
public function set($key, $value) {
$file = $this->cacheDir . '/' . md5($key);
file_put_contents($file, serialize($value));
}
public function clear() {
array_map('unlink', glob($this->cacheDir . '/*'));
}
}
// 使用
$cache = new FileCache();
if (!$data = $cache->get('expensive_data')) {
$data = fetchDataFromDatabase(); // 耗时操作
$cache->set('expensive_data', $data);
}
使用 Memcached 或 Redis
// Memcached
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$data = $memcached->get('key');
if (!$data) {
$data = getExpensiveData();
$memcached->set('key', $data, 3600);
}
// Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$data = $redis->get('key');
if (!$data) {
$data = getExpensiveData();
$redis->setex('key', 3600, $data);
}
常见问题与最佳实践
| 场景 | 推荐方案 |
|---|---|
| 动态页面(如个人账户) | 设置 Cache-Control: no-cache, must-revalidate + 表单令牌 |
| 静态资源(JS/CSS/图片) | 设置强缓存(max-age=一年)+ 版本号/指纹 |
| API 响应 | 使用 ETag + 304 或设置短时间缓存(如 60 秒) |
| 数据库查询结果 | 使用 Redis/Memcached 缓存查询结果 |
| 整页输出 | 使用 ob_start() + 文件缓存,或 Varnish/CDN |
| PHP 代码本身 | 启用 Opcache(生产环境必须) |
PHP 缓存控制的核心思路:
- 浏览器端:通过 HTTP 头告诉客户端是否缓存、缓存多久
- 服务端输出:用 ob 系列函数捕获和复用生成的内容
- 代码执行:使用 Opcache 编译缓存
- 业务数据:用文件、Memcached 或 Redis 缓存计算结果
根据实际业务场景选择合适的缓存策略,通常需要组合多种方式才能达到最佳性能。