一、什麼是php擴展包?
我們用於項目增強有兩種方式:
-
PHP 原生擴展
PHP 原生擴展(PHP Native Extension),我們通常指基於 C/C++ 語言開發的對 PHP 語言的擴展,需要編譯安裝,比如我們最常使用的 phpredis、GD、MySQL、cURL 擴展等,這裏有一個 PHP 的擴展列表。 -
PHP 擴展包
PHP 擴展包(PHP Package)或者 PHP 包,我們通常指用 PHP 代碼編寫的代碼包。它通常是一些特定功能的封裝,比如Intervention/image
,它是基於 PHP 圖像處理庫GD/Imagick
實現的圖像處理功能包,具有非常強大且優雅的圖片處理 API,我們可以非常便捷的基於它完成常規的圖片操作,簡化開發工作。
本文要討論的就是 PHP 擴展包的開發,而不是 PHP 擴展。
二、Composer基礎介紹
在製作php擴展包之前,需要對php的包管理工具composer有一定了解。簡單來說,composer是php的包管理工具,可以類比於其他語言來理解它的作用:
語言 | 包管理工具 |
---|---|
PHP | composer |
NodeJs | npm |
Java | maven |
Go | govendor |
1、基本組成
Composer 主要由三個部分組成:命令行工具、包倉庫、代碼庫:
-
包倉庫(Packagist)
它是官方倉庫,也就是我們平常說的 Composer 源,它的作用是存儲這些包的信息,版本,代碼來源,依賴,作者,主頁等信息。官網是 packagist.org, 你也可以將自己的包發佈在上面,這樣 Composer 工具就能搜索與安裝你的包了。 -
代碼倉庫(Repository)
代碼倉庫,Packagist 支持公開與私有倉庫,通常是 GitHub 作爲代碼倉庫,當然也可以是 BitBucket 或者 GitLab。 -
本地存儲路徑(Vendor directory)
我們的 Composer 依賴包都統一安裝在項目的vendor
目錄下,其中還有vendor/composer
目錄用於存儲依賴包的一些基本信息,比如命名空間等。
2、命令行工具的基礎使用
我們安裝依賴的時候,composer 命令行工具會向包倉庫發起請求,請求需要安裝的包以及它依賴包的信息,它的依賴,版本等,然後在本地檢查依賴關係,檢查完畢後根據包信息裏的代碼庫地址(或者壓縮包地址)進行下載,下載到本地後安裝到 vendor
目錄。
Composer 命令在本文中不做贅述,相信大家都已熟練掌握,如有需要可查閱我的另一篇文章 Composer 基本命令介紹,更多指令可查閱 官方文檔。
三、php擴展包的開發(composer包的製作)
此章節將以 laravel-onelogin 項目爲例穿插講解
1、基本目錄結構
寫代碼我們習慣按照一定的規律安排目錄結構,規範的目錄結構有助於我們擴展項目,也有助於他人閱讀。
比如我們按這種結構設計項目:
your-composer-package/
├── .editorconfig # 編輯器配置文件,比如縮進大小、換行模式等
├── .gitattributes # git 配置文件,可以設計導出時忽略文件等
├── .gitignore # git 忽略文件配置列表
├── .php_cs # PHP-CS-Fixer 配置文件
├── README.md # 說明文檔
├── composer.json # composer依賴文件
├── phpunit.xml.dist # 單元測試xml文件
├── src # 源代碼目錄
│ └── .gitkeep
└── tests # 單元測試代碼目錄
└── .gitkeep
-
src 目錄
通常我們將源代碼放置到此目錄下,文件名與類命名遵循駝峯命名法,目錄與命名空間一致。注意:我們命名空間通常是按包名來的,然後 src 映射到駝峯寫法的命名空間,代碼組織結構請符合 PSR-4 規範。
-
tests 目錄
用於存放單元測試或者功能測試的測試用例代碼,與 src 組織規則基本一致。 -
.editorconfig 文件
EditorConfig 的配置文件,EditorConfig 是一套用於統一代碼格式的解決方案,很多項目都有用到,比如 Laravel、jQuery、Underscore 和 Ruby 等等。EditorConfig 可以幫助開發者在不同的編輯器和 IDE 之間定義和維護一致的代碼風格。EditorConfig 包含一個用於定義代碼格式的文件和一批編輯器插件,這些插件可以讓編輯器讀取配置文件並依此格式化代碼。EditorConfig 的配置文件十分易讀,並且可以很好的在 VCS(Version Control System)下工作。
簡單的說就是,這個配置文件定義了一些規則,比如 PHP 縮進是用空格還是用 Tab。它會被現代的編輯器所識別並應用(部分編輯器可能需要安裝對應的插件,請參考 EditorConfig 官網 )使用。
-
.gitattributes
Git 的屬性配置文件,你可以對個別文件或目錄定義不同的合併策略,讓 Git 知道怎樣比較非文本文件,在你提交或簽出前讓 Git 過濾內容。你將在這部分瞭解到能在自己的項目中使用的屬性,以及一些實例。更多請參考:《自定義 Git - Git 屬性》- git-scm.com -
.gitignore
Git 忽略文件列表配置文件,將不需要納入版本控制的文件或者目錄按行配置在該文件即可。 -
.php_cs
代碼格式修復工具 PHP-CS-Fixer 配置文件,它可以按配置的標準自動修復代碼格式,以及統一文件頭註釋等非常多的功能。 -
README.md
項目說明文檔,一份項目介紹與使用指引,維護狀態授權方式等。 -
composer.json
Composer 配置文件。 -
phpunit.xml.dist
PHPUnit 配置文件,指定測試目錄與測試環境變量等,具體內容請參考官方文檔:《組織測試:用 XML 配置來編排測試套件》- P…。 -
.gitkeep
如果一個目錄爲空,我們是無法納入到版本控制中的,所以我們創建了一個隨意命名(最好還是按業界通用做法命名爲 .gitkeep)的隱藏文件來保證 目錄不爲空。
下面介紹幾種初始化composer包的方式:
-
composer init
composer init 指令可以幫助我們初始化生成composer.json
文件。$ composer init Welcome to the Composer config generator This command will guide you through creating your composer.json config. Package name (<vendor>/<name>) [wyq/test]: Description []: test Author [wyq <[email protected]>, n to skip]: Minimum Stability []: Package Type (e.g. library, project, metapackage, composer-plugin) []: library License []: mit Define your dependencies. Would you like to define your dependencies (require) interactively [yes]? yes Search for a package: Would you like to define your dev dependencies (require-dev) interactively [yes]? no { "name": "wyq/test", "description": "test", "type": "library", "license": "mit", "authors": [ { "name": "wyq", "email": "[email protected]" } ], "require": {} } Do you confirm generation [yes]?
指引流程完成後當前文件夾會增加
composer.json
文件。其他目錄結構或文件需要手動創建。 -
package-builder 工具
我們在開發過程中每次都去建立這個目錄會比較麻煩,我們可以使用包結構生成工具來完成這些基礎工作:overtrue/package-builder安裝:
composer global require "overtrue/package-builder" --prefer-source
使用:
package-builder build [目標目錄]
-
Bootpack 工具
Bootpack 是 Laravel 5 包啓動器,它會生成更詳細的與Laravel結構相似的目錄結構:packages/LaravelNews └── example ├── composer.json ├── LICENSE ├── README.md └── src ├── Assets │ └── README.md ├── Classes │ ├── ExampleClass.php │ └── README.md ├── Commands │ ├── ExampleCommand.php │ └── README.md ├── Config │ └── example.php ├── Contracts │ ├── ExampleContract.php │ └── README.md ├── Controllers │ ├── ExampleController.php │ └── README.md ├── ExampleServiceProvider.php ├── Middleware │ ├── ExampleMiddleware.php │ └── README.md ├── Migrations │ ├── 2017_08_11_171401_create_Example_table.php │ └── README.md ├── Routes │ ├── api.php │ ├── README.md │ └── web.php ├── Translations │ ├── en │ │ └── basic.php │ └── README.md └── Views ├── README.md └── sample.blade.php
在此不做過多介紹,可查閱 bootpack 倉庫 來獲取更多關於 Bootpack 的信息。
2、示例
簡單來說,我們只需要像平時封裝service類一樣,編寫代碼,將功能封裝起來即可。composer包實際上相當於提供了自動加載和方便版本控制,結合代碼倉庫方便託管和共享,讓我們在不同項目中複用代碼時,不用再手動粘貼文件,讓我門的代碼能夠以更規範且方便的形式提供給他人使用。
【此章節主要以線下分享、演示爲主】
3、對 Laravel 框架的集成
在 Laravel 應用的 config/app.php
配置文件中,providers
選項定義了能夠被 Laravel 加載的服務提供者列表。當有人安裝你的擴展包時,你需要將你的服務提供者包含到這個列表中。你可以將服務提供者定義到擴展包的 composer.json 文件中的 extra 部分,而不是讓用戶手動將你的服務提供者添加到列表中。
"extra": {
"laravel": {
"providers": [
"Wyq\\Onelogin\\Providers\\OneLoginServiceProvider"
]
}
}
服務提供者負責將一些東西綁定到 Laravel 的 服務容器 中,並且告訴 Laravel 從哪裏加載擴展包的資源文件,例如路由、配置文件、視圖、語言包等。
以 wyq/laravel-onelogin
包爲例,我們在composer的extra項中設置了 OneLoginServiceProvider
服務提供者,讓他在自動加載時被發現,之後被框架註冊並執行。在 register
方法中,我們讓默認配置被加載到框架中。在 boot
方法中,我們讓路由自動註冊到框架中,並且提供了配置發佈功能:
class OneLoginServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
// 讓i1默認配置合併到框架中
$this->mergeConfigFrom(
__DIR__.'/../config/i1.php', 'i1'
);
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//自動註冊路由
$this->loadRoutesFrom(__DIR__.'/../routes.php');
//發佈配置
$this->publishes([
__DIR__.'/../config/i1.php' => config_path('i1.php'),
], 'config');
}
}
上邊的示例只用到了 config、route 資源的註冊,此外還提供了數據庫遷移、語言包、視圖、命令、公共資源文件、發佈羣組文件等,他們的使用方式都是相似的,都十分簡單。
更多關於laravel提供的資源文件註冊方式,可以查閱官方文檔:擴展包開發
四、包倉庫介紹及私有倉庫搭建
當我們PHP擴展包完成後,就可以嘗試發佈上線,在其他項目中使用它。
你可以選擇上傳到github,或者其他git倉庫託管平臺,如gitee、coding、gitlab等。但是值得注意的是:composer官方倉庫 Packagist 是以github作爲代碼倉庫的。也就是說,如果你選擇github託管代碼,可以很方便創建composer倉庫。一般情況下,開源項目我們強烈推薦選擇github,配合Packagist發佈自己的擴展包,但如果不希望代碼外泄,則需要使用私有git倉庫,或者自建composer倉庫,然後通過配置repository來獲取你的私有擴展包。
首先,我們來介紹一般場景下開源項目擴展包的發佈流程:
-
創建github代碼倉庫
此步驟不做描述 -
提交到 Packagist
Composer 安裝包都是從 Packagist 源讀取信息的,所以我們需要去註冊我們的擴展包,別人才能安裝,如果你還沒有 Packagist 賬號,先註冊一個,建議使用 GitHub 登錄:
登錄以後,點擊頂部菜單欄 “Submit” 開始提交項目,填入我們 代碼所在的 GitHub 的倉庫 URL,然後點 “Check”,然後提交即可:
新版 GitHub 已經不需要手動註冊 webhook 了,當然前提是你在 packgist.org 使用github賬號登錄授權。
//@TODO 由於寫此文時主要是私有包倉庫建設,所以關於github的部分沒有實際驗證,如有問題後續會補充。 -
在項目中使用
在packgist.org發佈後,直接在項目中require即可:
composer require wyq/laravel-onelogin
如果我們不想讓所有人都能使用我們的擴展包,則需要使用私有倉庫,讓擁有私有倉庫訪問權限的人才能正常獲取擴展包:
-
創建私有git倉庫
此過程不做描述 -
對composer.json文件增加repository
"repositories": [ { "type": "vcs", "url": "[email protected]:wyq/laravel-onelogin.git" } ]
其中 repositories 可以是數組,也可以命名個key,如下:
"repositories": { "laravel-onelogin": { "type": "vcs", "url": "[email protected]:wyq/laravel-onelogin.git" } }
需要注意的是,repository 支持多種 type 類型,上文使用的
vcs
即 version control system,下文會介紹另一種type:composer
類型,完整信息可見官方文檔 Repositores 部分 -
在項目中使用
當我們添加了新的repository之後,composer在官方倉庫找不到對應的擴展包時,會繼續依次使用配置的源來尋找擴展包,因此我們依然是直接執行require
指令即可:composer require wyq/laravel-onelogin
私有包倉庫搭建
上邊我們介紹了私有擴展包如何使用,但是我們使用vcs倉庫時會面臨一個問題:每增加一個擴展包,就要增加一條repositries的倉庫。
這時,我們可以通過搭建包倉庫來管理擴展包。這裏我們可以選擇satis或toran他們都是官方提供。
satis
Satis 是開源的,它只是一個靜態的 Composer 儲存庫生成器。它有點類似於一個超輕量的 packagist , 可以用於託管公司的私有 packages 或者 保存自己的私有 packages 。可以從 GitHub 下載 或者 在命令行運行以下命令:
php composer.phar create-project composer/satis --stability=dev --keep-vcs
創建完成後,如果目錄中沒有satis.json文件,則需要手動創建:
{
"name": "huanqiu/repository",
"homepage": "http://repository.demo.com",
"repositories": [
{
"type": "vcs",
"url": "[email protected]/wyq/laravel-onelogin.git"
}
],
"require": {
"wyq/laravel-onelogin": "*"
}
}
更完整的配置示例:
{
"name": "Easy Repository",
"homepage": "http://packagist.satis.cc",
"repositories": [
{"type": "vcs","url": "https://gitlab.local.com/aBigPackage/helloWorld"},
{"type": "composer", "url": "https://packagist.laravel-china.org"}
],
"archive": {
"directory": "dist",
"format": "tar",
"skip-dev": true,
"prefix-url": "http://packagist.satis.cc"
},
"abandoned":{
"lastcraft/simpletest" : "simpletest/simpletest"
},
"require":{
"monolog/monolog": "*",
"darkaonline/l5-swagger": "~5.4",
"laravel/laravel":"~5.4",
"league/flysystem-aws-s3-v3":"*",
"zircote/swagger-php":"*",
"simpletest/simpletest":"*"
},
"require-all": false,
"require-dependencies": true,
"require-dev-dependencies": true
}
配置解釋:
// Composer 私有源的名稱,可隨意
"name": "Easy Repository",
// 建立之後home頁面的地址(用於查看這個源有哪些package)
"homepage": "http://packagist.satis.cc",
// 獲取package的地址
/**
這裏如果你是需要從私有的git源獲取package的話,就參照如下這樣寫就可以了
(帶.git和不帶.git似乎都ok)
{"type": "vcs","url": "https://gitlab.local.com/aBigPackage/helloWorld"}
如果你是需要構建內網的源,且內外網分離的情況下從外網獲取package到內網,就參照下面這樣寫就好了
(沒有必要挨個去寫需要引用的package的github地址)
{"type": "composer", "url": "https://packagist.laravel-china.org"}
*/
"repositories": [
{"type": "vcs","url": "https://gitlab.local.com/aBigPackage/helloWorld"},
{"type": "composer", "url": "https://packagist.laravel-china.org"}
],
//如果需要satis將package下載到本地,直接從本地拉取,則需要配置這一項
//(內網源必須配置此項)
"archive": {
"directory": "dist",
//tar or zip
"format": "tar",
//是否需要爲分支創建下載(默認只對有tag的提交創建下載)
"skip-dev": false,
"prefix-url": "http://packagist.satis.cc"
},
// 被拋棄或替換的package
"abandoned":{
//true表示這個 package 真正的被拋棄
"lox/simpletest":true
//表示 lastcraft/simpletest 被 simpletest/simpletest 替換
"lastcraft/simpletest" : "simpletest/simpletest"
},
// 需要 satis 的全部的 package
"require":{
"monolog/monolog": "*",
"darkaonline/l5-swagger": "~5.4",
"laravel/laravel":"~5.4",
"league/flysystem-aws-s3-v3":"*",
"zircote/swagger-php":"*",
"simpletest/simpletest":"*"
},
//是否需要將配置的源的全部的package都拉取
"require-all": false,
//是否自動解決依賴
"require-dependencies": true
//是否自動解決dev依賴
"require-dev-dependencies": true
倉庫構建:
// 全部需要的package重新檢查更新並構建
// 強烈推薦追加 --skip-errors 參數,否則碰到某些已經被放棄的 package 會卡住構建
php bin/satis build satis.json public/ --skip-errors
// 僅僅重新檢查更新並構建指定的幾個包
php bin/satis build satis.json public/ A/package B/other-package
// 如果只想掃描單個存儲庫並更新其中找到的所有包,請將VCS存儲庫URL作爲可選參數傳遞:
php bin/satis build --repository-url https://only.my/repo.git satis.json public/
當構建完成後,public目錄下會生成package.json文件,記錄所有擴展包的索引;include文件夾存儲壓縮包(如果設置了archive)
toran
Toran-Proxy是一套相對完整的私有倉庫管理和代理工具( https://toranproxy.com/ ),也是composer作者開發的,可靠性也有保證。相比Satis而言主要有以下幾個優勢:
- 提供了一套簡單的UI來管理包,可以直接通過表單提交和更新;
- 提供了一套簡單的API,可以被作爲持續集成的一部分,觸發添加、更新包等操作;
- 除了私有包管理外,還提供了官方packagist或其他代理源的內部代理功能,提升加載速度的同時,當packagist或其他代理源不能工作時,一定程度上保障服務可用性
關於toran的內容在此不做詳細介紹。
私有包倉庫使用
向 composer.json 文件的 repositories 增加新的倉庫源即可。
"repositories": [
{
"type": "composer",
"url": "https://repository.demo.com/composer"
}
]