☰
ThinkPHP6.0.2环境搭建:PHPStudy+Composer避坑
2026/10/3 9:15:47 网站建设 项目流程

1. 先聊两句:这套组合到底卡人卡在哪

我最近给一个老项目补环境,重新走了一遍 ThinkPHP 6.0.2 + PHPStudy + Composer 这套流程。说实话,网上相关的教程一抓一大把,但真正能一口气跑通的并不多。问题不是步骤难,而是教程之间互相矛盾:有的让你用集成环境自带的 PHP 版本,有的让你先改 PHPStudy 的端口,还有的直接甩一个几十 MB 的项目压缩包让你解压到根目录,结果访问一片空白,谁都不知道是哪一步出了错。

我写这篇不是想凑热闹,而是把这次实际搭建的完整过程,连同两年里在这套环境上反复踩过的坑一起整理出来。目标很明确:让一个从没配过 ThinkPHP 6 的人,照着操作能在半小时内看到默认欢迎页,并且知道每一个关键步骤背后“为什么一定要这样做”。适合三类人看:刚学 ThinkPHP 的新手、要接手旧项目跑本地环境的开发者,以及想用 PHPStudy 快速验证 TP6 功能模块的技术爱好者。

先说结论:这套环境真的不难,但有一个绕不开的命门——Composer 拉取依赖的速度和稳定性。只要把 Composer 的源和 PHP 版本选对,后面基本就是一路下一步。

2. 环境准备:PHPStudy 与 PHP 版本选择的讲究

2.1 别下错 PHPStudy 版本

PHPStudy 现在官方叫“小皮面板”,官网直接搜就能找到。它有 Windows 版和 Linux 版,Windows 下是一个桌面控制面板,Linux 下是网页面板,两者界面差异很大,教程不能互相套用。

我平时本地开发用 Windows 版,服务器上用 Linux 版。如果你是跟着这篇走本地开发,下载 Windows 版即可,版本号其实不用太追新,v8.x 或者最新的 8.1 都行,核心功能差别不大。

安装时有一个不少人忽略的点:安装路径不要带中文和空格。我见过有人装在D:\软件\phpstudy,后面 Composer 和 PHP 扩展加载各种报错,改回D:\phpstudy_pro才消停。这不是玄学,是 PHP 的某些扩展和工具链对路径里的特殊字符敏感,用纯英文路径省心很多。

另外安装完面板后,第一次启动会提示选择 Apache 还是 Nginx。这里不用太纠结,先选哪个都能跑,后面伪静态配置我会分别给写法。我自己倾向于 Nginx,占内存小、并发表现好,而且 ThinkPHP 官方生产环境文档也以 Nginx 为主。

2.2 ThinkPHP 6.0.2 对 PHP 版本的硬性要求

ThinkPHP 6.0 的官方写的是 PHP >= 7.2.5,也就是说 PHP 7.2.5 以上都能跑。但版本能跑和跑得舒服是两码事。

我在 PHPStudy 里装了 7.2、7.4、8.0 三个版本,实测下来 ThinkPHP 6.0.2 配 PHP 7.4 是最稳的。7.2 也能跑,但有些第三方包已经开始放弃对 7.2 的兼容,装依赖时容易碰到坑。8.x 在 TP6.0.2 早期版本上偶尔会有“函数未定义”这类兼容问题,因为 PHP 8 删掉了一批旧函数。虽然 6.0.2 有兼容处理,但没必要拿老框架去挑战新运行时。

给你们一个选版本的建议表:

PHP 版本ThinkPHP 6.0.2 兼容性我的推荐度备注
7.2可运行,依赖兼容性下降不推荐第三方包很多已放弃支持
7.4稳定运行强烈推荐目前 TP6 本地开发最佳选择
8.0大部分功能正常可以但没必要老项目改造时容易出幺蛾子
8.1+风险较高不推荐建议直接用 ThinkPHP 8

2.3 PHP 版本怎么在面板里切换和确认

