可重用套件的最佳實務
本文全部關於如何建構您的可重用套件,使其可配置和可擴展。可重用套件是指那些旨在在許多公司專案中私下共享,或公開共享,以便任何 Symfony 專案都可以安裝它們的套件。
套件名稱
套件也是一個 PHP 命名空間。命名空間必須遵循 PSR-4 PHP 命名空間和類別名稱的互操作性標準:它以供應商區段開始,後跟零或多個類別區段,並以命名空間簡短名稱結束,該名稱必須以 Bundle
結尾。
只要您向其中添加「套件類別」(即擴展 Bundle 的類別),命名空間就成為套件。套件類別名稱必須遵循以下規則
- 僅使用字母數字字符和底線;
- 使用 StudlyCaps 名稱(即,首字母大寫的 camelCase);
- 使用描述性且簡短的名稱(不超過兩個單詞);
- 以供應商(以及可選的類別命名空間)的串聯作為名稱的前綴;
- 以
Bundle
作為名稱的後綴。
以下是一些有效的套件命名空間和類別名稱
命名空間 | 套件類別名稱 |
---|---|
Acme\Bundle\BlogBundle |
AcmeBlogBundle |
Acme\BlogBundle |
AcmeBlogBundle |
按照慣例,套件類別的 getName()
方法應傳回類別名稱。
注意
如果您公開共享您的套件,則必須使用套件類別名稱作為儲存庫的名稱(例如 AcmeBlogBundle 而不是 BlogBundle)。
注意
Symfony 核心套件不會以 Symfony
作為套件類別的前綴,並且總是添加 Bundle
子命名空間;例如:FrameworkBundle。
每個套件都有一個別名,它是使用底線的套件名稱的小寫簡短版本(AcmeBlogBundle 的 acme_blog
)。此別名用於強制專案內的唯一性,並用於定義套件的配置選項(有關一些使用範例,請參閱下文)。
目錄結構
以下是建議的 AcmeBlogBundle 目錄結構
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<your-bundle>/
├── assets/
├── config/
├── docs/
│ └─ index.md
├── public/
├── src/
│ ├── Controller/
│ ├── DependencyInjection/
│ └── AcmeBlogBundle.php
├── templates/
├── tests/
├── translations/
├── LICENSE
└── README.md
注意
當您的套件類別擴展建議的 AbstractBundle 時,預設會使用此目錄結構。如果您的套件擴展了 Bundle 類別,則您必須如下覆寫 getPath()
方法
1 2 3 4 5 6 7 8 9
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AcmeBlogBundle extends Bundle
{
public function getPath(): string
{
return \dirname(__DIR__);
}
}
以下檔案是強制性的,因為它們確保自動化工具可以依賴的結構慣例
src/AcmeBlogBundle.php
:這是將純目錄轉換為 Symfony 套件的類別(將此更改為您的套件名稱);README.md
:此檔案包含套件的基本描述,通常會顯示一些基本範例和指向其完整文檔的連結(它可以使用 GitHub 支援的任何標記格式,例如README.rst
);LICENSE
:程式碼使用的許可證的完整內容。大多數第三方套件都以 MIT 許可證發布,但您可以選擇任何許可證;docs/index.md
:套件文檔的根檔案。
對於最常用的類別和檔案,子目錄的深度應保持在最低限度。最多兩個層級。
套件目錄是唯讀的。如果您需要寫入臨時檔案,請將它們儲存在主機應用程式的 cache/
或 log/
目錄下。工具可以在套件目錄結構中生成檔案,但前提是生成的檔案將成為儲存庫的一部分。
以下類別和檔案具有特定的位置(有些是強制性的,另一些只是大多數開發人員遵循的慣例)
類型 | 目錄 |
---|---|
命令 | src/Command/ |
控制器 | src/Controller/ |
服務容器擴展 | src/DependencyInjection/ |
Doctrine ORM 實體 | src/Entity/ |
Doctrine ODM 文檔 | src/Document/ |
事件監聽器 | src/EventListener/ |
設定(路由、服務等) | config/ |
Web 資產(編譯後的 CSS 和 JS、圖像) | public/ |
Web 資產來源(.scss 、.ts 、Stimulus) |
assets/ |
翻譯檔 | translations/ |
驗證(不使用屬性時) | config/validation/ |
序列化(不使用屬性時) | config/serialization/ |
樣板 | 樣板/ |
單元和功能測試 | tests/ |
類別
套件目錄結構用作命名空間層次結構。例如,儲存在 src/Controller/ContentController.php
中的 ContentController
控制器的完整限定類別名稱將為 Acme\BlogBundle\Controller\ContentController
。
所有類別和檔案都必須遵循Symfony 編碼標準。
某些類別應被視為外觀模式,並且應盡可能簡短,例如命令、助手、監聽器和控制器。
連接到事件分派器的類別應以 Listener
作為後綴。
異常類別應儲存在 Exception
子命名空間中。
Doctrine 實體/文檔
如果套件包含 Doctrine ORM 實體和/或 ODM 文檔,建議使用儲存在 config/doctrine/
中的 XML 檔案來定義它們的對應。這允許使用標準 Symfony 機制來覆寫套件部分來覆寫該對應。當使用屬性來定義對應時,這是不可能的。
測試
套件應附帶使用 PHPUnit 編寫並儲存在 tests/
目錄下的測試套件。測試應遵循以下原則
- 測試套件必須可使用從範例應用程式運行的簡單
phpunit
命令執行; - 功能測試應僅用於測試響應輸出以及您可能擁有的一些分析資訊;
- 測試應涵蓋至少 95% 的程式碼庫。
注意
測試套件不得包含 AllTests.php
腳本,但必須依賴 phpunit.xml.dist
檔案的存在。
持續整合
持續測試套件程式碼(包括其所有提交和提取請求)是一種稱為持續整合的良好實務。有幾項服務為開源專案免費提供此功能,例如 GitHub Actions 和 Travis CI。
套件至少應測試
- 其依賴項的下限(通過運行
composer update --prefer-lowest
); - 支援的 PHP 版本;
- 所有支援的主要 Symfony 版本(例如,如果聲稱同時支援
4.x
和5.x
)。
因此,支援 PHP 7.3、7.4 和 8.0 以及 Symfony 4.4 和 5.x 的套件應至少具有此測試矩陣
PHP 版本 | Symfony 版本 | Composer 標誌 |
---|---|---|
7.3 | 4.* |
--prefer-lowest |
7.4 | 5.* |
|
8.0 | 5.* |
提示
測試應在 SYMFONY_DEPRECATIONS_HELPER
環境變數設定為 max[direct]=0
的情況下運行。這確保套件中沒有程式碼直接使用已棄用的功能。
最低依賴項測試可以使用此變數設定為 disabled=1
來運行。
要求特定的 Symfony 版本
您可以將特殊的 SYMFONY_REQUIRE
環境變數與 Symfony Flex 一起使用,以安裝特定的 Symfony 版本
1 2 3 4 5 6 7 8 9 10 11 12
# this requires Symfony 5.x for all Symfony packages
export SYMFONY_REQUIRE=5.*
# alternatively you can run this command to update composer.json config
# composer config extra.symfony.require "5.*"
# install Symfony Flex in the CI environment
composer global config --no-plugins allow-plugins.symfony/flex true
composer global require --no-progress --no-scripts --no-plugins symfony/flex
# install the dependencies (using --prefer-dist and --no-progress is
# recommended to have a better output and faster download time)
composer update --prefer-dist --no-progress
警告
如果您要快取 Composer 依賴項,請勿快取 vendor/
目錄,因為這會產生副作用。而是快取 $HOME/.composer/cache/files
。
安裝
套件應在其 composer.json
檔案中設定 "type": "symfony-bundle"
。這樣,Symfony Flex 將能夠在安裝套件時自動啟用您的套件。
如果您的套件需要任何設定(例如配置、新檔案、更改 .gitignore
等),那麼您應該建立一個 Symfony Flex 配方。
文件
所有類別和函數都必須附帶完整的 PHPDoc。
還應在 docs/
目錄中提供廣泛的文檔。索引檔案(例如 docs/index.rst
或 docs/index.md
)是唯一強制性的檔案,並且必須是文檔的入口點。reStructuredText (rST) 是用於在 Symfony 網站上呈現文檔的格式。
安裝指示
為了簡化第三方套件的安裝,請考慮在您的 README.md
檔案中使用以下標準化說明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
Installation
============
Make sure Composer is installed globally, as explained in the
[installation chapter](https://composer.dev.org.tw/doc/00-intro.md)
of the Composer documentation.
Applications that use Symfony Flex
----------------------------------
Open a command console, enter your project directory and execute:
```console
composer require <package-name>
```
Applications that don't use Symfony Flex
----------------------------------------
### Step 1: Download the Bundle
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
```console
composer require <package-name>
```
### Step 2: Enable the Bundle
Then, enable the bundle by adding it to the list of registered bundles
in the `config/bundles.php` file of your project:
```php
// config/bundles.php
return [
// ...
<vendor>\<bundle-name>\<bundle-long-name>::class => ['all' => true],
];
```
上面的範例假設您正在安裝套件的最新穩定版本,在這種情況下,您不必提供套件版本號(例如 composer require friendsofsymfony/user-bundle
)。如果安裝說明引用了過去的套件版本或某些不穩定的版本,請包含版本約束(例如 composer require friendsofsymfony/user-bundle "~2.0@dev"
)。
或者,您可以添加更多安裝步驟(步驟 3、步驟 4 等)來解釋其他所需的安裝任務,例如註冊路由或轉儲資產。
路由
如果套件提供路由,則它們必須以套件別名作為前綴。例如,如果您的套件名為 AcmeBlogBundle,則其所有路由都必須以 acme_blog_
作為前綴。
樣板
如果套件提供樣板,則它們必須使用 Twig。除非套件提供完整的工作應用程式,否則不得提供主版面配置。
設定
為了提供更大的靈活性,套件可以使用 Symfony 內建機制來提供可配置的設定。
對於簡單的配置設定,請依賴 Symfony 配置的預設 parameters
條目。Symfony 參數是簡單的鍵/值對;值可以是任何有效的 PHP 值。雖然這只是一個最佳實務建議,但每個參數名稱都應以套件別名開頭。參數名稱的其餘部分將使用句點 (.
) 分隔不同的部分(例如 acme_blog.author.email
)。
最終用戶可以在任何配置文件中提供值
1 2 3
# config/services.yaml
parameters:
acme_blog.author.email: 'fabien@example.com'
從容器中檢索程式碼中的配置參數
1
$container->getParameter('acme_blog.author.email');
雖然此機制需要最少的精力,但您應考慮使用更進階的語義套件配置,以使您的配置更強大。
服務
如果套件定義了服務,則它們必須以套件別名作為前綴,而不是像在專案服務中那樣使用完全限定的類別名稱。例如,AcmeBlogBundle 服務必須以 acme_blog
作為前綴。原因是套件不應依賴諸如服務自動裝配或自動配置之類的功能,以免在編譯應用程式服務時造成額外負擔。
此外,不打算直接供應用程式使用的服務應定義為私有。對於公共服務,應從介面/類別到服務 ID 建立別名。例如,在 MonologBundle 中,從 Psr\Log\LoggerInterface
到 logger
建立別名,以便 LoggerInterface
類型提示可用於自動裝配。
服務不應使用自動裝配或自動配置。相反,所有服務都應明確定義。
提示
如果最終用戶不打算使用服務 ID,您可以通過在其前面加上點(例如 .acme_blog.logger
)將其標記為隱藏。這可以防止服務在預設 debug:container
命令輸出中列出。
另請參閱
您可以閱讀這篇文章,了解有關套件中服務載入的更多資訊:如何在套件內載入服務配置。
Composer 元數據
composer.json
檔案應至少包含以下元數據
name
- 包含供應商和簡短的套件名稱。如果您是代表自己而不是代表公司發布套件,請使用您的個人姓名(例如
johnsmith/blog-bundle
)。從套件簡短名稱中排除供應商名稱,並用連字符分隔每個單詞。例如:AcmeBlogBundle 轉換為blog-bundle
,AcmeSocialConnectBundle 轉換為social-connect-bundle
。 description
- 套件用途的簡要說明。
type
- 使用
symfony-bundle
值。 license
- 具有有效許可證標識符的字串(或字串陣列),例如
MIT
。 autoload
-
Symfony 使用此資訊來載入 bundle 的類別。建議使用 PSR-4 自動載入標準:使用命名空間作為鍵,以及 bundle 主要類別的位置(相對於
composer.json
)作為值。由於主要類別位於 bundle 的src/
目錄中1 2 3 4 5 6 7 8 9 10 11 12
{ "autoload": { "psr-4": { "Acme\\BlogBundle\\": "src/" } }, "autoload-dev": { "psr-4": { "Acme\\BlogBundle\\Tests\\": "tests/" } } }
為了讓開發人員更容易找到您的 bundle,請將其註冊在 Packagist 上,Packagist 是 Composer 套件的官方儲存庫。