PAYATHON 2026

修改 httpd.conf 后 PHP require/include 失效

排障赵师傅

结论

这组错误与 httpd.conf 是否导致 PHP 的 require_once 或 include 失效无关。错误中最值得注意的是:

Failed opening required '1'

PHP 尝试加载的文件名是 1,并非 constants.php。也就是说,传给 require_once 的表达式在运行时成了布尔值 true 或整数 1。

如果实际执行的是:

require_once('constants.php');

错误信息中应该出现 constants.php。现在的结果说明,当前执行的代码、实际访问的站点目录或第 17 行的表达式,至少有一项与预期不同。

DocumentRoot 负责将 HTTP 请求映射到磁盘目录,不会把 PHP 文件名改成 1。Options Includes 控制 Apache SSI,也不影响 PHP 的 include。

检查 require_once 收到的值

打开报错指向的文件:

C:\Program Files (x86)\Zend\Apache2\htdocs\index.php

直接查看第 17 行,不要只对照另一台机器上的副本。可以在调用前输出参数:

<?php

$path = 'constants.php';

var_dump($path, gettype($path));
require_once $path;

正常情况下应显示:

string(13) "constants.php"

如果显示:

bool(true)

或:

int(1)

需要向前追查 $path 的赋值过程。例如,下面的代码误把检测结果传给了 require_once:

<?php

// 错误:file_exists() 返回布尔值
require_once file_exists('constants.php');

文件存在时,file_exists() 返回的 true 会在错误信息中显示为 1。可以改成:

<?php

$path = __DIR__ . DIRECTORY_SEPARATOR . 'constants.php';

if (!is_file($path)) {
    throw new RuntimeException('Configuration file not found: ' . $path);
}

require_once $path;

同时检查变量有没有被覆盖、函数是否返回了布尔值,以及条件表达式的括号是否正确。

用脚本目录构造绝对路径

相对路径不一定以当前 PHP 文件所在的目录为基准。PHP 会结合当前工作目录和 include_path 查找文件。如果代码调用过 chdir(),或者入口脚本、任务脚本发生变化,相对路径的解析结果也会随之变化。

如果 constants.php 和 index.php 在同一目录,可以写成:

<?php

require_once __DIR__ . DIRECTORY_SEPARATOR . 'constants.php';

如果当前 PHP 版本不支持 __DIR__,则使用:

<?php

require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'constants.php';

如果配置文件放在网站根目录之外,应按真实位置构造路径。例如:

<?php

$configFile = 'C:/private-config/constants.php';

if (!is_readable($configFile)) {
    throw new RuntimeException('Cannot read configuration file: ' . $configFile);
}

require_once $configFile;

Windows 下的 PHP 通常能够识别正斜杠,也可以借此减少反斜杠转义问题。

确认 Apache 实际加载的代码

结合“相同文件放在同一目录仍然找不到”和错误中的 '1' 来看,实际请求可能没有运行正在编辑的文件。可以临时建立一个诊断页面:

<?php

header('Content-Type: text/plain; charset=utf-8');

echo '__FILE__: ', __FILE__, PHP_EOL;
echo '__DIR__: ', __DIR__, PHP_EOL;
echo 'getcwd(): ', getcwd(), PHP_EOL;
echo 'DOCUMENT_ROOT: ', $_SERVER['DOCUMENT_ROOT'] ?? '(undefined)', PHP_EOL;
echo 'include_path: ', get_include_path(), PHP_EOL;
echo 'open_basedir: ', ini_get('open_basedir'), PHP_EOL;
echo 'php.ini: ', php_ini_loaded_file() ?: '(none)', PHP_EOL;

核对以下信息:

  • __FILE__ 是否指向正在编辑的文件。
  • DOCUMENT_ROOT 是否为预期目录。
  • 是否有另一个 VirtualHost 将同一域名映射到其他目录。
  • 浏览器访问的端口是否属于这套 Zend Server。
  • 系统中是否还有另一套 Apache、IIS 或其他 Web 服务正在运行。

也可以在目标文件中加入一段容易辨认的临时输出:

<?php

exit('Loaded from: ' . __FILE__);

如果浏览器没有显示这段内容,请检查请求是否进入了其他文件、其他站点实例或缓存层。

确认 Apache 已应用恢复后的配置

恢复 httpd.conf 后必须重启 Apache。如果新配置有语法错误,服务可能启动失败,原来的进程也可能仍在响应请求。

