# 通用 PHP 编程规则

本文件仅规范 PHP 代码开发，可独立复制到其他项目使用。项目自身规则应补充最低 PHP 版本、框架与自动加载方式、数据访问层、返回结构和日志入口；冲突时以项目明确约定和用户最新要求为准。

## 1. 修改原则

- 修改前理解入口、调用链、数据流、输入输出和影响范围，只修改完成需求必需的文件。
- 优先稳定、最小修改和历史兼容，不顺手重构、批量格式化、重命名或改写无关代码。
- 优先复用已有函数、类、业务方法、返回结构和错误处理方式，不建立重复包装层。
- 不随意改变历史方法名、接口字段、状态码、Content-Type 或成功、失败语义。
- 提交可运行的完整实现，不用省略关键逻辑的伪代码代替。
- 业务含义、权限边界或验收标准存在高风险歧义时，先确认再实现。

## 2. 兼容与代码风格

- 以项目声明的最低 PHP 版本为语法和标准库基线，不使用更高版本才支持的能力。
- 沿用项目现有框架、命名空间、自动加载、命名、缩进和括号风格，不另建一套架构。
- 新增数组在版本允许时优先使用短数组 `[]`，不为统一风格批量替换历史 `array()`。
- 变量名使用有业务含义的英文，并遵循项目现有命名习惯，避免无意义缩写。
- 不新增 `@` 错误抑制；不随意新增全局变量，确需使用时说明原因和影响范围。
- 复杂逻辑添加简短中文注释，说明关键步骤、分支原因和特殊处理；自解释代码不逐行注释。

## 3. 复用、函数粒度与性能

- 只有逻辑会重复使用、职责明显独立，或拆分后能显著降低主流程复杂度时才新增函数。
- 避免过多小函数、单行转发函数和只增加调用层级的包装方法。
- 公共逻辑只有在复用性明确且职责清晰时才提升为公共函数或类。
- 避免在循环中执行可批量化的数据库查询、文件写入或远程请求，优先批量处理、内存映射或分页。
- 大任务按批次保存进度，正常完成或异常退出前持久化必要状态，避免每处理一条就重写完整文件。

## 4. PHPDoc

- 新增或实质重写类、方法、函数时写必要 PHPDoc；未实质重写的历史代码不得改变作者归属。
- 新增或实质重写的方法必须使用 `@method` 写清中文业务说明；摘要行不得重复相同说明。
- 使用 `@date YYYY-MM-DD` 记录本次新增或实质重写日期；不得给未实际修改的历史方法补写当前日期。
- `@author` 必须写具体操作人；AI 完成或参与时追加 `& AI`。无法确认操作人时先询问，不得猜测或默认填写。
- 方法无返回值时不强行写 `@return void`。
- 读取请求或其他外部输入时，注明请求方式、参数名、类型、可选性和校验规则。
- 返回 JSON、数组或项目统一返回结构时，给出贴近真实实现的成功、失败示例，不写“返回数组格式”等空泛说明。

示例：

```php
/**
 * @method 自动调整当前业务员和客户之间的关系
 * @http POST
 * @param $customerId int [必填] 客户 ID，大于 0
 * @return 成功：['status'=>true, 'data'=>['customerId'=>1], 'code'=>0]
 *         失败：['status'=>false, 'data'=>'错误信息', 'code'=>0]
 * @author 操作人姓名 & AI
 * @date 2026-08-12
 */
```

## 5. 输入、输出与安全

- 请求参数、Cookie、请求头、服务器变量、上传内容和第三方响应均视为不可信，使用前校验类型、格式、长度、范围和枚举值。
- 外部标量先规范化类型；ID、状态和枚举比较避免宽松比较，`in_array()`、`array_search()` 使用严格模式，并正确区分 `false` 与 `0`。
- 检查 `json_encode()`、`json_decode()` 的失败状态和解码结果类型，不得默认 JSON 编解码成功。
- 业务代码禁止使用 `eval()`、用户可控的动态 `include`/`require` 路径和不可信数据反序列化；确需 `unserialize()` 时仅接受可信来源并限制允许类。
- 写操作在服务端确认身份、权限和资源归属，不只依赖页面入口限制。
- HTML 文本、属性、URL、JavaScript 和 JSON 按输出上下文分别编码；数据库转义不能代替输出编码。
- 上传校验扩展名、MIME、大小和真实内容；输入生成的文件路径必须防止路径穿越。
- 写入、覆盖、移动或删除文件前，确认解析后的最终路径仍位于预期目录。
- 密码、密钥、Token、证书、连接信息、隐私数据和完整敏感请求不得硬编码或写入日志、错误响应、文档和测试数据。
- 外部服务地址、连接信息、密钥和环境版本由项目配置管理，不写死在业务代码中。

