PHP 是什么
这一课你将能做到
- 说清楚 PHP 在一次 Web 请求中的位置和完整处理流程
- 解释 PHP「每请求重新加载」的运行模型,以及 PHP-FPM 在其中的角色
- 在本机准备好 PHP 8.2 环境,用 CLI 和内置服务器各跑通第一段代码
为什么要学
PHP 是世界上部署最广的服务端语言之一:WordPress 支撑了全球四成以上的网站,Laravel、Symfony 是大量创业公司的默认选择,Facebook 早期也靠 PHP 起家。作为 Java 开发者,理解 PHP 的运行模型,能让你在选型评审、接手老项目、跨栈协作时有专业判断力,而不是停留在「PHP 很烂」「PHP 简单」的刻板印象上。
人话:浏览器发出请求 → Web 服务器收到 → 交给 PHP 处理 → PHP 查数据库、算业务逻辑 → 生成 HTML 或 JSON 返回浏览器。PHP 就是中间那个「处理」的引擎,和 Java Web 里 Servlet/Controller 扮演的角色类似,但生命周期完全不同。
先建立图像
一次请求在 PHP 世界里的完整旅程:
浏览器
│ HTTP 请求 GET /index.php
▼
Nginx / Apache(Web 服务器,只管收发 HTTP)
│ 发现是 PHP 文件,通过 FastCGI 协议转发
▼
PHP-FPM 进程池(常驻内存的主进程 + 一组工作进程)
│ 取一个空闲工作进程,加载脚本,执行
▼
你的 index.php
│ 读超全局变量、查库、拼字符串
▼
输出 HTML/JSON,原路返回给浏览器
关键点:工作进程干完这一单,所有变量立即销毁,下一个请求从零开始。这就是 PHP 著名的「共享无关(shared-nothing)」架构,理解了它,PHP 的大量设计取舍都能想通。
跟着做
步骤 1:确认 PHP 环境
打开终端执行:
php -v
看到 PHP 8.2.x 或更高版本即可。没有环境时:Windows 可用 phpstudy、XAMPP 或直接上 Docker;macOS 用 brew install php;Linux 用发行版包管理器装 php8.2-cli。建议先学 CLI 模式,后面再上 FPM。
步骤 2:CLI 跑第一段代码
新建 hello.php,输入:
<?php
declare(strict_types=1);
$name = 'PHP';
printf("你好,%s!当前版本:%s\n", $name, PHP_VERSION);
执行 php hello.php,输出:
你好,PHP!当前版本:8.2.21
<?php 是 PHP 代码的开始标记;PHP_VERSION 是内置常量;CLI 模式下没有 HTTP,纯本地执行。
步骤 3:内置 Web 服务器跑通 HTTP
新建目录 demo,里面放 index.php:
<?php
header('Content-Type: text/plain; charset=utf-8');
echo '现在是 ' . date('H:i:s') . PHP_EOL;
echo '请求方法:' . $_SERVER['REQUEST_METHOD'] . PHP_EOL;
在该目录执行:
php -S localhost:8000
浏览器访问 http://localhost:8000,能看到当前时间和 GET。反复刷新,时间在变——说明每次请求都重新执行了一遍脚本。
步骤 4:亲手感受 shared-nothing
在 demo 里加 counter.php:
<?php
$count = 0;
$count++;
echo "count = {$count}\n";
无论刷新多少次浏览器,页面永远是 count = 1。变量不会跨请求存活,想保存状态只能放 Session、Cookie、数据库或 Redis。这和 Spring Boot 里单例 Bean 的字段能一直存活是本质区别。
讲清楚要点
- 解释执行 + OPcache 缓存:PHP 脚本每次请求会先编译成操作码(opcode),OPcache 扩展把编译结果缓存在共享内存,跳过重复编译。PHP 8 的 JIT 是 OPcache 之上的可选加速层,不是替代品。
- SAPI 决定运行形态:CLI(命令行)、FPM(FastCGI Process Manager)、Apache mod_php 都是把 PHP 引擎接到不同宿主的接口。生产环境主流是 Nginx + PHP-FPM,CLI 用于脚本和开发调试。
- 共享无关是特征不是缺陷:请求之间天然隔离,一个请求崩了不影响下一个,扩容就是加机器;代价是框架每次请求都要重新引导,靠 OPcache 和 PHP 7.4+ 的预加载(preload)把开销压到最低。
- 现代生态三大件:Composer 负责依赖管理(对标 Maven),Packagist 是官方包仓库(对标 Maven Central),Laravel / Symfony 是两大主流框架(对标 Spring Boot)。WordPress、WooCommerce 是 CMS 世界的巨头。
你可能会懵的地方
- PHP 只能写网站吗? 不是。CLI 模式能写命令行工具、定时任务、队列消费者;Swoole、RoadRunner 等方案还能让 PHP 常驻内存处理请求。但核心生态确实是围绕 Web 请求构建的。
- PHP 7 和 PHP 8 差距很大吗? 很大。PHP 7 性能是 5.x 的两倍以上,PHP 8 加入 JIT、枚举、构造器属性提升、纤程、只读属性,类型系统已接近静态语言。网上大量「PHP 很差」的资料停留在 5.x 时代,不必采信。
- 为什么每个请求都要重新跑一遍框架引导代码? shared-nothing 的必然结果。OPcache 缓存编译结果,预加载能把核心类在 FPM 启动时载入共享内存,让引导成本大幅下降,但仍无法做到 JVM 那种「启动一次服务终身常驻」。
- WordPress 代码那么老,能代表 PHP 现状吗? 不能。WordPress 是历史包袱较重的项目,官方现已支持 PHP 8.2+。评价 PHP 的现代性应看 Laravel、Symfony 的代码质量,而不是拿十几年前的插件代码说事。
- 学 PHP 前需要先学 HTML 吗? 最好会。PHP 的经典工作方式就是生成 HTML 或 JSON,模板语法和 HTML 标签交织,不懂 HTML 会寸步难行。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 用全局变量或静态属性在请求间存数据 | 下个请求就被清空 | 用 Session、Redis、数据库持久化状态 |
生产环境用 php -S 内置服务器 | 单进程、无并发、无健壮性 | 生产用 Nginx + PHP-FPM |
| 认为 PHP 无编译就无性能瓶颈 | 每请求重复编译耗 CPU | 开启 OPcache,必要时配置 JIT/预加载 |
| 对照 Java 经验把对象缓存到进程内存 | PHP 进程内对象请求结束即销毁 | 用 Redis 等外部缓存 |
学老教材用 mysql_* 系列函数 | PHP 7 已整体移除 | 统一用 PDO |
卡点急救
- 提示
php不是内部或外部命令:PHP 未安装或未配置 PATH。Windows 把php.exe所在目录加入系统环境变量 PATH,重开终端再试。 - 浏览器直接显示
<?php源码:文件没有被 PHP 处理。必须通过http://localhost:8000访问,不能双击文件用浏览器打开。 php -S报端口被占用:Windows 用netstat -ano | findstr 8000找到占用进程结束它,或直接换端口php -S localhost:8080。
面试常问
- 描述一次 PHP 请求的完整生命周期。
Nginx 接收请求,按 location 规则把
.php请求通过 FastCGI 协议转发给 PHP-FPM;FPM 从空闲工作进程池中取一个进程执行脚本;脚本经 OPcache 读取编译产物、初始化变量、执行业务、冲刷输出缓冲区;请求结束,引擎销毁本请求全部状态,进程回到池中等待下一个请求。 - shared-nothing 架构的优缺点? 优点:请求天然隔离、没有跨请求共享状态引发的并发问题、无状态化使水平扩容简单。缺点:进程内无法维持连接池和常驻对象,框架引导有重复开销,状态必须外置到 Redis、数据库等存储。
- PHP-FPM 是什么?和 CGI 有什么区别? 传统 CGI 每个请求都要 fork 一个新进程执行后退出,开销巨大;FastCGI 让进程常驻、循环处理请求;PHP-FPM 是 FastCGI 协议的 PHP 实现与进程管理器,提供主进程守护、工作进程池管理、慢执行日志、平滑 reload 等能力。
自测三问
- Nginx 和 PHP-FPM 之间用什么协议通信?
- 为什么 PHP 里不存在 Spring Boot 那种「单例 Bean 字段跨请求共享」?
- OPcache 解决什么问题?JIT 和它是什么关系?
小结一句话
PHP 是为 Web 请求而生、以「每请求重新加载」为特征的解释型语言,生产形态是 Nginx + PHP-FPM + OPcache,现代生态由 Composer 与 Laravel/Symfony 支撑。
延伸
- PHP 官方手册:PHP 是什么
- 下一课:基础语法——变量、数组、控制流与类型声明。