跳到內容

可重用套件的最佳實務

編輯此頁

本文全部關於如何建構您的可重用套件,使其可配置和可擴展。可重用套件是指那些旨在在許多公司專案中私下共享,或公開共享,以便任何 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 子命名空間中。

供應商

套件不得嵌入第三方 PHP 函式庫。它應改為依賴標準 Symfony 自動載入。

套件也不應嵌入以 JavaScript、CSS 或任何其他語言編寫的第三方函式庫。

Doctrine 實體/文檔

如果套件包含 Doctrine ORM 實體和/或 ODM 文檔,建議使用儲存在 config/doctrine/ 中的 XML 檔案來定義它們的對應。這允許使用標準 Symfony 機制來覆寫套件部分來覆寫該對應。當使用屬性來定義對應時,這是不可能的。

測試

套件應附帶使用 PHPUnit 編寫並儲存在 tests/ 目錄下的測試套件。測試應遵循以下原則

  • 測試套件必須可使用從範例應用程式運行的簡單 phpunit 命令執行;
  • 功能測試應僅用於測試響應輸出以及您可能擁有的一些分析資訊;
  • 測試應涵蓋至少 95% 的程式碼庫。

注意

測試套件不得包含 AllTests.php 腳本,但必須依賴 phpunit.xml.dist 檔案的存在。

持續整合

持續測試套件程式碼(包括其所有提交和提取請求)是一種稱為持續整合的良好實務。有幾項服務為開源專案免費提供此功能,例如 GitHub ActionsTravis CI

套件至少應測試

  • 其依賴項的下限(通過運行 composer update --prefer-lowest);
  • 支援的 PHP 版本;
  • 所有支援的主要 Symfony 版本(例如,如果聲稱同時支援 4.x5.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.rstdocs/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。除非套件提供完整的工作應用程式,否則不得提供主版面配置。

翻譯檔

如果套件提供訊息翻譯,則它們必須以 XLIFF 格式定義;網域應以套件名稱命名 (acme_blog)。

套件不得覆寫來自另一個套件的現有訊息。

設定

為了提供更大的靈活性,套件可以使用 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\LoggerInterfacelogger 建立別名,以便 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 套件的官方儲存庫。

資源

如果 bundle 引用任何資源(設定檔、翻譯檔等),您可以使用實體路徑(例如 __DIR__/config/services.xml)。

過去,我們建議僅使用邏輯路徑(例如 @AcmeBlogBundle/config/services.xml)並使用 Symfony kernel 提供的 資源定位器 來解析它們,但這不再是建議的做法。

本作品,包括程式碼範例,皆以 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本