PHP源码部署后无法运行?常见原因与检查方法

PHP源码部署后无法运行?常见原因与检查方法

PHP源码部署后无法运行?常见原因与检查方法

PHP 项目上传到服务器后,可能出现白屏、500 错误、404、下载源码、数据库连接失败,或者命令行能运行、浏览器却打不开等情况。这些现象看起来相似,原因却可能分布在 PHP 版本、Web 服务配置、项目依赖、文件权限和应用自身配置等不同层面。

排查时不要一上来就改代码或重装环境。更有效的方法是先确认请求有没有到达服务器,再根据 HTTP 状态码和日志缩小范围,最后检查运行环境、依赖与项目配置。本文适用于把 PHP 源码部署到 Linux 服务器、虚拟主机、容器或本地开发环境的场景;具体命令和配置需按所用发行版、Web 服务及 PHP 安装方式调整。

一、先判断“无法运行”具体是什么现象

先记录访问的 URL、发生时间、浏览器显示的内容,以及服务器返回的 HTTP 状态码。状态码不能直接证明根因,但能帮助选择排查方向:

  • 连接超时或拒绝连接:检查域名解析、云防火墙、安全组、端口监听和 Web 服务是否启动。
  • 404:检查域名对应的网站配置、站点根目录、请求路径,以及应用是否需要重写规则。
  • 403:检查访问控制、目录权限、索引文件配置和安全策略。不要简单地把目录权限改成 777。
  • 500:常见于 PHP 致命错误、配置错误、缺失依赖或 Web 服务与 PHP-FPM 通信失败,应优先查看错误日志。
  • 502 或 503:通常要检查反向代理上游、PHP-FPM 服务状态、套接字地址和连接权限。
  • 浏览器下载 PHP 文件,或页面直接显示 PHP 源码:说明请求可能没有交给 PHP 解释器处理。立即停止对外提供该站点,先确认 PHP 处理配置,避免源码泄露。
  • 页面空白:可能是 PHP 错误未显示、应用主动返回空内容,或响应被缓存。检查日志和实际响应,不要仅凭空白页面判断是 PHP 没安装。

可以从客户端查看响应头和状态码:

curl -i https://example.com/

如果服务器能通过 SSH 登录,可同时观察 Web 服务和 PHP-FPM 日志。日志位置取决于安装方式和配置,常见位置包括系统日志目录、虚拟主机单独配置的日志文件,以及 PHP-FPM 的错误日志。不要假设所有机器都使用相同路径。

二、确认请求确实进入了正确的网站目录

部署文件存在,不代表 Web 服务正在读取这些文件。一个服务器可以配置多个域名和多个站点根目录;域名解析、虚拟主机、容器挂载或反向代理配置不一致时,请求可能落到默认站点。

先核对以下信息:

  • 域名的 A、AAAA 或 CNAME 记录是否指向预期地址;使用 CDN 或代理时,也要确认代理回源目标。
  • Web 服务配置中的域名匹配项(例如 Nginx 的 server_name)是否包含当前域名。
  • 站点根目录是否指向项目实际的公开入口目录。许多框架要求指向 public,而不是项目根目录。
  • 容器中的路径与宿主机挂载路径是否对应;在容器里检查文件,不要只检查宿主机目录。
  • 请求是否经过负载均衡、反向代理或旧的缓存节点。

在服务器上可以先确认文件位置和当前目录:

pwd
find /var/www -maxdepth 4 -type f -name 'index.php' -print

/var/www 只是示例,请替换为实际部署目录。不要通过把整个项目随意复制到系统目录来“试试看”,这容易制造多份不一致的代码。

三、检查 Web 服务是否把 PHP 交给解释器处理

PHP 文件需要由 PHP 解释器执行。以 Nginx 配合 PHP-FPM 为例,Nginx 负责接收 HTTP 请求,通常通过 FastCGI 把 PHP 请求转交给 PHP-FPM。Apache 则可能使用 PHP 模块或 PHP-FPM,配置方式并不相同。

