為什麼要文檔化類?
- 降低認知負荷:新成員閱讀介面即懂用法,無需翻原始碼。
- IDE 智能提示:型別提示、參數補全、跳轉定義。
- 自動生成 API 文檔站:對外開放 API、SDK 使用者必備。
- 靜態分析基礎:Psalm、PHPStan、Rector 依賴型別註解發現 Bug。
歷代工具回顧
| 工具 | 年代 | 特點 | 現狀 |
| phpDocumentor 1.x | ~2010 | @tag 註解、模板生成 HTML | 停維護 |
| Doxygen | 早期 | 支援 C++/Java/PHP、交叉引用 | PHP 支援弱 |
| ApiGen | 2012-2017 | 現代 UI、支援 PHP 7、已停更 | 歸檔 |
| Sami | 2013-2019 | Symfony 專用、Twig 模板、快 | 不再更新 |
| phpDocumentor 3.x | 2018-至今 | PHP 7/8 原生、支援屬性、Twig、植入 CI | 主流標準 |
| Doctrine Annotations | 2010- | 註解解析器、被屬性取代 | 維護模式 |
現代標準:PHP 8 Attributes + phpDocumentor 3
1. 類與方法完整範例
<?php
declare(strict_types=1);
namespace App\Service;
use DateTimeImmutable;
use InvalidArgumentException;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
final class ApiEndpoint {
public function __construct(
public string $path,
public string $method = 'GET',
public int $version = 1
) {}
}
final class OrderService {
/**
* 建立訂單並回傳訂單號。
*
* @param array{user_id:int, items:array, coupon_code?:string} $data
* @return string 訂單號 (格式: ORD-YYYYMMDD-XXXXXX)
* @throws InvalidArgumentException 當商品為空或庫存不足
*/
#[ApiEndpoint(path: '/orders', method: 'POST', version: 2)]
public function createOrder(array $data): string {
// ...
return 'ORD-20240115-A1B2C3';
}
/**
* 依條件查詢訂單列表。
*
* @param array{status?:string, date_from?:DateTimeImmutable, date_to?:DateTimeImmutable, page:int, per_page:int} $filters
* @return array{data:Order[], meta:array{total:int, page:int, per_page:int}}
*/
public function listOrders(array $filters): array {
// ...
return ['data' => [], 'meta' => ['total' => 0, 'page' => 1, 'per_page' => 15]];
}
}
2. 關鍵註解標籤速查
| 標籤 | 用途 | 範例 |
@param | 參數型別與描述 | @param string $name 用戶名 |
@return | 返回型別與描述 | @return Order|null 訂單或 null |
@throws | 拋出異常型別與條件 | @throws InvalidArgumentException 參數錯誤 |
@var | 屬性/變數型別 | @var OrderRepository |
@see | 關聯連結 | @see OrderService::createOrder() |
@deprecated | 棄用說明 | @deprecated 使用 createOrderV2() |
@internal | 內部使用,不出現在公開文檔 | @internal |
@psalm-* | Psalm 專用型別提示 | @psalm-pure |
3. 複雜型別寫法(PHPStan/Psalm 通用)
// 陣列形狀
@param array{id:int, name:string, tags:string[]} $user
// 聯合型別
@return Order|false
// 可為 null
@param ?string $nickname
// 可呼叫物件
@param callable(string):bool $validator
// 泛型(PHPStan)
@param iterable $orders
@return Collection
// 模板型別
@template T
@param T $value
@return T
安裝與 CI 整合
# 安裝
composer require --dev phpdocumentor/phpdocumentor
# 生成文檔
vendor/bin/phpdoc -d src -t docs/api --template=clean
# GitHub Actions 片段
- name: Generate API Docs
run: |
composer install --no-interaction
vendor/bin/phpdoc -d src -t public/api
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public/api
靜態分析聯動:讓文檔發揮更大價值
# composer.json scripts
"scripts": {
"phpstan": "phpstan analyse src --level=5",
"psalm": "psalm --show-info=false",
"docs": "phpdoc -d src -t docs/api",
"check": ["@phpstan", "@psalm", "@docs"]
}
- 型別註解不準確 → PHPStan/Psalm 報錯 → 強迫修正文檔與代碼一致。
- Rector 可根據型別註解自動重構(如升級陣列語法、補全嚴格型別)。
常見坑與建議
- 不要寫廢話:
@param int $id ID → 刪除,型別已自述。
- 公開 API 必須 100% 覆蓋:public/protected 方法、屬性、類別。
- 私有方法可選:複雜邏輯加註解助維護,簡單者省略。
- 保持同步:改簽名同步改註解,CI 失敗即阻擋合併。
- 使用 IDE 插件:PHPStorm / VS Code (PHP Intelephense) 即時顯示文檔懸浮提示。
原文《如何文檔化你的PHP類》重構,棄用舊工具對比,聚焦 PHP 8 Attributes + phpDocumentor 3 + 靜態分析的現代工具鏈。