PHP项目目录结构范例:从混乱到优雅的架构实战指南
📚 目录导读
- 为什么你需要一套标准的PHP目录结构?
- 经典分层结构:MVC与模块化设计
- 现代PHP项目目录范例(基于Composer与PSR-4)
- 核心目录逐层拆解:App、Config、Public、Storage
- 扩展与进阶:多应用、微服务与领域驱动设计(DDD)
- 常见问题问答(FAQ)
为什么你需要一套标准的PHP目录结构?
在团队协作或独立开发中,混乱的目录结构是技术债的温床,一个清晰的结构能带来三个核心收益:可维护性(新成员能快速定位代码)、可测试性(依赖注入与命名空间解耦)、可部署性(公共目录隔离保护核心代码),根据PHP-FIG的PSR-4规范,优秀的目录结构能让自动加载机制发挥最大效率,避免手动 require 链的灾难。

经典分层结构:MVC与模块化设计
传统MVC(Model-View-Controller)在PHP中已演变出多种流派,最基础的范例是:
project/
├── app/
│ ├── Controllers/
│ ├── Models/
│ └── Views/
├── public/
└── routes.php
但面对复杂业务,单纯MVC容易让Model变成“上帝对象”,建议引入服务层(Service)和仓库层(Repository)。
app/
├── Http/
│ ├── Controllers/ # 只负责请求响应
│ └── Middleware/ # 鉴权、日志
├── Services/ # 业务逻辑编排(如订单结算)
├── Repositories/ # 数据库查询封装
└── Models/ # 数据属性映射(Eloquent或Doctrine实体)
现代PHP项目目录范例(基于Composer与PSR-4)
当前主流框架(Laravel、Symfony、ThinkPHP)都遵循一套“现代分层”模式,以下是一个面向长线维护的通用范例:
my-php-app/
├── app/ # 应用核心(业务代码)
│ ├── Console/ # Artisan命令、计划任务
│ ├── Exceptions/ # 自定义异常处理
│ ├── Http/
│ │ ├── Controllers/ # 控制器层
│ │ ├── Middleware/ # 中间件(限流、CORS)
│ │ └── Requests/ # 表单验证请求类
│ ├── Models/ # 数据模型(非数据库语句)
│ ├── Providers/ # 服务提供器(框架启动引导)
│ ├── Services/ # 服务层(核心业务逻辑)
│ └── Support/ # 门面、辅助函数
├── bootstrap/ # 框架启动引导文件
├── config/ # 所有配置文件(返回数组)
├── database/ # 迁移与种子数据
│ ├── migrations/
│ ├── factories/
│ └── seeds/
├── public/ # Web根目录(唯一对外暴露)
│ ├── index.php # 前端控制器
│ ├── .htaccess # Apache重写规则
│ └── assets/ # 编译后的CSS/JS(可选)
├── resources/ # 视图、语言包、未编译资源
│ ├── views/
│ ├── lang/
│ └── assets/ # SASS/TS源文件
├── routes/ # 路由定义(web.php, api.php)
├── storage/ # 运行时文件(日志、缓存、文件上传)
│ ├── app/
│ ├── framework/
│ └── logs/
├── tests/ # 单元与功能测试
├── vendor/ # Composer依赖(忽略)
├── composer.json # 依赖清单与自动加载映射
├── .env.example # 环境变量模板
└── artisan # CLI入口(若用Laravel)
核心目录逐层拆解:App、Config、Public、Storage
-
App目录:这是“你的世界”,务必坚持按业务模块分包,而非按技术类型。
Services/ ├── Payment/ │ ├── WechatPayService.php │ └── AlipayService.php └── Order/ └── OrderService.php这种做法在大型项目中比扁平结构更清晰。
-
Config目录:所有配置项返回数组,避免硬编码,使用
env()辅助函数读取.env文件。配置文件不写业务逻辑。 -
Public目录:这是Web服务器入口,只放
index.php和静态资源。.env文件和app/目录都在上一级,防止被直接HTTP访问泄露源码。 -
Storage目录:权限必须为
755或775(写权限给php-fpm用户),建议把日志与缓存分离到logs/和framework/cache/,便于日志收集工具接入。
扩展与进阶:多应用、微服务与领域驱动设计(DDD)
-
多应用架构(如:admin 与 api 分离):
app/ ├── Admin/ │ ├── Controllers/ │ └── Services/ └── Api/ ├── Controllers/ # 返回JSON资源 └── Transformers/ # 响应格式转换使用子命名空间
App\Admin与App\Api配合PSR-4即可。 -
DDD(领域驱动设计):适用于复杂业务。
app/ ├── Domain/ │ ├── Order/ # 聚合根、实体、值对象 │ └── Shared/ # 领域事件 ├── Application/ # 应用服务(事务、编排) └── Infrastructure/ # 仓储实现、第三方接口注意:DDD要求极高,不要为了炫技而过度设计。
常见问题问答(FAQ)
Q1: 为什么 public/index.php 里的代码只有几行?
A: 这是“前端控制器”模式,所有请求先进入 index.php,它负责加载自动加载器、启动框架,并将请求分发到路由,此举将系统初始化和业务隔离,安全且灵活。
Q2: 我可以把 storage/ 目录放在项目外吗?
A: 完全可以,在部署时,将 storage/ 软链接到独立磁盘分区(如 /data/myblog/storage),这能避免代码升级时覆盖本地文件,也方便备份日志。
Q3: 目录太深会不会影响性能?
A: 不会,PHP只解析需要加载的类文件(由Composer生成 classmap 或使用 opcache),目录嵌套合理的主要依据是命名空间清晰度,而非深度,只要坚持PSR-4,性能开销可忽略。
Q4: 如何处理第三方库生成的额外目录(如 bootstrap/cache/)?
A: 将这些目录加入 .gitignore,并在部署脚本中通过 php artisan optimize 生成,不要手动去编辑它们,它们只是“编译后”的缓存产物。
Q5: 若使用原生PHP(无框架),结构如何简化? A: 可精简为:
project/
├── core/ # 核心类(Router, Database)
├── controllers/
├── models/
├── views/
├── public/
├── config/
└── helpers/
但强烈建议至少用 composer.json 引入自动加载,告别手动 include。
目录结构不是装饰,它是项目健康的骨架,无论你使用Laravel还是ThinkPHP,遵循“按业务分层、配置外置、公共目录隔离”原则,你的代码将赢得未来的维护者尊重,从明天开始,重构你的 uploads/ 和 assets/ 吧——那将是优雅架构的第一步。