Nginx 配置常见结构如下。它只是示意,具体的 PHP-FPM 地址、入口文件和目录必须与本机环境一致:

server {
    listen 80;
    server_name example.com;
    root /var/www/myapp/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }
}

其中 /run/php/php-fpm.sock 是示例路径。有的系统使用版本号命名的套接字,有的环境使用 TCP 地址,例如 127.0.0.1:9000。应以 PHP-FPM 池配置中的监听地址为准,并检查运行 Nginx 的用户是否有权访问套接字。

修改配置后先测试语法,再平滑重载服务;不同系统的服务名称可能不同:

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status php-fpm

如果 PHP-FPM 服务名不是 php-fpm,应使用系统实际的服务名称。配置语法通过不等于应用一定能运行,还要确认 Nginx 转发的路径和 PHP-FPM 能看到的文件路径一致。

确认 PHP 处理链路时,不要在公网长期放置 phpinfo() 页面。该页面可能暴露 PHP 配置、扩展、路径和环境信息。确需临时诊断时,应限制访问,并在检查结束后立即删除。更安全的做法是通过服务器命令检查 PHP 信息,或使用仅本机可访问的诊断方式。

四、区分命令行 PHP 与网站使用的 PHP

执行 php -v 查看的是当前命令行所调用的 PHP,不一定是网站正在使用的 PHP。服务器可能同时安装多个版本,CLI 与 PHP-FPM 也可能加载不同的配置文件和扩展。因此,“命令行正常”不能单独证明 Web 环境正常。

php -v
php --ini
php -m

php --ini 显示命令行 SAPI 使用的配置文件,php -m 列出命令行加载的模块。Web 端应继续检查 PHP-FPM 服务使用的版本、池配置和日志;在容器部署中,还要确认命令是在正确的容器内执行。

PHP 版本也必须符合项目要求。查看项目的 composer.json 中 require 对 PHP 的限制,并核对框架或依赖的兼容要求。比如项目明确要求 PHP 8.2,那么 PHP 7.4 即使能启动,也可能在加载语法或调用函数时失败。反过来,版本更高也不保证所有旧项目都兼容。

五、检查扩展和 Composer 依赖

项目可能依赖 mbstring、curl、openssl、pdo_mysql、intl、gd 等扩展,具体以项目文档和 Composer 依赖为准。扩展缺失时,报错可能发生在启动阶段,也可能只在特定功能被调用时出现。

部署时应优先使用项目提交的 composer.lock 安装锁定版本,而不是在生产环境随意运行 composer update。后者会重新解析依赖,可能更新到与当前代码不匹配的版本。推荐流程示例:

cd /var/www/myapp
composer validate --no-check-publish
composer install --no-dev --prefer-dist --optimize-autoloader

选项含义需要结合项目理解:--no-dev 不安装 require-dev 中的开发依赖,前提是生产运行不依赖这些包;--prefer-dist 优先使用发行包,不保证每个依赖都一定能以该方式安装;--optimize-autoloader 优化自动加载器。执行前要确认生产环境有可用的 Composer、网络访问权限和满足要求的 PHP 扩展。

如果服务器不适合安装构建工具,也可以在受控的构建环境中按锁文件安装依赖,再将完整的部署产物发布到服务器。不要只上传业务源码却漏掉必须的 vendor 目录;也不要把含开发依赖的开发环境目录直接当作生产产物而不做评估。

可以用 Composer 检查平台需求:

composer check-platform-reqs --no-dev

该命令检查当前环境是否满足已安装依赖声明的平台要求,但不能替代应用的集成测试,也不能自动判断所有业务功能是否可用。若命令行检查通过而网站仍报扩展缺失,重点核对 PHP-FPM 与 CLI 是否使用相同版本和扩展配置。

六、排查入口文件、路由和重写规则

传统 PHP 项目可能直接由某个 index.php 处理请求;框架项目通常把公开入口放在 public/index.php,并依赖 Web 服务将不存在的路径转发到该入口。若站点根目录指错,可能出现首页能打开、子页面 404,甚至配置文件被放到可公开访问的位置。

