PHP 需要能够打开文件才能调用 opcache;如果不存在,则无法加载...
让我们详细look,看看我们可能会玩什么花样:
if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok) {
/* The Accelerator is disabled, act as if without the Accelerator */
return accelerator_orig_compile_file(file_handle, type);
#ifdef HAVE_OPCACHE_FILE_CACHE
} else if (ZCG(accel_directives).file_cache_only) {
return file_cache_compile_file(file_handle, type);
#endif
} else if ((!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
(ZCSG(restart_in_progress) && accel_restart_is_active())) {
#ifdef HAVE_OPCACHE_FILE_CACHE
if (ZCG(accel_directives).file_cache) {
return file_cache_compile_file(file_handle, type);
}
#endif
return accelerator_orig_compile_file(file_handle, type);
}
我们可以看到启用文件缓存的地方,它优先于共享内存缓存。
接下来,我们要看file_cache_compile_file:
- block signals
- protect shared memory
- zend_file_cache_script_load
现在我们看看zend_file_cache_script_load:
-
open
-
read header (layout)
- verify magic "OPCACHE"
- verify system id
- optionally validate timestamp
- perform read of cached file
- verify checksum
所以我们遇到的第一个问题是system id 不是唯一的,而是由以下元素组成:
- PHP 版本
- Zend 扩展构建标识符
- 二进制标识符,包含以下内容:
sizeof(char)
-
sizeof(int)
-
sizeof(long)
-
sizeof(size_t)
-
sizeof(zend_long)
ZEND_MM_ALIGNMENT
- 如果不使用开发版 PHP(未发布,来自 git):
-
___DATE__二进制编译日期
-
___TIME___二进制编译时间
需要 PHP 版本和构建标识符,因为至少以下内容可能会在版本或构建之间发生变化:
- 操作码的整体标识符
- 内部结构布局
- VM 期望的指令序列(现有控制结构的细节可能会改变 fe.foreach)
- opcache 执行的优化(因为之前的优化可能会被发现是不安全的)
二进制标识符是必需的,因为至少 zval 的布局会随着字节序和架构而变化:架构可能会影响一些基本编译器类型(long、size_t 等)的大小以及上限和下限在这些类型中,字节序会影响结构中成员的顺序,以及基本编译器类型的二进制表示。
请注意,识别当前系统花费了相当多的精力,这应该让您停下来思考......
禁用时间戳opcache.validate_timestamps=0 的验证将允许加载文件缓存条目,即使文件系统上的当前文件为空。
标头中包含的校验和仅用于验证文件的脚本部分(位于标头之后),它不(也不能)包含系统标识符的标头或写入校验和本身.
因此,您可以通过将缓存文件的header 中的系统标识符更改为与目标机器标识符相对应来欺骗 PHP 从另一台机器加载缓存文件。
你应该吗?
也许是为了好玩,但作为部署软件的一种方法,绝对不是。
文件缓存不适用于此目的,从不同架构和/或构建加载缓存会导致 PHP 崩溃。