PHP 原理大纲

这份不是“会不会写 PHP”的大纲,而是“PHP 在后端系统里到底是怎么工作的,以及为什么它适合你这类复杂业务系统”。

一、总纲

PHP 原理层最核心的 5 条主线:

  1. 运行模型
  2. 请求生命周期
  3. 进程管理与 PHP-FPM
  4. 代码组织与框架机制
  5. 复杂业务系统里的优势与边界

二、PHP 运行模型

1. PHP 本质是什么

PHP 本质上是解释执行的服务端脚本语言。 在 Web 场景里,通常是每次请求来到时由 PHP-FPM 拉起 worker 执行脚本,请求结束后释放大部分运行态。

2. PHP 的经典模型是什么

经典模型是:

  • 请求进来
  • 初始化运行环境
  • 执行业务逻辑
  • 输出响应
  • 请求结束后释放运行上下文

这种模型的好处是:

  • 简单
  • 稳定
  • 请求之间默认隔离
  • 不容易因为内存状态污染彼此

坏处是:

  • 每个请求都会重复初始化一部分资源
  • 不适合天然长连接场景

3. 为什么这个模型适合复杂业务后台

因为复杂业务系统更看重:

  • 规则实现效率
  • 团队协作效率
  • 请求之间状态隔离
  • 问题容易收敛

你可以把这个挂回自己项目:

像订阅、支付、退款、后台、活动、规则配置这种复杂业务,不一定最追求单请求极限性能,反而更看重落地效率和可维护性。


三、Nginx + PHP-FPM 请求链路

1. 链路是什么

典型链路是:

  1. 客户端请求到 Nginx
  2. Nginx 判断静态资源还是动态请求
  3. 动态请求通过 FastCGI 转给 PHP-FPM
  4. PHP-FPM 选一个 worker 执行脚本
  5. 执行结果返回 Nginx
  6. Nginx 再返回客户端

2. Nginx 和 PHP-FPM 分别干什么

Nginx

  • 连接接入
  • 路由
  • 静态资源处理
  • 反向代理
  • 转发 PHP 动态请求

PHP-FPM

  • 管理 PHP worker 进程
  • 执行 PHP 脚本
  • 控制并发 worker 数量

3. 面试可延伸点

  • 为什么 PHP-FPM 会打满
  • max_children 是什么
  • 慢请求怎么查
  • 为什么不是 Nginx 一层就够了

四、PHP-FPM 原理重点

1. PHP-FPM 是什么

PHP-FPM 是 FastCGI Process Manager。 它的核心价值不是“让 PHP 能跑”,而是:

  • 管理 worker 进程
  • 控制并发
  • 提高稳定性
  • 支持慢日志、平滑重启、池化管理

2. 为什么要用进程池

因为 PHP Web 请求本质上要靠 worker 去执行。 如果每次请求都临时起一个 PHP 进程,成本太高;如果完全没有上限,又容易打爆机器。 所以需要进程池去做平衡。

3. 常见模式

  • static
  • dynamic
  • ondemand

4. 面试时怎么理解“FPM 打满”

本质上是请求执行速度和到达速度失衡。 可能原因是:

  • 慢 SQL
  • 外部依赖超时
  • 代码逻辑太重
  • worker 数不足
  • 突发流量

所以“调大 max_children”只是手段,不是根因修复。


五、PHP 框架机制原理

这一块你不需要背框架源码,但要理解框架在解决什么问题。

1. 路由

把 URL / method 映射到控制器或处理函数。

2. 中间件

在请求真正进入业务逻辑前后做统一处理,比如:

  • 鉴权
  • 日志
  • 限流
  • 参数校验

3. IOC / DI

通过依赖注入把“接口”和“实现”解耦,降低耦合度,方便替换和测试。

4. Service / Repository / Domain 分层

不是为了好看,而是为了:

  • 让业务规则不要散落在 controller
  • 让复杂逻辑更集中
  • 让测试和扩展更容易

5. 配置与容器

框架通过配置文件和容器,把系统中的公共能力组织起来。


六、复杂业务建模、幂等与领域设计

这一块非常适合你,因为它正好对应你做过的订阅、支付、退款、归因和后台规则这些复杂业务。

1. 为什么 PHP 项目做复杂业务不能只靠 controller + model

因为一旦业务进入多状态、多入口、多回调的阶段,逻辑很快就会散。

常见表现是:

  • 控制器里堆满 if / else
  • 同一条规则在多个入口重复判断
  • 支付状态、订阅状态、权益状态混成一个字段
  • 出问题时只能靠猜,不知道哪一层写坏了状态

所以复杂业务通常至少要有:

  • controller 入口层
  • service / domain 业务层
  • repository 持久化层
  • 状态机 / 枚举 / DTO / VO 这类稳定表达

2. 为什么 PHP 项目里的幂等必须落数据库

PHP-FPM 本质上是多进程请求模型,请求之间默认不共享内存状态。 这意味着你不能指望某个进程里的静态变量或内存标记来全局挡住重复请求。

真正稳的做法通常是:

  • 先定义业务唯一键
  • 事件表落库并加唯一索引
  • 主状态更新放事务
  • 副作用继续二次幂等
  • 分布式锁只做并发辅助,不替代数据库幂等

3. 领域设计在 PHP 项目里到底解决什么

领域设计也就是 DDD,核心不是堆术语,而是让代码围着业务语义组织。 当业务里经常出现“恢复订阅”“退款完成”“权益发放”“订单关闭”这些稳定概念时,代码模型就应该尽量贴近这些概念。

你最需要掌握的几个词:

  • 实体:订单、订阅、用户
  • 值对象:金额、时间段、状态快照
  • 聚合:一组必须一起保持一致的对象边界
  • 仓储:聚合的持久化入口
  • 领域服务:跨多个实体的规则承载
  • 领域事件:关键业务事实发生后的通知