进入 Zend Server 的 Apache bin 目录执行配置检查,具体路径以本机安装位置为准:

httpd.exe -t
httpd.exe -V
httpd.exe -S

这三个命令分别检查配置语法、显示服务器根目录和主配置文件,以及列出 VirtualHost 映射。需要核对:

  • HTTPD_ROOT 和 SERVER_CONFIG_FILE 是否指向修改过的配置。
  • 是否通过 Include 或 IncludeOptional 加载了其他配置文件。
  • VirtualHost 中是否另外设置了 DocumentRoot。
  • Windows 服务实际启动的是哪个 httpd.exe。
  • Apache 的重启时间是否与配置修改时间一致。

如果问题描述中的英文说明确实写在 httpd.conf 里,只是在展示时省略了注释符,那么每行说明前都要保留 #。否则 Apache 会把这些文字当成指令并报告语法错误。

检查 PHP 实际加载的配置

错误中显示的搜索路径是:

.;C:\Program Files (x86)\Zend\ZendServer\share\ZendFramework\library

其中的 . 代表当前工作目录,不一定是 index.php 所在目录,因此不应依赖它加载重要配置文件。

建议通过 Web 请求执行 phpinfo(),因为命令行 PHP 和 Apache 使用的配置可能不同,仅运行 php --ini 无法确认 Web 环境的实际配置:

<?php

phpinfo();

检查以下项目:

  • Loaded Configuration File
  • Scan this dir for additional .ini files
  • Additional .ini files parsed
  • include_path
  • open_basedir
  • auto_prepend_file
  • auto_append_file

还要在项目和附加配置中搜索:

set_include_path(...)
ini_set('include_path', ...)
chdir(...)

并检查 Apache 或 VirtualHost 配置里是否有:

php_value include_path ...
php_admin_value open_basedir ...
php_value auto_prepend_file ...

AllowOverride None 只表示该目录中的 .htaccess 无法覆盖配置。其他 VirtualHost、Zend Server 管理配置或额外加载的配置文件仍然可能生效。

排查 Windows 权限和链接

确认参数已经是正确的完整路径后,再检查文件系统:

<?php

$path = __DIR__ . DIRECTORY_SEPARATOR . 'constants.php';

var_dump([
    'path'       => $path,
    'realpath'   => realpath($path),
    'exists'     => file_exists($path),
    'is_file'    => is_file($path),
    'is_readable'=> is_readable($path),
]);

根据结果继续判断:

  • realpath() 返回 false:路径不存在、链接目标失效,或当前进程无法解析该路径。
  • file_exists() 返回 false:检查文件名、扩展名、目录和链接目标。
  • is_readable() 返回 false:检查运行 Apache 服务的 Windows 账户及其 NTFS 权限。
  • 普通文件可读,但链接目录不可读:检查 junction/symlink 的目标和服务账户权限。

Dropbox 目录可能位于某个特定用户的配置文件下,而 Apache 服务通常由另一个账户运行。登录用户能够访问该目录,不代表服务账户也有权限。网络盘符往往也只对登录用户可见,服务应使用其能够访问的本地路径或 UNC 路径,并配置相应权限。

如果触发了 open_basedir,PHP 通常会明确报告 open_basedir restriction in effect。NTFS 权限不足时,通常会出现 Permission denied。当前错误直接指向文件名 1,应先解决参数值异常,再继续排查权限。

重新安装没有解决问题的原因

重新安装 Zend Server 不一定会删除以下内容:

  • 原有站点代码及第 17 行的错误。
  • Zend Server 保存的 PHP 配置。
  • VirtualHost 或附加 .conf 文件。
  • Windows 服务注册信息。
  • Dropbox 目录、symlink、junction 和 NTFS 权限。
  • 仍在运行的另一套 Apache 或 PHP 环境。

继续替换 httpd.conf 很难解决当前问题。应先确认实际执行的文件、PHP 实际加载的配置,以及 require_once 参数的类型和值。

针对现有错误,可以先把调用参数改为可验证的绝对文件路径:

<?php

$configFile = __DIR__ . DIRECTORY_SEPARATOR . 'constants.php';

if (!is_readable($configFile)) {
    throw new RuntimeException(
        sprintf('Required file is not readable: %s; cwd=%s', $configFile, getcwd())
    );
}

require_once $configFile;

如果运行这段代码后仍然报告加载 '1',说明浏览器执行的并非刚才修改的代码。此时应继续检查 Apache 实例、VirtualHost、端口和缓存。

备注:内容仅供参考。