## 6. 数据访问代码

- 业务代码遵循项目既有数据访问分层，不绕过业务层或数据访问封装直接依赖底层驱动。
- 优先扩展已有业务数据方法；没有合适能力时，在既有数据层增加职责清晰的方法。
- 业务值优先使用参数绑定或项目数据层提供的安全能力；不得把原始请求值直接拼入 SQL。
- 动态表名、字段名、排序字段、排序方向和原始表达式必须来自代码白名单。
- 数字 ID、页码、数量和状态先做类型转换；`IN` 列表逐项校验。
- 复杂查询使用明确的字段清单、表别名和字段来源，避免 `SELECT *` 及同名字段歧义。
- 金额、费率等精确值不得使用二进制浮点直接累计或精确比较；按项目约定使用最小单位整数、精确字符串或高精度函数。
- 避免双重转义；输入校验、SQL 参数处理和输出编码各自按职责执行。
- 多步骤资金、库存、订单等写入明确事务边界、失败路径、幂等和补偿方案。
- 表设计、字段命名、索引、Comment 和 DDL 属于数据库专项规范，不在本文重复定义。

## 7. 异常、日志与外部调用

- 沿用项目现有异常和日志入口，不建立同功能包装层。
- 捕获异常时保留必要且脱敏的诊断信息，不吞掉错误，也不向用户暴露内部堆栈和敏感信息。
- 重要写操作沿用项目审计入口，记录操作者、操作对象、动作、结果和必要时间信息，并保持脱敏。
- 批量任务记录成功数、失败数、失败原因和必要上下文。
- 外部同步和批处理支持幂等与断点续跑；已成功处理的数据默认跳过，强制重跑必须有显式控制。
- 外部调用设置合理超时和有上限的重试；连续失败达到阈值后停止并报告。
- 外部写操作和回调考虑签名、重放、幂等与重复提交。
- 先进行只读、预览或小批量验证；未经授权不执行全量修复、批量更新、批量删除或 DDL。

## 8. Composer、第三方代码与文件

- 不直接修改 `vendor` 或其他第三方源码实现业务需求。
- 新增或升级依赖前说明用途，核对最低 PHP 版本和历史行为兼容性，不随意跨大版本升级。
- 不盲目使用 `--ignore-platform-reqs`，不手工编辑锁文件中的包元数据。
- 删除依赖前扫描类、函数和自动加载引用，确认无运行时依赖。
- 删除或移动 PHP 类、函数或文件前，核对非配置代码中的加载、调用、回调、脚本入口和自动加载引用。
- 安装产生的临时镜像、缓存和调试文件不得残留在源码中。
- 保持目标文件原有编码、换行和局部风格；发现乱码先识别真实编码，不整仓转码。
- 不修改缓存、编译产物或生成文件代替源文件。

## 9. 验证

- 每个修改过的非第三方 PHP 文件使用项目最低 PHP 版本执行语法检查；可隔离解释器配置时优先使用 `php -n -l <file>`。
- 静态复核调用方、参数、返回字段、权限、异常路径和兼容影响。
- 有相关测试时执行最小相关测试；自动加载变化时验证自动加载。
- 只有在项目授权且不会读取受保护内容时，才执行应用入口或集成测试。
- 数据库、上传、浏览器或外部服务未实际连通，就明确标记“未做集成验证”，不得声称完整通过。

## 10. PHP 7.3 兼容档案

项目最低版本为 PHP 7.3 时，不得新增以下能力：

- PHP 7.4+：箭头函数、类型化属性、空合并赋值 `??=` 和高版本数组解包。
- PHP 8.0+：`match`、Nullsafe `?->`、联合类型、`mixed`、`static` 返回类型、Attributes、命名参数和构造器属性提升。
- PHP 8.1+：`enum`、`readonly`、`never` 和交叉类型。
- PHP 7.3 标准库不存在的函数，包括 `str_contains()`、`str_starts_with()` 和 `str_ends_with()`。