4. 什么场景适合上 DDD,什么场景不适合

适合:

  • 支付、退款、订阅、库存、审批、营销规则
  • 状态流转复杂、边界不清容易写乱的系统

不适合一上来就上:

  • 简单 CRUD 后台
  • 业务还在快速试错
  • 团队没有统一建模习惯,只会把 DDD 做成形式

5. 你在面试里可以怎么串起来

我做 PHP 复杂业务时,不会只停留在 controller + service 这种口头分层。我会继续看状态模型、事务边界、幂等边界和领域边界。如果一块业务已经明显重于 CRUD,我会用领域设计的方式重新梳理规则归属。


七、autoload 与代码组织

1. Composer autoload 在解决什么问题

解决类加载问题,让代码按命名空间和目录规则组织起来,而不是手写 include / require。

2. Composer autoload 实际是怎么工作的

你可以把它理解成四步:

  1. composer.json 里声明自动加载规则
  2. 执行 composer installcomposer dump-autoload
  3. Composer 在 vendor/composer/ 下生成映射文件
  4. 项目入口先 require vendor/autoload.php,把自动加载器注册进去

之后代码里只要第一次引用某个类,Composer 就会根据映射规则去定位对应文件并加载。

3. 生成出来的文件大概是什么

常见会看到:

  • vendor/autoload.php
  • vendor/composer/autoload_real.php
  • vendor/composer/autoload_psr4.php
  • vendor/composer/autoload_classmap.php
  • vendor/composer/autoload_static.php

这些文件本质上就是:

  • 哪个命名空间前缀对应哪个目录
  • 哪些类有直接映射
  • 哪些公共文件要直接引入

所以 Composer autoload 的核心不是“自动猜”,而是“提前把查找规则生成为 PHP 代码”。

4. PSR-4 的意义

核心不是记规范,而是:

  • 目录结构清楚
  • 命名空间统一
  • 代码组织可维护

这对大型项目尤其重要。

例如:

  • App\\Services\\OrderService 对应 app/Services/OrderService.php
  • Domain\\Payment\\SubscriptionService 对应 src/Domain/Payment/SubscriptionService.php

只要命名空间和目录约定稳定,代码协作就会更顺。

5. classmap 和 files 又是什么

除了 PSR-4,Composer 还支持:

  • classmap:直接把类和文件路径做成映射表,适合历史目录或不规范目录
  • files:项目启动时就主动引入的文件,常用来放 helper 函数

所以面试时如果只会说 PSR-4,会显得稍微薄一点。更稳的说法是:

现代项目主流是 PSR-4,classmap 和 files 更多是兼容特殊目录和全局函数场景。

6. Laravel 启动时怎么接入 Composer autoload

Laravel 的请求入口通常从 public/index.php 开始。 这里会很早执行:

require __DIR__.'/../vendor/autoload.php';

这一步完成后:

  • Composer 自动加载器已经注册好了
  • 后面的应用实例、服务提供者、控制器、中间件、事件监听器、命令类都能按命名空间加载

也就是说,Laravel 自己的容器、路由和中间件体系,是建立在 Composer autoload 先可用的基础上的。

7. composer dump-autoload -o 在干什么

  • dump-autoload:重新生成自动加载文件
  • -o:优化 autoload,把更多内容整理成更直接的映射

开发时你更关注“类能不能被正确找到”; 线上你更关注“查找路径是否足够直接、启动和解析是否更稳”。

8. 面试里怎么把这块讲得像做过

我对 Composer autoload 的理解,不是停留在“它会自动加载类”,而是知道它会把 composer.json 里的规则生成到 vendor/composer 下面,再由入口文件 require vendor/autoload.php 注册到运行时。Laravel 其实也是先站在这套自动加载能力上,后面才继续做容器、路由和服务解析。


八、复杂业务系统里为什么 PHP 仍然合理

核心答案

因为 PHP 在复杂业务后端里,优势不是“语法简单”,而是:

  • 业务落地快
  • 团队协作成熟
  • 请求隔离天然清晰
  • 生态成熟
  • 后台系统和业务系统开发效率高

你的项目里怎么讲

海外业务平台

  • 订阅
  • 支付
  • 退款
  • 权益同步
  • 后台和运营配置

这些都属于规则复杂、协作密集、迭代频繁的系统,PHP 很合适。

核心业务链路

  • 活动
  • 小程序
  • 商家
  • 交易

也都偏复杂业务落地,而不是极致高并发纯计算服务。


九、PHP 的边界

这一段很重要,能体现你不是盲目站队。

PHP 不擅长的典型场景

  • 长连接
  • 极高并发实时处理
  • 大量消费者常驻服务
  • 重 IO 并发协程场景

所以怎么做

你的最佳回答是:

我不会让 PHP 承担所有角色。 复杂业务主链路、后台和规则配置继续放 PHP; 实时链路、高并发消费者、长生命周期服务拆到 Go。 这是我项目里一直在做的分工方式。


十、你最应该能顺着讲出的原理链

你至少要能完整顺着讲出这条链:

请求 -> Nginx -> FastCGI -> PHP-FPM -> worker 执行脚本 -> 框架路由/中间件 -> service 层业务逻辑 -> MySQL/Redis/外部服务 -> 响应返回

只要这条链你讲顺了,PHP 原理层就不会显得空。


十一、最适合你的结尾表达

我对 PHP 的理解,不是停留在语法和 CRUD,而是理解它在 Web 后端里的运行模型、进程池方式和框架分层方式,也知道它最适合承接什么类型的业务。对我来说,PHP 不是“落后语言”,而是复杂业务系统里非常高效的一层。