很多刚接触 ThinkPHP6 的开发者安装完框架第一件事就是打开浏览器访问首页结果看到的不是欢迎页而是一个又一个 404。别慌这大概率不是框架本身的问题而是安装完成后必做的两个前置操作漏掉了一个是 URL 重写rewrite配置另一个是正式开发前必须决定的多应用模式配置。这两个操作直接影响你能不能优雅地访问路由、能不能按需拆分多个应用模块也和后续的线上部署、伪静态方案强相关。这篇文章就专门把这套前置流程讲透。不管你是刚从一个旧项目迁过来的老 PHP 开发者还是第一次用 ThinkPHP6 写新项目的初学者只要照着下面这套思路把环境和配置理顺后续写接口、写页面都会省心很多。我尽量把每一步背后的为什么也说清楚这样大家碰到问题时不至于只会照着抄还能自己判断问题出在哪个环节。1. 环境底线与项目创建这些前置配置决定你后面顺不顺1.1 PHP 版本与扩展项ThinkPHP6 的技术底线严格来说ThinkPHP6 要求 PHP 7.1.0 以上但我个人强烈建议直接用 PHP 7.4 或者 PHP 8.0 及以上版本。理由很简单7.1 虽然能跑但很多第三方扩展包早就放弃了老版本兼容你后面每装一个依赖就可能踩一次版本坑。PHP 7.4 是性价比最高的选择函数特性、性能表现都很稳妥如果项目是全新启动直接上 PHP 8.0 也完全没问题TP6 官方对 PHP8 的兼容已经相当成熟。除了版本还要确认 PHP 环境里安装了几个关键扩展PDO 扩展这是数据库操作的基础TP6 的查询构造器和 ORM 都依赖它。CURL 扩展涉及到远程请求、第三方 API 调用时必用。mbstring 扩展字符串处理尤其是中文场景没这个扩展框架直接报错。fileinfo 扩展文件上传时验证文件类型要用到很多人漏装它。检查方式很简单命令行敲php -m看一下已加载的模块或者直接在项目里放一个探针文件访问查看。框架安装完如果有红色错误提示八成就是某个扩展没开先把扩展装好再继续。1.2 Composer 创建项目与压缩包下载的差别ThinkPHP6 的官方推荐安装方式是用 Composercomposer create-project topthink/think tp6 --prefer-dist执行完后当前目录下会有一个tp6文件夹这就是项目根目录。--prefer-dist参数是让 Composer 优先用压缩包方式下载依赖比逐个 git 克隆要快得多。国内网络环境下如果下载慢提前把 Composer 镜像源切换到国内镜像能省下一大块时间。也有同学习惯直接从官网下载完整源码包解压后就能跑。这种方式不是不行但有个隐患源码包里自带的依赖有可能不是最新版本而且你没法通过 Composer 灵活地增删扩展包。后面但凡想装多应用扩展、想升级某个组件就得手动和 vendor 目录纠缠不清。所以能用 Composer 就尽量用 Composer这是项目能够长期维护的基础。项目创建成功后命令行进入目录执行php think run默认会启动一个 PHP 内置的开发服务器地址是http://127.0.0.1:8000。看到欢迎页面说明基础环境通过。1.3 目录结构认知TP6 和 TP5 最大的不同这个点值得单独强调因为很多人从 ThinkPHP5 转过来时非常不适应。TP5 的项目结构里自带application目录并且默认就是模块化结构里面已经分好了index、admin这类模块。但 ThinkPHP6 把一切都简化了默认是单应用模式安装完成后的目录结构大致如下tp6 ├── app │ └── controller │ └── Index.php ├── config ├── public │ └── index.php ├── route ├── runtime ├── vendor └── think注意看这里没有application目录而是app目录这不是单纯改个名而是框架的设计思路变了。TP6 默认没有所谓的前台模块和后台模块之分就是一个干净的应用。如果你希望像 TP5 那样拆出多个模块就需要走多应用模式也就是我后面专门讲的内容。还有一个细节正式部署时站点根目录必须指向public目录而不是项目根目录。这样做既能避免通过 URL 直接访问到源码文件也让重写规则更安全。很多人直接把根目录指到项目根目录然后发现各种路径问题、安全漏洞根源就在这。2. URL 重写rewrite配置Apache、Nginx 两种主流方案一次搞懂2.1 为什么要藏起 index.phpURL 重写的底层逻辑ThinkPHP6 默认的 URL 访问形式是http://localhost/index.php?s/index/hello如果你什么都没配访问欢迎页看到的长这样。这种形式的缺点很明显URL 里带着入口文件名既不美观语义也不清晰有的场景还会影响路由参数解析。而经过 URL 重写之后访问地址变成http://localhost/index/hello看起来清爽很多也更接近现代框架的访问风格。所谓的 rewrite就是把后一种好看的形式在服务端透明地重写成前一种真实请求让框架仍然能正常接收参数。你作为开发者只需要专注于配置好规则浏览器和用户根本感知不到底层发生了什么。这个隐藏 index.php的操作在不同 Web 服务器上的做法完全不同。下面逐一说明。2.2 Apache 环境.htaccess 与 AllowOverride 的配合Apache 环境主要靠.htaccess文件实现。TP6 的public目录下默认已经带了一个.htaccess文件内容如下IfModule mod_rewrite.c Options FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L] /IfModule这段规则的含义是当请求的文件路径既不是真实存在的目录也不是真实存在的文件时就把请求交给index.php处理同时保留原始路径参数。QSA表示把原有查询字符串拼接上PT是让重写结果交给下一个处理器L表示匹配后立即停止继续匹配。但很多人在 Apache 下配置失效问题往往不在.htaccess文件本身而是虚拟主机配置里没开AllowOverride。你需要确认当前站点的Directory配置中允许覆盖Directory /var/www/html/tp6/public AllowOverride All /Directory如果写的是AllowOverride None.htaccess就形同虚设规则完全不会生效。另一个容易被忽略的点是mod_rewrite模块本身有没有启用。Apache 2.4 下建议直接用命令启用a2enmod rewrite然后重启 Apache 验证。2.3 Nginx 环境root 指向与 rewrite 规则的完整配置Nginx 下没有.htaccess这种按目录生效的机制所有规则都写在虚拟主机配置的location块中。先看一个完整的最小配置server { listen 80; server_name localhost; root /var/www/html/tp6/public; index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }这里最关键的两个点root必须指向项目的public目录否则框架的静态资源全部加载不出来。location /里的if (!-e $request_filename)判断请求的文件或目录是否存在不存在才交给index.php处理。rewrite ^(.*)$ /index.php?s$1 last;中的$1会捕获原始 URL 路径转成 ThinkPHP 需要的参数格式。有些同学用的是try_files方案location / { try_files $uri $uri/ /index.php?s$uri; }这两种写法效果基本一致选一种就行。但从我的实际经验看rewrite方式在传递复杂路由参数时更可控一些尤其是路径中包含特殊字符时不容易丢参数。还有一点要注意如果你的 Nginx 配置里原本就有多个location务必让 PHP 解析相关的location放在后面避免正则优先匹配导致 rewrite 出来的/index.php?s...没有被正确交给 PHP 处理。配置完后用nginx -t检查语法然后reload重启服务。2.4 配置完成后的验证步骤伪静态规则配没配好最简单的验证方法是访问一个不存在的地址比如http://localhost/index/hello。如果 URL 重写生效这个地址会进入框架的路由解析流程返回的是一个可读的错误提示页面而不是 Nginx 或 Apache 默认的 404 页面。还有一个更直接的验证方式访问http://localhost/index.php/index/hello对比和重写后的地址返回的结果。如果两者都能正常响应说明 rewrite 规则已经把好看地址和真实地址打通了。如果前者正常、后者 404问题基本锁定在服务端重写规则上。3. 多应用模式的完整改造从单应用变成多应用的前置配置3.1 认清 TP6 的应用模型默认是单应用多应用是扩展这是 ThinkPHP6 和 TP5 在架构上最大的区别很多人就是在这块卡住的。TP6 默认只有一个应用app目录下直接就是控制器层。它不像 TP5 那样天然带着index模块、admin模块。如果你只需要写一个小网站、一个接口服务单应用模式完全够用。但稍微正式一点的项目几乎都会面临前台和后台分离、API 端和管理端的拆分问题。这时候就需要切换到多应用模式。TP6 官方把多应用做成了一个独立的扩展包名字叫think-multi-app意味着核心框架不再内置多应用能力需要你按需引入。这样做的好处是核心足够精简你想要什么功能就装什么扩展而不是被默认的全家桶拖累。3.2 开启多应用模式并调整目录结构第一步安装扩展包composer require topthink/think-multi-app安装完成后修改config/app.php配置文件把多应用开关打开auto_multi_app true,光开开关还不够你要手动创建对应的应用目录。比如这里准备做前台index、后台admin两个应用那么在app目录下创建app ├── admin │ ├── controller │ └── view ├── index │ ├── controller │ └── view └── common注意每个应用目录内部还要自行组织好controller目录控制器类文件的命名空间也要跟着变。比如index应用的控制器命名空间是app\index\controlleradmin应用的控制器命名空间是app\admin\controller。这个命名空间如果写错框架会直接报控制器不存在的错而且容易让人摸不着头脑。配置文件里auto_multi_app开启后如果你仍然希望某个应用作为默认入口可以设置默认应用default_app index,这样访问根域名时会自动落到index应用。3.3 URL 规则、默认控制器与路由文件的关系多应用模式下URL 格式变成http://localhost/应用名/控制器/操作比如访问admin应用下的User控制器中的list方法URL 就是http://localhost/admin/user/list。如果省略应用名则走default_app指定的默认应用。每个应用下还可以单独设置默认控制器和默认操作在对应的应用配置文件中配置即可。这里有一个容易误解的地方多应用模式下route目录下的app.php路由文件是全局生效的也就是说不管哪个应用发起的请求都会加载同一个路由文件。如果你希望让admin应用走一套独立的路由规则、index应用走另一套那么需要在各自的app/admin/route、app/index/route目录下放置自己的路由文件并在全局路由文件中根据应用名做分发或者干脆在配置里关闭自动多应用并手动绑定。我见过好几个项目开发阶段把所有路由都写在一个route/app.php里结果应用一多路由规则越来越乱根本分不清哪个规则属于哪个应用。正确的做法是提前规划好路由文件的组织方式哪怕前期多花十分钟后面维护的时候省下的时间远不止这些。3.4 域名绑定让不同域名指向不同应用如果你有多个域名希望www.example.com走前台、admin.example.com走后端可以直接配置域名绑定。在config/app.php中有两个相关数组// 应用域名绑定 domain_bind [ admin.example.com admin, ], // 应用映射 app_map [ admin admin, ],domain_bind的作用是把指定域名直接定位到某个应用。配置后访问admin.example.com就不再需要 URL 里写admin前缀。这个功能在生产环境特别实用前后台彻底在地址上分离用户感知不到的底层拆分。注意一点域名绑定和 URL 传参应用名不要混用。一旦绑定了域名再用 URL 传应用名可能会出现预期外的冲突所以项目里要统一一种方式要么靠域名区分要么靠 URL 前缀区分别两个同时用。4. 配置完成后最容易踩的坑与完整排查链路4.1 伪静态规则没生效到处都是 404这个问题的出现频率最高。有一次朋友找我排查他明明把官方文档里的 Nginx rewrite 配置抄得一字不差访问首页还是 404。我远程看了一圈发现他的location /里确实有 rewrite 规则但root指到了项目根目录而不是public目录。因为入口文件index.php在public下面根目录设置错位导致 rewrite 统统找不到入口自然全是 404。排查这类问题我的固定思路是第一步先把 rewrite 规则里的地址改成带index.php的形式手动访问。如果http://localhost/index.php/index/hello能通说明框架没问题问题在 rewrite 层。第二步确认站点根目录是不是指向public。第三步检查 rewrite 规则是否真的被加载。Apache 看模块和.htaccess的AllowOverrideNginx 看nginx -T输出里虚拟主机配置是否包含了 rewrite 指令。第四步确认服务是否重置。Apache 改配置要重启Nginx 至少reload很多人改了配置不重置白改半天。4.2 开启多应用后仍然访问不到目标应用有的同学安装完think-multi-app、设好了auto_multi_app true结果访问http://localhost/admin/index/index仍然返回默认应用内容或者直接报控制器不存在。这种情况八成是应用目录结构建错了。检查一下app/admin/controller目录下的控制器文件是否是合法命名并且类名与文件名一致。TP6 的控制器类文件名默认首字母大写比如Index.php、User.php类名也必须是Index、User命名空间要补齐。如果你直接复制了单应用模式下的控制器文件到多应用目录忘记改命名空间就会报错。另外注意清一下缓存。TP6 有路由缓存和应用缓存开发时每一次改动配置最好执行一次php think clear我碰到过排错半天最后发现是缓存没清的情况当时觉得特别冤。开发阶段把缓存关掉更省心直接在config/app.php里设置trace true每次请求结束后都能在下方的调试信息里看到当前的解析结果、SQL 语句、路由匹配情况对定位问题帮助极大。4.3 URL 参数丢失与路由规则冲突多应用模式下控制器方法的参数绑定依赖 URL 分段比如http://localhost/index/user/detail/id/8会提取id8。但如果你没有重写对了URL 里带着index.php参数解析也能出来可一旦 rewrite 写法有问题比如把^/(.*)$写成了^/(.)$某些场景下最后一段参数会被吞掉。还有一种常见冲突你在路由文件里手动定义了一条规则比如Route::get(hello/:name, index/Index/hello)同时又在 URL 里传了额外参数?page2结果发现page参数没拿到。这通常不是 rewrite 的问题而是路由规则定义时没有开启参数合并。建议在路由规则后面加上[ext ]或者使用Route::get(hello/:name, index/Index/hello)-allowCrossDomain();时看清楚方法签名确认参数传递方式符合预期。遇到参数丢失最好的调试手段是打开trace查看最终生成的完整 URL 和框架接收到的请求参数而不是猜。4.4 一套稳妥的排错顺序我总结一下自己平时处理 TP6 安装配置问题时的顺序大家可以直接当成清单用第一确认浏览器地址栏里的 URL 形式。如果 URL 里有index.php先验证带入口文件的地址能不能正常访问。第二确认服务端 rewrite 规则加载情况。Apache 查模块和 AllowOverrideNginx 查语法和根目录指向。第三确认 PHP 扩展完整。打开调试模式看框架报错提示。第四确认多应用开关与目录结构。auto_multi_app是否开启应用目录是否创建命名空间是否正确。第五清缓存重新访问。第六如果仍然 404打开 Nginx 或 Apache 的错误日志日志里的信息往往比页面提示更能说明问题。这套顺序看起来简单但我见过很多开发者遇到问题时先改路由、再改配置绕了一圈回不到基本盘。先把基础链路验证通了往往问题就迎刃而解。写在最后的经验我在实际项目里被 URL 重写和多应用配置折腾过好几轮以后养成了一个比较稳妥的习惯项目初始化阶段就把 URL 重写规则、多应用目录结构、域名绑定方案一次性定好并且在代码仓库里专门放一份部署说明文档。这样不管换了几台开发机、换了几个人接手只要照着文档把前置配置做完项目就能直接跑起来不用每次靠记忆重建一遍。另外个人强烈建议在开发阶段打开 Trace 调试很多配置问题在调试信息面前都会变得透明。还有一个我刚入行时没太在意的小技巧无论 Apache 还是 Nginx每次调整完配置务必确认服务真正重启成功很多看似诡异的 404 其实就是新配置还没生效白白浪费了半小时。前置配置这东西最怕的就是东改一下西改一下最后自己都不知道改了什么。把步骤捋清楚一次配齐后面所有项目都能少踩一半的坑。
阅读完成 · 觉得有帮助?