PHPStudy 装完默认可能给的是 PHP 8 或者更老的 5.x,需要在面板的“软件管理 - PHP”里看一下已经装了哪些版本。如果没有 7.4,点“安装”按需装一个,这个过程会下载安装包,稍微等一下就行。

确认版本有个很简单的办法:在 PHPStudy 的“设置 - 配置文件 - PHP”,能看到当前站点用的 PHP 版本;也可以在项目根目录写一个临时 PHP 文件,放一句<?php phpinfo();,访问后看第一行显示的版本对不对。

注意一点:你切换了 PHPStudy 里的 PHP 版本,并不代表命令行里的 PHP 也跟着变了。Windows 环境下,PHPStudy 的 PHP 是装在D:\phpstudy_pro\Extensions\php\7.4.3nts这种路径下的,跟系统 PATH 里的 PHP 是两回事。这一点正好是下一节 Composer 的问题源头。

2.4 开发环境里必须检查的三个开关

进 PHPStudy 面板之后,不需要改太多东西,但有三处我要建议你提前确认:

  1. MySQL 的端口如果不是 3306,ThinkPHP 的.env文件里数据库配置要跟着改,不然连不上库。
  2. Nginx/Apache 的进程没有占用,特别是 80 端口被 IIS 或者其它进程抢走时,站点起不来。
  3. PHP 扩展里fileinfo、curl、openssl这些必须打开。ThinkPHP 6 的依赖里有的会用到它们,缺了某些扩展,Composer 安装时候直接报错,弹出来的提示还不直观。

打开扩展的位置在面板的“PHP 扩展”栏,勾选后点“应用更改”,PHPStudy 会自动帮你改php.ini并重启服务,不用手动去翻配置文件,这点比我们自己改配置省力很多。

3. Composer 是第一个大坎:安装、换源与版本指定

3.1 Windows 下 Composer 的两种安装方式

Composer 在 Windows 下推荐有两种方式:

  • 直接下载Composer-Setup.exe安装包,一路下一步。它会帮你把composer.bat加入系统 PATH,以后在命令行直接敲composer就能用。
  • 下载composer.phar文件,放在项目目录或者一个固定目录,用php composer.phar的方式调用。好处是不污染系统,坏处是每次命令都要带php前缀。

我推荐第一种,省事。但安装过程中有一个关键选择:Composer 安装程序会让你指定 PHP 的路径。这时候如果你机器上装过多个 PHP,务必确保选的是 PHPStudy 里同一版本的php.exe。我见过有人系统里装了别家的 PHP(比如小皮之外的),Composer 用的是那个 PHP,依赖装好后跑起来却是另一个版本,最后项目直接白屏。

最简单的做法:在已安装的其他环境菜单里勾选“排除安装”,让 Composer 只认识一个 PHP。或者在 PHPStudy 的面板里找到当前站点用的 PHP 可执行文件,Composer 安装时手动指定到那个路径。

3.2 不换源,创建项目能等到你怀疑人生

Composer 默认的依赖源是 Packagist 的国外服务器,在国内网络环境下拉取速度非常不稳定。我最初用默认源创建一个 ThinkPHP 6.0.2 项目,卡在updating dependencies阶段十几分钟不动,最后直接超时失败。换成国内镜像后,同样的操作两三分钟完成。

切换全局镜像源的方法,命令行执行:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

这条命令是把全局的 Composer 镜像源改成阿里云镜像。改动之后,以后所有composer create-project和composer install都会走这个源。

想验证是否成功,执行:

composer config -g -l

可以看到repositories.packagist的值是我们刚设置的地址。

如果换了源仍慢,还可以临时指定源:

composer create-project topthink/think=6.0.2 tp6 --prefer-dist --no-interaction

当然阿里云镜像偶尔有缓存延迟,遇到某个包版本拉取不到的时候,可以临时切到腾讯源比较一下:

composer config -g repo.packagist composer https://mirrors.cloud.tencent.com/composer/

注意镜像源只影响 Composer 下载,不影响项目代码的运行,切换后重新composer update也不会破坏已有项目结构。

3.3 多 PHP 版本共存时 Composer 用错版本怎么办

这是我认为整套流程里最容易踩但又最没人提的一个坑。

PHPStudy 装了多个 PHP 版本时,Composer 默认使用PATH里那个 PHP。如果你在命令行里敲php -v显示的是 PHP 8,那 Composer 会用 PHP 8 去执行依赖解析。很多依赖在 PHP 8 下安装会出现版本兼容警告,甚至直接拒绝安装。

我当时碰到的情况是:项目要跑在 PHP 7.4,但 Composer 用的是 PHP 8,结果composer create-project时提示某些插件不被当前 PHP 支持。排查后发现是 PATH 顺序问题。

解决办法有两个:

方法一,临时指定 PHP 版本来执行 Composer:

D:\phpstudy_pro\Extensions\php\7.4.3nts\php.exe D:\composer\composer.phar create-project topthink/think=6.0.2 tp6

方法二,直接把 7.4 的目录提到 PATH 最前面。右键“此电脑 - 属性 - 高级系统设置 - 环境变量”,在Path里把 PHP 7.4 的目录移到最上边。

我个人建议用方法一,因为改动最小、不容易影响系统里其它工具。

3.4 常见 Composer 报错现场还原

我把这两年收集到的 Composer 高频报错整理成一个表,方便你对着排查:

报错信息实际情况解决办法
Composer is not initializedComposer.lock 缺失或者composer install没找到锁文件首次用composer update生成 lock 后再 install
Could not resolve host网络访问不了默认源切国内镜像源重试
Your requirements could not be resolved依赖版本冲突用composer create-project topthink/think=6.0.2指定版本,避免乱拉最新
The openssl extension is requiredphp.ini 里没开 opensslPHPStudy 面板勾选 openssl 扩展并重启
App directory already exists目标目录已存在同名文件换一个目录名,或删掉目标目录后重试

最后一行的App directory already exists是我看到新手问得最多的。这问题特别简单:你创建项目的目录不是空的,Composer 不敢覆盖。换一个新目录名就行,比如tp6,别直接建think再往里塞。

4. 创建 ThinkPHP 6.0.2 项目:命令、目录结构与首次访问

4.1 创建命令与版本锁定

准备工作都做完后,创建 TP6.0.2 项目的完整流程如下:

cd D:\www composer create-project topthink/think=6.0.2 tp6

分解一下这条命令:

  • create-project是 Composer 的创建项目指令,等同于先git clone再composer install。
  • topthink/think是 ThinkPHP 框架的主包名。
  • =6.0.2是版本约束,锁定在 6.0.2 版本。
  • tp6是要创建的目标目录名。

如果你不写=6.0.2,默认会拉取该包可用的最新 6.x 版本。作者标题里既然提到 6.0.2,说明项目需求是明确的,那就老老实实锁版本。

命令执行过程中会下载 ThinkPHP 核心和一组依赖包。下载完成后,你会在tp6目录下看到类似下面的结构:

tp6/ ├── app/ 应用目录 │ ├── controller/ 控制器目录 │ ├── model/ 模型目录 │ └── ... ├── config/ 配置目录 ├── route/ 路由定义目录 ├── runtime/ 运行时生成目录(缓存、日志) ├── vendor/ Composer 依赖包目录 ├── view/ 视图目录(部分版本可能没有,需自行创建) ├── public/ Web 根目录 │ └── index.php 入口文件 ├── .env 环境配置文件(默认没有,需复制 .env.example 或自己建) ├── composer.json 依赖声明文件 └── think 命令行入口(Linux/macOS)

4.2 不能用根目录当站点根目录:public 的定位

刚接触 ThinkPHP 6 的人容易有的一个错误认知:以为项目根目录就是网站根目录。其实不是。

ThinkPHP 6 强制要求 Web 服务器把站点根目录指到public目录,原因有两条:

一是入口安全。TP6 的入口文件只有public/index.php。如果站点根目录是项目根目录,访问者直接访问http://你的域名/app/controller/User.php就能看到源码,安全性等于裸奔。而指到public后,根目录以上的文件都无法直接通过 URL 访问。

二是路由纯净。URL 重写时会把所有访问请求交给index.php处理,如果把根目录暴露出去,静态资源和入口会混在一起,伪静态规则容易冲突。

理解这一点,后面配置 PHPStudy 就不会迷糊。

4.3 首次访问:欢迎页出现才算第一步完成

先在项目里建立一个访问入口。如果创建后public下已经有.example这类示例文件,直接删掉就行,不影响。然后在命令行执行:

cd D:\www\tp6 php think run

这条命令会调用 PHPStudy 里配置好的 PHP 内置服务器,默认监听127.0.0.1:8000。浏览器访问http://127.0.0.1:8000,正常情况下能看到 ThinkPHP 的默认欢迎页。

看到欢迎页,说明 Composer 拉取的框架代码没问题、PHP 扩展没问题、目录权限没问题。后面要做的就是用 PHPStudy 把域名和站点串起来。

另一种更快的方式是在 PHPStudy 面板里直接创建一个站点,把域名指向项目目录。但这就要先解决“运行目录指向 public”的事,所以我把这一个主题拆成了独立的一节来说。

5. 必踩的坑:PHPStudy 里运行目录不指向 public 会怎样

5.1 小皮面板里创建站点并指定运行目录

打开 PHPStudy 面板,在左侧菜单点“网站”,再点“创建网站”。填写以下几项:

  • 域名:比如tp6.test
  • 端口:默认 80,如果被占用可以改成 8000、8080 这类
  • 网站目录:选到你的项目根目录,比如D:\www\tp6
  • 运行目录:这里最关键,要手动选择为\public
  • PHP 版本:选择 PHP 7.4(对应你项目需要的版本)

截图省略,因为不同版本界面稍有差异,但关键点都一样:运行目录必须选public。

如果创建网站时没有设置运行目录,默认指向网站根目录。此时访问站点,可能出现的情况有两种:一是直接显示目录结构列表,把app、config都列出来,非常不安全;二是访问http://tp6.test/index.php能出页面,但访问http://tp6.test/是 403 或者 404。两种都说明目录指向错了。

5.2 本地域名解析:hosts 文件别忽略

PHPStudy 创建的站点域名tp6.test,本地浏览器访问时实际上查的是系统 DNS。如果系统配置的 DNS 服务器不认识这个域名,会解析失败。

开发环境的常规做法是在 hosts 文件里手动加一行解析记录。hosts 文件在C:\Windows\System32\drivers\etc\hosts,以管理员身份打开记事本,在最下面加:

127.0.0.1 tp6.test

如果 PHPStudy 创建站点时勾选了“创建 hosts 解析”,它会自动帮你写进去;没勾照做,别忘了这一步。

5.3 设置之后仍然打不开的三个检查点

有次我帮一个同事排查,他运行目录也选了public,hosts 也配了,访问却还是 404。最后发现是下面三个点的问题:

  1. PHPStudy 是 Nginx 环境,伪静态规则没有加上。ThinkPHP 6 默认需要 URL 重写功能,Nginx 下如果没配置伪静态,访问http://tp6.test时index.php没被正确路由,自然打不开页面。关于这一点,下一节专门展开。

  2. 项目目录没有给足读写权限。TP6 运行时会在runtime目录写日志、缓存,如果 PHPStudy 进程对该目录没有写权限,会出现页面空白但错误日志里没有任何记录的情况。Windows 下大部分情况不会有这个权限问题,但 Linux 面板必须注意,经常是runtime目录权限为 755、所属用户不对导致的问题。

  3. 端口冲突。站点的端口如果是 80,而系统里 IIS 或者其它 Web 服务也占用着 80,PHPStudy 的站点就起不来。解决办法是把 PHPStudy 的 Nginx 或者 Apache 停掉,重新启动,或者在创建站点时换个端口。

