ThinkPHP 8.0 是国内最受欢迎的 PHP 框架之一,它以「轻量、约定优于配置」著称,
8.0 版本在保持简洁风格的同时,全面拥抱了 PHP 8.0+ 的强类型、注解和纤程等新特性。
本文将带你从零开始,搭建一个能跑、能测、能部署的 ThinkPHP8 项目。
环境要求
在开始之前,请确保本机或服务器满足以下最低配置:
- PHP ≥ 8.0(推荐
8.2,可获得完整的 readonly / true / false 类型支持) - Composer ≥ 2.4
- MySQL 5.7+ / PostgreSQL 13+ / SQLite 3
- Node.js ≥ 18(仅在使用前端构建链时需要)
- Docker ≥ 24(部署阶段使用)
PHP 扩展清单
ThinkPHP 8 默认依赖以下扩展,开启它们即可:
# Debian / Ubuntu
sudo apt-get install -y php8.2 \
php8.2-cli php8.2-fpm php8.2-mysql php8.2-redis \
php8.2-mbstring php8.2-xml php8.2-curl php8.2-zip \
php8.2-bcmath php8.2-sockets php8.2-opcache
# 验证扩展
php -m | grep -E "pdo|mbstring|openssl|tokenizer|xml|ctype|json|bcmath|fileinfo|curl|sockets"
创建项目
使用 Composer 创建 ThinkPHP8 项目只需要一行命令:
composer create-project topthink/think blog-app
cd blog-app
# 启动内置服务器验证
php think run
# 默认监听 http://127.0.0.1:8000
启动成功后,浏览器打开 http://127.0.0.1:8000,如果能看到「ThinkPHP 迎你」字样,
那么环境就完全 OK 了。
目录规范
ThinkPHP8 采用「多应用 + 单入口」结构,下面是我个人比较推荐的组织方式:
| 目录 | 作用 | 是否提交 |
|---|---|---|
app/ | 应用代码(控制器、模型、中间件) | ✓ |
config/ | 配置(数据库、缓存、路由等) | ✓ |
route/ | 路由定义 | ✓ |
public/ | Web 入口、静态资源 | ✓ |
runtime/ | 运行时缓存 / 日志 | ✗ |
vendor/ | Composer 依赖 | ✗ |
.env | 本地环境变量 | ✗ |
runtime/、vendor/、.env、*.log,否则仓库会越来越臃肿,也会泄露密钥。
路由与中间件
TP8 推荐用「路由注解」声明接口,开发体验非常接近 Spring Boot:
namespace app\controller;
use app\model\Post as PostModel;
use think\annotation\Route;
use think\response\Json;
class Post
{
#[Route('GET', 'posts', 'index')]
public function index(): Json
{
$list = PostModel::with(['author', 'tags'])
->order('id', 'desc')
->paginate(10);
return json([
'code' => 0,
'data' => $list,
]);
}
#[Route('POST', 'posts', 'save')]
public function save(): Json
{
$data = input();
$post = PostModel::create($data);
return json(['code' => 0, 'data' => $post]);
}
}
用中间件统一处理 JWT 鉴权
把所有「控制器之前要做的事」放进中间件,可以让控制器保持纯粹:
namespace app\middleware;
use think\facade\Jwt;
use think\response\Json;
class Auth
{
public function handle($request, \Closure $next)
{
$token = $request->header('Authorization');
if (!$token || !$uid = Jwt::verify($token)) {
return json(['code' => 401, 'msg' => '未登录'])->code(401);
}
$request->uid = $uid;
return $next($request);
}
}
在 route/app.php 中按需挂载:
use app\middleware\Auth;
Route::group('api', function () {
Route::get('posts', 'Post/index');
Route::post('posts', 'Post/save')->middleware(Auth::class);
})->middleware(\app\middleware\Cors::class);
数据库与 ORM
TP8 的 ORM(基于 think-orm)使用起来非常顺手,下面展示一个「用户 → 文章 → 评论」的三表关系:
namespace app\model;
use think\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class, 'user_id');
}
public function profile()
{
return $this->hasOne(UserProfile::class, 'user_id');
}
}
预加载(
with())是 N+1 查询的特效药。一次 JOIN 拉完所有数据,比循环里再查一遍快 10~100 倍。
Docker 容器化部署
本地跑通只是第一步。生产环境我们需要把 PHP、Nginx、MySQL、Redis 一起编排起来。
下面是一份可直接运行的 docker-compose.yml:
version: "3.9"
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
volumes:
- ./:/var/www/html
depends_on:
mysql: { condition: service_healthy }
redis: { condition: service_healthy }
networks: [app-net]
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./public:/var/www/html/public:ro
depends_on: [app]
networks: [app-net]
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpwd
MYSQL_DATABASE: blog
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
networks: [app-net]
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
networks: [app-net]
volumes:
mysql_data:
networks:
app-net:
driver: bridge
多阶段 Dockerfile
用一个 Dockerfile 跑 Composer 装依赖,再把 vendor 拷到运行时镜像,最终镜像只有 ~120MB:
# 阶段一:安装依赖
FROM composer:2.7 AS vendor
WORKDIR /app
COPY composer.* ./
RUN composer install --no-dev --no-scripts --prefer-dist
COPY . .
RUN composer dump-autoload --optimize
# 阶段二:运行时
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql bcmath opcache
WORKDIR /var/www/html
COPY --from=vendor /app /var/www/html
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
Nginx 配置
把请求全部转发到 app:9000,静态资源由 Nginx 直接返回:
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
一键启动
所有文件就绪后,在项目根目录执行:
# 启动全部服务(后台)
docker compose up -d --build
# 查看运行状态
docker compose ps
# 进入容器执行命令
docker compose exec app php think migrate:run
# 实时查看日志
docker compose logs -f app
docker-compose.yml 接入 GitHub Actions,推送到自建 Harbor 镜像仓库,再用 Watchtower 自动滚动更新,整条 CI/CD 就闭环了。
小结
本篇用一个完整的示例,串起了 ThinkPHP8 项目从环境准备、目录规划、路由 ORM,到容器化部署的全过程。 核心思路其实就是三件事:
- 用
composer把依赖管好,用.env把配置隔离 - 用 路由注解 + 中间件 让控制器保持纯粹
- 用 多阶段构建 + Compose 把运行环境固化下来
希望这份笔记能帮你少踩几个坑。如果你有更好的实践,欢迎在评论区或邮件与我交流 🚀