对 Nginx 而言,常见的前端控制器规则是:

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

这条规则并不适用于所有应用。静态站点、API 服务和使用其他路由方案的项目可能需要不同配置。Apache 项目可能使用 .htaccess,但只有在 Apache 启用了相应的重写模块,并允许目录配置生效时,它才会按预期工作;Nginx 不会读取 .htaccess。

检查入口路径时,至少确认:配置的根目录确实包含预期入口;入口文件能被 PHP-FPM 读取;重写规则没有把静态资源或真实目录错误地转发;框架所需的命令行初始化或缓存步骤已经按文档执行。

七、检查权限,但不要用 777 代替排查

PHP-FPM 通常以特定系统用户运行。如果该用户不能读取代码、不能访问目录,或不能写入框架要求的缓存与上传目录,就可能出现 403、日志写入失败、缓存异常等问题。

先查看目录和文件的属主、属组及权限:

ls -ld /var/www/myapp /var/www/myapp/public
ls -l /var/www/myapp/public/index.php

PHP 读取文件通常需要相应目录的遍历权限和文件读取权限;写操作则应只赋予应用确实需要写入的目录,例如特定的缓存、日志或上传目录。具体属主和权限取决于部署方式、Web 服务用户及发布流程。

不要为了省事执行 chmod -R 777。这会让过多用户获得写权限,扩大文件被篡改的风险,而且往往掩盖了属主、组或挂载策略配置错误。更稳妥的做法是识别 PHP-FPM 的实际运行用户,仅对必要目录授予所需权限,并让代码文件保持不可由 Web 进程随意修改。

八、检查环境变量、配置文件与数据库连接

开发环境和生产环境经常使用不同的数据库地址、账号、缓存服务和密钥。部署后若仍读取开发配置,或环境变量没有注入到 PHP-FPM 进程,应用可能在启动时失败,也可能表现为数据库连接超时、认证失败或加密相关错误。

先根据项目文档确认配置来源:可能是环境变量、专用配置文件、容器编排配置或秘密管理服务。检查变量是否传递到了运行应用的进程,不要只在 SSH 登录后的交互式 Shell 中确认。PHP-FPM 服务通常不会自动继承所有交互式 Shell 环境变量,具体行为受服务配置与启动方式影响。

下面是一个最小 PHP 环境变量读取示例,可用于理解配置逻辑。它不是框架通用配置接口,实际项目应使用项目自身的配置系统:

<?php
declare(strict_types=1);

$databaseUrl = getenv('DATABASE_URL');

if ($databaseUrl === false || $databaseUrl === '') {
    http_response_code(500);
    error_log('DATABASE_URL is not configured');
    exit('服务暂不可用');
}

echo '数据库配置已读取';

示例不会打印数据库地址,避免把连接串、账号或密码返回给客户端。生产环境不要把数据库密码写进公开目录中的 PHP 文件,也不要把包含秘密的配置文件提交到公共代码仓库。若凭据曾意外暴露,应及时轮换,而不只是删除文件。

数据库问题应区分网络、认证、库名和驱动:确认数据库服务可达、白名单或防火墙允许连接、账号有目标库权限,并确认 PHP 加载了应用使用的 PDO 驱动或其他所需扩展。不要在排错时关闭 TLS 或把数据库开放到所有公网地址,除非经过明确评估并采取相应的访问控制。

九、白屏或 500 时,优先查日志而不是公开错误详情

生产环境通常不应把详细 PHP 错误直接显示给访客,因为错误内容可能包含服务器路径、SQL 片段、配置值或堆栈信息。应在受控的日志中记录错误,并用请求时间、请求 ID 或访问日志关联排查。

可以在 CLI 中确认当前 PHP 的错误日志设置:

php -i | grep -E 'error_log|log_errors|display_errors'