6. 伪静态与 URL 重写:让 URL 里没有 index.php

6.1 为什么要处理伪静态

ThinkPHP 6 默认的路由格式是:

http://tp6.test/index.php/index/hello

如果你把index.php留在 URL 里,性能上没大影响,但两个问题:

  • URL 不美观,对外分享的时候很长;
  • 某些环境下的安全策略会把index.php当成漏洞特征,虽然多数是误报,但没必要自己给自己找麻烦。

处理掉index.php的办法叫“伪静态”,实质是在 Web 服务器层面把所有不存在的文件请求重写到入口文件index.php。

6.2 Apache 环境下的伪静态配置

Apache 下最简单的方式是利用 ThinkPHP 自带的.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>

把这段配置放到public/.htaccess里,并且确保 Apache 开启了mod_rewrite。PHPStudy 的 Apache 默认会开这个模块,一般不用手动改。

6.3 Nginx 环境下的伪静态配置

Nginx 环境没有.htaccess,需要在站点的 Nginx 配置里加上一段 location 规则。

在 PHPStudy 面板里,找到该站点对应的 Nginx 配置文件(“网站 - 操作 - 配置文件”),在server {}段里加:

location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; break; } }

保存后,在面板里重启 Nginx。这里有个小细节:Nginx 的if (!-e $request_filename)意思是请求的文件不存在时,才进入重写,避免把真实的静态资源如 CSS、JS、图片也转给index.php。

6.4 配置完成后的验证方式

配置完伪静态后,验证三步:

  1. 访问http://tp6.test,应该直接出现欢迎页或你写的控制器页面。
  2. 访问一个有控制器但不存在的路由,比如http://tp6.test/nonexist,应该返回 TP6 的 404 页面,而不是 Nginx 默认的 404。
  3. 在public目录放一个静态文件,比如public/robots.txt,能正常访问到文件内容,说明静态资源没有被重写规则误伤。

这三步走通,伪静态就基本到位了。

7. 冷门但真实:$_SERVER['REQUEST_URI'] 为空导致路由失效

这个坑我在热搜词里看到有人也搜了,很值得单说一节。因为它不是一个常见配置问题,而是环境层面的“幽灵问题”。

7.1 问题现象与排查链路

有次我搭好 TP6 项目后,访问首页正常,但一访问任何二级路由就白屏。打开调试模式看错误,显示路由解析失败,类不存在的提示。一开始我以为是路由定义写错,检查了半天没发现问题。后来在public/index.php里临时写:

var_dump($_SERVER['REQUEST_URI']); var_dump($_SERVER['REQUEST_METHOD']); die;

发现$_SERVER['REQUEST_URI']居然是空字符串。

ThinkPHP 6 的路由解析依赖$_SERVER['REQUEST_URI']或者$_SERVER['PATH_INFO']来识别当前请求的地址。如果REQUEST_URI为空,框架无法正确解析控制器的路由,所以首页通过默认路径能打开,但二级路径全部失效。

7.2 为什么会出现这个情况

实际上,REQUEST_URI是由 Web 服务器注入到 PHP 环境变量里的。正常情况下 Nginx 会通过fastcgi_param REQUEST_URI $request_uri;传给 PHP-FPM。如果它为空,说明fastcgi_params或php-fpm接收方配置里缺少这一行,或者被覆盖了。

常见的触发场景有几个:

  • 某些精简版的 Nginx 配置模板里漏了fastcgi_param REQUEST_URI,而 ThinkPHP 恰好依赖它;
  • Apache 环境下mod_rewrite规则和AcceptPathInfo的相互作用导致REQUEST_URI被改写为空;
  • 某些安全软件拦截了带有路径信息的请求,转发时把REQUEST_URI置空。

7.3 兜底解决思路与代码示例

第一个解决路径是检查 Web 服务器配置:Nginx 的fastcgi.conf或者站点配置里是否包含fastcgi_param REQUEST_URI $request_uri;。没有就补上,然后重载配置。

