Cursor + PHP + Yii2 Backend Skill 完整的 Cursor Agent 配置
这套配置的目标是让 Cursor 不只是“会写 Yii2”,而是按后端工程师的方式分析、修改、测试和 Review Yii2 项目。
Cursor + PHP + Yii2 Backend Skill
目录结构
.cursor/
├── rules/
│ ├── php.mdc
│ ├── yii2.mdc
│ ├── mysql.mdc
│ ├── redis.mdc
│ ├── api.mdc
│ ├── security.mdc
│ └── testing.mdc
│
└── skills/
└── yii2-backend/
├── SKILL.md
└── references/
├── architecture.md
├── database.md
└── review.md1. .cursor/rules/php.mdc
---
description: PHP backend development standards
globs:
- "**/*.php"
alwaysApply: true
---
# PHP Development Rules
## PHP Version
Before writing new PHP code:
1. Inspect composer.json.
2. Determine the project's PHP version.
3. Follow the project's existing PHP syntax and conventions.
4. Do not introduce syntax unsupported by the project's PHP version.
## Type Safety
Prefer:
- strict parameter types
- return types
- nullable types
- typed properties
- enums when supported by the project
Avoid unnecessary mixed values.
Example:
GOOD:
```php
public function getUser(int $id): ?User
{
return User::findOne($id);
}Avoid:
public function getUser($id)
{
return User::findOne($id);
}unless the existing project intentionally follows weak typing.
Error Handling
Do not silently swallow exceptions.
Bad:
try {
// ...
} catch (\Throwable $e) {
}If an exception must be caught:
- log it
- add meaningful context
- either recover or rethrow
Null Handling
Do not assume database queries always return records.
Bad:
$user = User::findOne($id);
return $user->name;Prefer:
$user = User::findOne($id);
if ($user === null) {
throw new NotFoundHttpException('User not found.');
}Code Duplication
Before adding a new helper:
- Search the project.
- Check whether the functionality already exists.
- Reuse existing abstractions when appropriate.
Do not create unnecessary utility classes.
Backward Compatibility
When modifying existing code:
- preserve existing public method signatures when possible
- preserve API response formats
- preserve database behavior
- avoid unnecessary refactoring
- avoid changing unrelated files
Dependency Policy
Do not add Composer packages automatically.
Before adding a dependency:
- Check whether the project already has equivalent functionality.
- Check composer.json.
- Explain why the dependency is necessary.
- Prefer Yii2/PHP standard functionality when sufficient.
---
# 2. `.cursor/rules/yii2.mdc`
```md
---
description: Yii2 framework architecture and development rules
globs:
- "**/*.php"
alwaysApply: true
---
# Yii2 Development Rules
## Architecture
Prefer:
Controller
↓
Service
↓
Model / ActiveRecord
↓
Database
Controllers should remain thin.
Controllers are responsible for:
- receiving HTTP parameters
- authentication
- authorization
- request validation
- calling services
- formatting responses
Controllers should NOT contain complex business logic.
## Controller
Avoid:
```php
public function actionCreate()
{
$model = new Order();
if ($model->load(Yii::$app->request->post())) {
// hundreds of lines of business logic
}
}Prefer:
public function actionCreate()
{
$params = Yii::$app->request->post();
$order = $this->orderService->create($params);
return $order;
}Service
Use services for:
- business workflows
- transactions
- cross-model operations
- external API calls
- complex business rules
- queue dispatch
- multi-step operations
Example:
final class OrderService
{
public function create(array $params): Order
{
return Yii::$app->db->transaction(function () use ($params) {
// business logic
});
}
}ActiveRecord
Use ActiveRecord for:
- entity persistence
- simple queries
- relationships
- validation
Avoid putting large business workflows inside ActiveRecord.
ActiveQuery
Prefer readable queries:
Order::find()
->where(['status' => Order::STATUS_PENDING])
->orderBy(['id' => SORT_DESC])
->all();N+1 Prevention
Always check for N+1 when using relations.
Potentially dangerous:
$orders = Order::find()->all();
foreach ($orders as $order) {
echo $order->user->name;
}Prefer:
$orders = Order::find()
->with('user')
->all();When modifying relationship-heavy code, inspect the generated SQL if necessary.
Large Dataset
Never blindly use:
Model::find()->all();for potentially large datasets.
Prefer:
foreach (
Model::find()->batch(500) as $models
) {
foreach ($models as $model) {
// ...
}
}or:
foreach (
Model::find()->each(500) as $model
) {
// ...
}updateAll/deleteAll
For large bulk operations consider:
Model::updateAll(
['status' => Model::STATUS_DONE],
['status' => Model::STATUS_PENDING]
);and:
Model::deleteAll([
'status' => Model::STATUS_DELETED,
]);But check:
- events
- behaviors
- timestamps
- business rules
because bulk operations bypass normal ActiveRecord lifecycle behavior.
Transactions
Use transactions when multiple writes must succeed or fail together.
Yii::$app->db->transaction(function () {
// write A
// write B
});Do not create unnecessary long transactions.
Do not hold database transactions while waiting for:
- HTTP requests
- external APIs
- long-running jobs
- user input
Validation
Never trust HTTP input.
Use:
- rules()
- scenarios
- explicit validation
- typed DTOs when appropriate
Do not blindly persist:
$model->load($request->post());
$model->save();without checking validation result.
Dependency Injection
For new code prefer dependency injection where practical.
Avoid unnecessary:
Yii::$app->someComponentinside deeply nested business logic.
Use constructor injection for stable dependencies when compatible with the project architecture.
Existing Project Conventions
Before creating a new class:
- Search similar classes.
- Follow existing namespace conventions.
- Follow existing directory structure.
- Follow existing naming conventions.
- Reuse existing base classes.
---
# 3. `.cursor/rules/mysql.mdc`
```md
---
description: MySQL database development and performance rules
globs:
- "**/*.php"
- "**/*.sql"
alwaysApply: true
---
# MySQL Rules
## Query Safety
Never concatenate user input into SQL.
Bad:
```php
$sql = "SELECT * FROM user WHERE name = '" . $name . "'";Use parameterized queries.
Query Performance
When modifying database code, consider:
- indexes
- WHERE conditions
- ORDER BY
- JOIN
- GROUP BY
- LIMIT
- result size
- N+1 queries
Indexes
Before adding an index:
- inspect existing indexes
- check query patterns
- avoid duplicate indexes
- consider composite index ordering
Do not add indexes blindly.
N+1
Always inspect loops containing database queries.
Bad:
foreach ($orders as $order) {
User::findOne($order->user_id);
}Prefer eager loading or batch querying.
Large Data
Avoid:
->all()for millions of records.
Prefer:
- batch()
- each()
- pagination
- cursor-like processing where appropriate
COUNT
Be careful with expensive COUNT queries on large tables.
Transactions
Keep transactions short.
Never perform slow external HTTP requests inside database transactions unless absolutely necessary.
SQL Review
For complex queries, consider using:
EXPLAINbefore recommending a performance optimization.
Never claim a query is optimized without examining its execution characteristics when performance is the actual task.
---
# 4. `.cursor/rules/redis.mdc`
```md
---
description: Redis caching, locking and queue rules
globs:
- "**/*.php"
alwaysApply: true
---
# Redis Rules
## Key Naming
Redis keys must have a consistent namespace.
Prefer:
```text
project:user:{id}
project:order:{id}
project:lock:order:{id}Avoid random key formats.
Cache
Every cache entry must consider:
- key
- TTL
- invalidation
- serialization
- stampede protection
Do not cache everything.
Cache Stampede
For hot data consider:
- locking
- early refresh
- stale-while-revalidate
- randomized TTL
Distributed Lock
When using Redis locks:
- always define TTL
- always release the lock
- avoid infinite locks
- make operations idempotent
Example conceptual pattern:
acquire lock
↓
execute
↓
finally release lockQueue
Queue jobs must be:
- idempotent
- retry-safe
- timeout-aware
- failure-aware
Do not assume a job executes exactly once.
Retry
When implementing retries consider:
- transient errors
- permanent errors
- retry count
- backoff
- dead-letter/failed queue
Redis Memory
Do not store unlimited collections.
Consider:
- TTL
- maximum list size
- maximum set size
- memory usage
---
# 5. `.cursor/rules/api.mdc`
```md
---
description: Yii2 REST API development rules
globs:
- "**/controllers/**/*.php"
- "**/modules/**/*.php"
alwaysApply: true
---
# API Rules
## Input
Never trust request input.
Validate:
- required fields
- type
- length
- range
- enum
- authorization
## Response
API responses should follow the project's existing response format.
Do not introduce a new response structure without checking existing APIs.
## HTTP Status
Use appropriate status codes when the project supports them:
200 - success
201 - created
204 - no content
400 - invalid request
401 - unauthenticated
403 - unauthorized
404 - not found
409 - conflict
422 - validation error
500 - server error
## Authentication
Authentication and authorization are different.
Always check authorization for resources.
Example:
User A must not be able to access User B's resource simply by changing:
```text
?id=123Pagination
For potentially large API results use pagination.
Do not return unlimited database records.
Serialization
Do not blindly return ActiveRecord objects if that may expose:
- password hashes
- tokens
- internal IDs
- internal status
- sensitive fields
Use explicit fields when appropriate.
API Compatibility
Before changing:
- field names
- response structure
- HTTP status
- error format
search existing frontend/client usage.
---
# 6. `.cursor/rules/security.mdc`
```md
---
description: PHP Yii2 security rules
globs:
- "**/*.php"
alwaysApply: true
---
# Security Rules
Every new backend feature must consider:
- authentication
- authorization
- input validation
- SQL injection
- XSS
- CSRF
- IDOR
- mass assignment
- file upload
- SSRF
- command injection
- sensitive information exposure
- logging of secrets
## SQL Injection
Never concatenate user-controlled SQL.
## Mass Assignment
Do not blindly assign:
```php
$model->attributes = $request->post();without checking Yii2 safe attributes and validation.
IDOR
Never assume an authenticated user can access a resource.
Bad:
$order = Order::findOne($id);if authorization is not checked.
Prefer checking ownership or RBAC.
Passwords
Never:
- log passwords
- return password hashes
- put secrets into source code
Tokens
Never log:
- JWT
- access token
- refresh token
- API key
- session cookie
File Upload
Validate:
- extension
- MIME type
- file size
- filename
- storage location
Do not trust the uploaded filename.
Command Execution
Be extremely careful with:
exec()
shell_exec()
system()
passthru()Never pass untrusted user input directly into shell commands.
External URL
Validate external URLs before making server-side HTTP requests.
Consider SSRF.
Logging
Logs should contain enough information for debugging but must not contain secrets or sensitive user data.
---
# 7. `.cursor/rules/testing.mdc`
```md
---
description: PHP Yii2 testing rules
globs:
- "**/*.php"
alwaysApply: true
---
# Testing Rules
When modifying business logic:
1. Identify existing tests.
2. Add or update tests when practical.
3. Run the relevant test suite.
4. Do not claim tests pass unless they were actually executed.
## Unit Tests
Prioritize:
- business rules
- services
- complex calculations
- permission checks
- edge cases
## API Tests
Test:
- successful request
- invalid parameters
- unauthorized request
- nonexistent resource
- boundary conditions
## Database Tests
When database behavior matters, test:
- insert
- update
- transaction rollback
- unique constraints
- relations
## Regression
When fixing a bug:
1. reproduce the bug
2. create a regression test
3. implement the fix
4. run the test
5. run related tests
## Test Claims
Never say:
"All tests pass."
unless tests were actually executed.8. .cursor/skills/yii2-backend/SKILL.md
---
name: yii2-backend
description: Expert PHP Yii2 backend development skill. Use for Yii2 architecture, ActiveRecord, ActiveQuery, REST APIs, services, database operations, Redis, performance optimization, security, debugging, refactoring and backend code review.
---
# Yii2 Backend Expert
You are an experienced PHP/Yii2 backend engineer.
Your job is not merely to generate code.
Before changing code, understand the existing project architecture and conventions.
## Workflow
For every non-trivial task:
### Step 1: Understand the project
Inspect:
- composer.json
- config/
- common/
- frontend/
- backend/
- console/
- api/
- modules/
- models/
- services/
- components/
- behaviors/
Do not assume the project follows the standard Yii2 structure.
### Step 2: Search existing implementation
Before creating new code search for:
- similar Controller
- similar Model
- similar Service
- existing helper
- existing component
- existing Redis wrapper
- existing HTTP client
- existing response formatter
- existing validation rules
Prefer reuse over duplication.
### Step 3: Understand data flow
Determine:
```text
Request
↓
Controller
↓
Validation
↓
Service
↓
Model
↓
Databaseor the actual architecture used by the project.
Step 4: Implement minimum necessary changes
Do not refactor unrelated code.
Avoid changing:
- unrelated formatting
- unrelated classes
- dependency versions
- database schema
unless required.
Yii2 ActiveRecord
Before modifying ActiveRecord code check:
- rules()
- scenarios()
- relations()
- behaviors()
- events
- beforeSave()
- afterSave()
- beforeDelete()
- afterDelete()
Remember:
updateAll()
deleteAll()do not behave exactly like individual ActiveRecord save/delete operations.
Query Optimization
When the task involves performance:
- inspect SQL
- identify query count
- identify N+1
- inspect indexes
- consider eager loading
- consider batch processing
- consider caching
- consider database execution plans
Do not optimize based solely on intuition.
Large Dataset
For large tables prefer:
->batch(500)or:
->each(500)instead of:
->all()Do not load millions of records into PHP memory.
Business Transactions
If a business operation modifies multiple related records:
Yii::$app->db->transaction(function () {
// operation
});Keep transaction scope small.
Do not perform external network calls inside transactions unless necessary.
API Development
When creating an API:
- inspect existing API conventions
- validate input
- authenticate
- authorize
- execute business logic
- return consistent response
- avoid exposing sensitive fields
Debugging
When debugging:
- reproduce
- inspect stack trace
- identify first meaningful error
- inspect surrounding code
- check configuration
- check database/Redis/external dependencies
- propose root cause
- implement minimal fix
- test
Do not blindly patch the last exception line.
Performance
When asked to optimize:
First establish the bottleneck.
Possible bottlenecks:
- MySQL
- Redis
- PHP CPU
- PHP memory
- network
- external API
- serialization
- filesystem
- queue
- locking
Do not automatically add Redis caching.
Security
Every change must consider:
- SQL injection
- XSS
- CSRF
- IDOR
- RBAC
- mass assignment
- file upload
- SSRF
- command injection
- secret leakage
Code Review
When reviewing code, check:
Correctness
- business logic
- edge cases
- null handling
- concurrency
Database
- N+1
- indexes
- transaction scope
- unnecessary queries
- large dataset handling
Performance
- loops
- memory
- caching
- Redis
- external requests
Security
- authorization
- validation
- injection
- sensitive information
Maintainability
- naming
- duplication
- complexity
- coupling
Compatibility
- existing API
- existing database schema
- existing callers
Response Format
For implementation tasks:
- Explain the root cause or approach briefly.
- Show the changed files.
- Provide complete relevant code.
- Explain important design decisions.
- Provide test commands.
- Clearly state what was actually tested.
Do not claim commands were executed unless they were actually executed.
Important Rule
Do not blindly follow the user's proposed implementation if it introduces:
- security vulnerabilities
- data corruption risk
- obvious performance problems
- broken Yii2 conventions
Instead explain the problem and provide a safer implementation.
---
# 9. `.cursor/skills/yii2-backend/references/architecture.md`
```md
# Yii2 Architecture Reference
Recommended default architecture:
Controller
↓
Request/DTO
↓
Service
↓
Domain/Model
↓
Repository/ActiveRecord
↓
Database
However, do not force this architecture onto an existing project.
Existing architecture always takes priority.
## Controller
Responsibilities:
- HTTP
- authentication
- authorization
- parameter extraction
- validation
- response
Avoid business logic.
## Service
Responsibilities:
- business workflow
- transaction
- multiple model coordination
- external service interaction
- queue dispatch
## Model
Responsibilities:
- data representation
- validation
- relationships
- persistence
Avoid putting huge business workflows into models.
## Repository
Do not introduce Repository classes automatically.
Use them when:
- query complexity is high
- data access must be abstracted
- multiple data sources exist
- existing project architecture already uses repositories
Do not create:
Controller → Repository → ActiveRecord
just for the sake of patterns.
## Transaction
Transaction should surround the atomic business operation.
Avoid:
transaction {
HTTP request
database operation
}
Prefer:
HTTP request
↓
validate
↓
external preparation if needed
↓
transaction {
database writes
}
↓
response10. .cursor/skills/yii2-backend/references/database.md
# Yii2 Database Reference
## Query Checklist
When reviewing a query ask:
1. How many SQL statements execute?
2. Is there N+1?
3. Are indexes available?
4. Is ORDER BY indexed?
5. Is the result set bounded?
6. Is the query executed inside a loop?
7. Is the same query repeated?
8. Can eager loading help?
9. Can batch processing help?
10. Is caching appropriate?
## Common Bad Pattern
```php
foreach ($users as $user) {
$orders = Order::find()
->where(['user_id' => $user->id])
->all();
}Potential N+1.
Better
Use eager loading:
$users = User::find()
->with('orders')
->all();or fetch data in batches depending on the use case.
Large Data
Bad:
$rows = Order::find()->all();Potentially dangerous for large tables.
Better:
foreach (Order::find()->batch(1000) as $orders) {
// process
}Bulk Update
Order::updateAll(
['status' => Order::STATUS_DONE],
['status' => Order::STATUS_PENDING]
);Remember that ActiveRecord events and behaviors may not run as with individual save() calls.
EXPLAIN
For slow SQL:
EXPLAIN SELECT ...Inspect:
- type
- possible_keys
- key
- rows
- Extra
---
# 11. `.cursor/skills/yii2-backend/references/review.md`
```md
# Yii2 Code Review Checklist
## PHP
- [ ] PHP version compatible
- [ ] types appropriate
- [ ] null handling
- [ ] exception handling
- [ ] no unnecessary duplication
## Yii2
- [ ] Controller is thin
- [ ] validation exists
- [ ] ActiveRecord relations checked
- [ ] N+1 checked
- [ ] transaction scope correct
- [ ] behaviors/events considered
## MySQL
- [ ] indexes checked
- [ ] unnecessary SQL avoided
- [ ] large result set controlled
- [ ] batch processing considered
- [ ] transaction performance checked
## Redis
- [ ] key naming consistent
- [ ] TTL exists where appropriate
- [ ] cache invalidation considered
- [ ] distributed lock has timeout
- [ ] queue job is idempotent
## Security
- [ ] authentication
- [ ] authorization
- [ ] SQL injection
- [ ] XSS
- [ ] CSRF
- [ ] IDOR
- [ ] mass assignment
- [ ] file upload
- [ ] SSRF
- [ ] command injection
- [ ] secrets
## API
- [ ] input validation
- [ ] response compatibility
- [ ] status codes
- [ ] pagination
- [ ] sensitive fields excluded
## Testing
- [ ] regression test
- [ ] edge cases
- [ ] relevant tests executed
- [ ] no false test claims推荐使用方式
完成配置后,可以直接在 Cursor Agent 中这样使用:
帮我分析这个 Yii2 项目的订单创建流程。
先不要改代码。
先找出:
1. Controller
2. Model
3. Service
4. 数据库表
5. Redis
6. 事务
7. 权限验证
8. 可能的 N+1
最后给出改造方案。然后:
按照刚才的方案实施。
要求:
1. 不修改无关文件
2. 保持现有 API 兼容
3. 保持 Yii2 项目现有架构
4. 所有数据库写操作检查事务
5. 检查 N+1
6. 增加必要测试
7. 修改完成后运行相关测试代码完成后:
请按照 yii2-backend 的 review checklist 对刚才的修改进行 Code Review。
重点检查:
- N+1
- SQL 性能
- Redis
- 事务
- 并发
- 权限
- IDOR
- 参数验证
- API 兼容性
- PHP 内存
- 大数据量处理
不要修改代码,只输出问题和严重程度。最后:
根据刚才 Code Review 的结果修复所有 P0/P1 问题。
修复后重新运行相关测试,并告诉我实际执行了哪些测试。
评论已关闭