这条命令检查的是 CLI 配置,不能据此断定 PHP-FPM 的值完全相同。还需要查看 PHP-FPM、Web 服务及应用日志。若临时调整错误显示用于本机开发,应限制在本地环境,不要把详细错误页面长期开放在公网。

对于 500 错误,可按时间顺序对照访问日志和错误日志:先确认请求是否到达目标虚拟主机,再查看 PHP-FPM 是否接到请求,最后查应用日志中的异常。若访问日志没有对应记录,问题可能在 DNS、代理或前置负载均衡,而不是 PHP 代码。

十、命令行任务、定时任务与 Web 请求不是同一环境

项目中的脚本可能能通过 SSH 手动运行,却无法由计划任务执行。这通常与执行用户、工作目录、PHP 可执行文件、环境变量或权限不同有关。

定时任务应使用明确的 PHP 路径和脚本路径,并记录标准输出与错误输出。例如:

/usr/bin/php /var/www/myapp/bin/console app:task >> /var/log/myapp/task.log 2>&1

/usr/bin/php、脚本路径、命令名称和日志目录都应替换成项目实际值。定时任务中的相对路径通常容易引发问题,宜使用绝对路径。日志目录也必须对执行任务的用户可写。不要直接把包含密码的命令行参数暴露在进程列表或日志中。

十一、适用场景与需要注意的边界

上述方法适合排查常见的 PHP 网站和应用部署问题,但不是所有部署模式的完整说明:

  • 虚拟主机:可能无法访问系统服务管理命令,也可能无法修改全局 PHP 配置,应使用服务商提供的版本、扩展和日志面板。
  • Docker 或 Kubernetes:宿主机看到的文件和容器运行时看到的文件可能不同。日志、进程、网络与环境变量要在对应容器或编排配置中检查。
  • Apache、Nginx、Caddy 等服务:PHP 处理方式、伪静态规则和配置测试命令不同,不可照搬另一种 Web 服务的配置片段。
  • Serverless 或托管平台:文件系统、启动命令、请求时长和扩展支持可能受平台限制,应优先依据平台运行时约束排查。
  • 仅命令行运行的 PHP 程序:无需配置网站根目录或 HTTP 路由,但仍需检查 CLI 版本、扩展、依赖、工作目录和环境变量。

排障命令也有权限边界。查看服务状态、读取日志或调整权限可能需要管理员授权;生产服务器执行重启、改配置、删除缓存或修改数据前,应确认影响范围,并按团队发布流程操作。不要为了诊断直接删除数据目录、数据库表或锁文件。

十二、按顺序执行的快速检查清单

  1. 用 curl -i 确认返回状态码、响应头和实际响应。
  2. 确认域名指向正确地址,请求命中了正确的虚拟主机和站点根目录。
  3. 若源码被下载或显示,立即检查并暂停站点访问,修复 PHP 处理配置。
  4. 查看 Web 服务、PHP-FPM 和应用日志,按请求时间定位第一条相关错误。
  5. 分别核对 CLI 与 PHP-FPM 的 PHP 版本、配置文件和必需扩展。
  6. 根据 composer.json 与 composer.lock 检查 PHP 版本、依赖和平台要求。
  7. 确认入口目录、前端控制器、重写规则与框架部署文档一致。
  8. 检查运行用户对代码、缓存、日志和上传目录的访问权限,只开放必要的写权限。
  9. 确认生产环境变量、数据库网络与账号权限正确,避免把秘密写入公开响应或日志。
  10. 修改配置后先做语法检查,再按变更流程重载服务,并从浏览器和命令行分别验证。

PHP 源码部署失败通常不是单一原因。把问题按“请求是否到达—请求由谁处理—PHP 环境是否满足—项目依赖和配置是否完整—应用执行在哪里失败”的顺序拆开,能够避免反复盲目改配置。尤其要记住:CLI 的 PHP 不一定等于网站的 PHP,项目根目录不一定是公开目录,文件权限错误也不意味着应该开放 777;根据日志和实际运行环境验证,才是可重复、可审计的排查方式。

© 版权声明

相关文章