第二个解决路径是代码层面兜底,在public/index.php入口文件的最上方,也就是框架加载之前加上判断:

if (empty($_SERVER['REQUEST_URI'])) { if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) { $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; } elseif (isset($_SERVER['REQUEST_URI_FALLBACK'])) { $_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI_FALLBACK']; } else { $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME']; if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING']) { $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING']; } } }

这段代码的原理是手动把缺失的变量用其它服务器变量拼装回来,保证框架读取路由时拿到的地址是完整可用的。它不是万能的,但能解决大多数 Web 服务器没有注入导致的空值问题。

如果用了这个兜底后问题还在,那我建议你把错误模式开大,看 TP6 日志里的完整请求参数是什么,顺着日志里缺少的 $_SERVER 键名继续补。

8. 进阶避坑笔记:关联删除、二级域名与安全底线

8.1 模型关联删除的三种实现

ThinkPHP 6 的模型关联删除,是很多人在实际业务里绕不开的需求。比如删一篇文章,要把它的评论、点赞关联数据一起删掉。三种方式我都用过:

方式一:使用with()关联后控制层手动循环删除。缺点是 N+1 次查询,数据量小还行,数据量大了性能难看。

方式二:使用模型事件,在模型类里定义一个删除监听:

protected static function onBeforeDelete($model) { // 删除前先清理关联数据 $model->comments()->delete(); }

这种方式的优点是把逻辑内聚在模型里,不污染控制器代码,推荐业务层使用。

方式三:数据库外键级联。在数据表设计时给外键加上ON DELETE CASCADE,数据库层自己处理。这种方式性能最好,但外键约束在某些分表场景下比较麻烦,动手要谨慎。

选哪种,取决于你的业务复杂度。简单说:单条数据删除用事件,批量的用查询后再统一删,数据量大时优先考虑数据库设计。

8.2 二级域名设置的两个层面

“ThinkPHP 开启二级域名设置”这个关键词也很热。坦率地讲,二级域名设置有硬件层和业务层两个层面,经常被人搞混:

  • 域名解析 + 服务器配置层面:需要把admin.tp6.test解析到你的服务器,并在 Nginx 或 Apache 里新增一个站点或者 server 块。
  • 框架路由层面:如果两个域名指向的是同一个 TP6 项目,可以在route/route.php里用Route::domain()做域名分组:
Route::domain('admin.tp6.test', function () { Route::get('index', 'admin.Index/index'); });

如果只是本地开发想模拟二级域名,PHPStudy 创建站点时直接填admin.tp6.test,并在 hosts 里加一行解析,效果一样,不用专门上外网服务器操作。

8.3 关于 thinkphp 漏洞的三个安全提醒

最后想多说一句安全相关的。搜“thinkphp 漏洞”的人不少,很多是运维或开发在自查。有一些基础操作可以大幅降低框架版本漏洞的风险:

  1. 本地开发可以用 6.0.2 这个版本,但上生产环境前一定要composer update topthink/framework到 6.0.x 的最新版本。旧版本没有后续安全补丁,等于把一个已公开的问题留在线上。

  2. 关闭调试模式。TP6 的.env文件里默认是APP_DEBUG = true,生产环境务必改成false,并且开启APP_TRACE前先想想日志里会不会带出敏感信息。

  3. 不要把 MySQL 的 root 密码写在项目里,尽量用最小权限账号,.env 文件不要让 Web 服务器目录直接可读。PHPStudy 运行目录指到public后,.env在项目根目录无法被 URL 访问,但文件系统层面该设的权限还是要设。

我个人的习惯是:本地随便折腾,生产环境打死不偷懒,依赖锁版本、配置最小化、日志定期清理。任何框架版本都有生命周期,ThinkPHP 6.0 也不例外,别让一个“能跑”变成“不敢动”。

这套环境真正用顺之后,你会发现后面写业务逻辑的速度远大于配环境的效率。把这次记录里的每一个坑都避开,你剩下的时间就可以花在更有意思的功能实现上了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询