PHP扩展包的制作及对Laravel框架的集成

一、什么是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 主要由三个部分组成:命令行工具、包仓库、代码库
PHP扩展包的制作及对Laravel框架的集成_第1张图片

  • 包仓库(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 (/) [wyq/test]:
    Description []: test
    Author [wyq , 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 的 服务容器 中,并且告诉 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来获取你的私有扩展包。

首先,我们来介绍一般场景下开源项目扩展包的发布流程:

  1. 创建github代码仓库
    此步骤不做描述

  2. 提交到 Packagist
    Composer 安装包都是从 Packagist 源读取信息的,所以我们需要去注册我们的扩展包,别人才能安装,如果你还没有 Packagist 账号,先注册一个,建议使用 GitHub 登录:
    PHP扩展包的制作及对Laravel框架的集成_第2张图片
    登录以后,点击顶部菜单栏 “Submit” 开始提交项目,填入我们 代码所在的 GitHub 的仓库 URL,然后点 “Check”,然后提交即可:
    PHP扩展包的制作及对Laravel框架的集成_第3张图片

    新版 GitHub 已经不需要手动注册 webhook 了,当然前提是你在 packgist.org 使用github账号登录授权。
    //@TODO 由于写此文时主要是私有包仓库建设,所以关于github的部分没有实际验证,如有问题后续会补充。

  3. 在项目中使用

    在packgist.org发布后,直接在项目中require即可:

    composer require wyq/laravel-onelogin
    

如果我们不想让所有人都能使用我们的扩展包,则需要使用私有仓库,让拥有私有仓库访问权限的人才能正常获取扩展包:

  1. 创建私有git仓库
    此过程不做描述

  2. 对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 部分

  3. 在项目中使用
    当我们添加了新的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"
        }
    ]

参考文档

  • Composer官方文档-command部分
  • Composer官方文档-composer.json配置部分
  • Composer官方文档-repositoriees部分
  • Composer官方文档-使用satis搭建私有仓库

你可能感兴趣的:(php)