用 AI 工具开发 PHP 应用实战:Cursor + Copilot 全程提效指南

AI 编程工具正在改变 PHP 后端开发的工作流。无论是用 Cursor 生成完整的 RESTful API,还是让 Copilot 自动补全数据库查询,AI 都能显著压缩重复劳动。本文以构建一个带 AI 能力的 PHP 内容管理接口为例,演示 AI 辅助开发的完整流程。
1. 用 AI 快速搭建项目脚手架
在 Cursor 中描述需求:"创建一个 PHP 8.2 的 RESTful API 项目,使用 Slim Framework 4,集成 Eloquent ORM,支持 JWT 鉴权,目录结构遵循 MVC 分层。"
AI 生成 composer.json 和基础目录结构:
{
"require": {
"php": "^8.2",
"slim/slim": "^4.12",
"slim/psr7": "^1.6",
"illuminate/database": "^10.0",
"firebase/php-jwt": "^6.10",
"vlucas/phpdotenv": "^5.6",
"monolog/monolog": "^3.5"
},
"require-dev": {
"phpunit/phpunit": "^10.5"
},
"autoload": {
"psr-4": { "App\\\\": "src/" }
}
}2. AI 生成数据模型与数据库迁移
告诉 AI:"帮我创建文章(posts)和用户(users)的 Eloquent 模型,以及对应的数据库迁移文件。" 生成结果:
<?php
// src/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = 'posts';
protected $fillable = ['title', 'content', 'summary', 'author_id', 'status'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function author()
{
return $this->belongsTo(User::class, 'author_id');
}
// AI 生成:自动生成摘要
public function generateSummary(): string
{
return mb_substr(strip_tags($this->content), 0, 200) . '...';
}
}
// database/migrations/create_posts_table.php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 255);
$table->longText('content');
$table->string('summary', 500)->nullable();
$table->foreignId('author_id')->constrained('users')->onDelete('cascade');
$table->enum('status', ['draft', 'published', 'archived'])->default('draft');
$table->timestamps();
$table->index(['author_id', 'status']);
});3. AI 生成带 JWT 鉴权的控制器
提示词:"写一个 PostController,实现 CRUD,POST/PUT/DELETE 需要 JWT 验证,GET 公开访问,统一 JSON 响应格式,包含输入验证。" AI 生成:
<?php
// src/Controllers/PostController.php
namespace App\Controllers;
use App\Models\Post;
use App\Services\AuthService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class PostController
{
public function __construct(private AuthService $auth) {}
// 获取文章列表(公开)
public function index(Request $request, Response $response): Response
{
$params = $request->getQueryParams();
$page = (int)($params['page'] ?? 1);
$limit = min((int)($params['limit'] ?? 10), 50);
$posts = Post::with('author:id,name')
->where('status', 'published')
->orderBy('created_at', 'desc')
->paginate($limit, ['*'], 'page', $page);
return $this->json($response, [
'code' => 200,
'data' => $posts->items(),
'meta' => [
'total' => $posts->total(),
'page' => $page,
'per_page' => $limit,
]
]);
}
// 创建文章(需要 JWT)
public function store(Request $request, Response $response): Response
{
$user = $this->auth->requireAuth($request); // 抛出 401 如未认证
$body = $request->getParsedBody();
// 输入验证
$errors = $this->validate($body, [
'title' => 'required|max:255',
'content' => 'required',
]);
if ($errors) {
return $this->json($response, ['code' => 422, 'errors' => $errors], 422);
}
$post = Post::create([
'title' => htmlspecialchars($body['title'], ENT_QUOTES, 'UTF-8'),
'content' => $body['content'],
'summary' => $body['summary'] ?? null,
'author_id' => $user->id,
'status' => $body['status'] ?? 'draft',
]);
return $this->json($response, ['code' => 201, 'data' => $post], 201);
}
// 更新文章(需要 JWT + 作者权限)
public function update(Request $request, Response $response, array $args): Response
{
$user = $this->auth->requireAuth($request);
$post = Post::findOrFail($args['id']);
if ($post->author_id !== $user->id) {
return $this->json($response, ['code' => 403, 'message' => 'Forbidden'], 403);
}
$post->update($request->getParsedBody());
return $this->json($response, ['code' => 200, 'data' => $post->fresh()]);
}
private function json(Response $response, array $data, int $status = 200): Response
{
$response->getBody()->write(json_encode($data, JSON_UNESCAPED_UNICODE));
return $response->withHeader('Content-Type', 'application/json')->withStatus($status);
}
private function validate(array $data, array $rules): array
{
$errors = [];
foreach ($rules as $field => $rule) {
foreach (explode('|', $rule) as $r) {
if ($r === 'required' && empty($data[$field])) {
$errors[$field][] = "{$field} 不能为空";
}
if (str_starts_with($r, 'max:')) {
$max = (int)substr($r, 4);
if (mb_strlen($data[$field] ?? '') > $max) {
$errors[$field][] = "{$field} 不能超过 {$max} 个字符";
}
}
}
}
return $errors;
}
}4. AI 集成 OpenAI:给内容自动生成摘要
提示词:"写一个 AIService,调用 OpenAI API 给文章自动生成摘要,限制在 150 字以内,支持异常降级。"
<?php
// src/Services/AIService.php
namespace App\Services;
class AIService
{
private string $apiKey;
private string $baseUrl = 'https://api.openai.com/v1/chat/completions';
public function __construct()
{
$this->apiKey = $_ENV['OPENAI_API_KEY'] ?? throw new \RuntimeException('Missing OPENAI_API_KEY');
}
public function generateSummary(string $content, int $maxChars = 150): string
{
$prompt = "请用不超过{$maxChars}字为以下文章生成摘要,只输出摘要内容:\n\n"
. mb_substr(strip_tags($content), 0, 2000);
try {
$payload = json_encode([
'model' => 'gpt-4o-mini',
'messages' => [['role' => 'user', 'content' => $prompt]],
'max_tokens' => 300,
'temperature' => 0.3,
]);
$ch = curl_init($this->baseUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"Authorization: Bearer {$this->apiKey}",
],
]);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new \RuntimeException("OpenAI API 返回 {$httpCode}");
}
$data = json_decode($result, true);
return $data['choices'][0]['message']['content'] ?? '';
} catch (\Throwable $e) {
// 降级:截取原文前 N 字作为摘要
error_log("AI 摘要生成失败: " . $e->getMessage());
return mb_substr(strip_tags($content), 0, $maxChars) . '...';
}
}
}5. AI 生成单元测试
让 AI "为 PostController::store 写 PHPUnit 测试,覆盖:正常创建、缺少 title 的 422 错误、未登录的 401 错误。"
<?php
// tests/PostControllerTest.php
class PostControllerTest extends TestCase
{
private App $app;
protected function setUp(): void
{
$this->app = AppFactory::create();
// 使用内存 SQLite,隔离测试数据
config()->set('database.default', 'sqlite');
config()->set('database.connections.sqlite.database', ':memory:');
$this->runMigrations();
}
public function test_create_post_success(): void
{
$token = $this->getValidJwtToken(userId: 1);
$request = $this->createRequest('POST', '/api/posts')
->withHeader('Authorization', "Bearer {$token}")
->withParsedBody(['title' => '测试文章', 'content' => '内容正文...']);
$response = $this->app->handle($request);
$this->assertEquals(201, $response->getStatusCode());
$body = json_decode((string)$response->getBody(), true);
$this->assertEquals('测试文章', $body['data']['title']);
}
public function test_create_post_missing_title_returns_422(): void
{
$token = $this->getValidJwtToken(userId: 1);
$request = $this->createRequest('POST', '/api/posts')
->withHeader('Authorization', "Bearer {$token}")
->withParsedBody(['content' => '内容正文']);
$response = $this->app->handle($request);
$this->assertEquals(422, $response->getStatusCode());
}
public function test_create_post_without_auth_returns_401(): void
{
$request = $this->createRequest('POST', '/api/posts')
->withParsedBody(['title' => '标题', 'content' => '内容']);
$response = $this->app->handle($request);
$this->assertEquals(401, $response->getStatusCode());
}
}6. AI 辅助开发经验总结
生成路由配置:告诉 AI 你的接口列表,让它一次性生成 Slim 路由文件,比手写快 5 倍
SQL 优化:把慢查询日志贴给 AI,它能分析瓶颈并建议加索引或改写 ORM 查询
代码审查:让 AI 检查 SQL 注入、XSS、SSRF 等常见安全漏洞,作为人工审查的补充
文档生成:把 Controller 代码贴给 AI,自动生成 OpenAPI 3.0 格式的接口文档
重构建议:对于老项目,让 AI 分析代码结构,给出从面向过程到 OOP 的重构路径
AI 工具最大的价值在于消灭"搬砖"代码——那些结构固定、逻辑简单但量大的代码(CRUD、验证、序列化)。把这些交给 AI,工程师可以把精力集中在系统设计、性能优化和业务建模上,这才是真正的效率革命。
发布评论
热门评论区: