如何发布自己的composer包?
Table of Contents
composer简介
是PHP用来管理依赖(dependency)关系的工具。你可以在自己的项目中声明所依赖的外部工具库(libraries),composer 会帮你安装这些依赖的库文件,你也可以写自己的工具库让别人下载使用。
其实composer就是一个跟js的npm
,RedHat/CentOS的yum
,Ubuntu的apt/apt-get
,macOS的brew
类似的工具。
安装composer
composer工具的本质
composer其实是用php写的一个php包管理工具(官方叫依赖管理工具),它的文件名叫composer.phar
,phar是PHp ARchive的缩写,中文直译可以称为“php归档文件”,是的一种包封装技术(其实就是一种压缩包),类似java的.jar
文件。
我们可以把composer.phar解压出来看看,在终端进入composer.phar
所在目录,执行以下代码即可解压:
php -r "(new Phar('./composer.phar'))->extractTo('composer');"
解压完之后,你就能看到它的源码就是php,特别是它的bin/composer
文件,并没有后缀,你可能会以为它就是一个二进制可执行文件,但其实它就是一个php写的程序,只不过没有以.php
结尾而已。
如果在phpstorm看就更简单,直接可以看,都用不解压:
这也就是为什么很多文档里使用composer的时候,是这样用的,比如安装monolog:
# 安装monolog
php composer.phar require monolog/monolog
但是你去Packagist看,它的安装命令是这样的:
composer require monolog/monolog
所以以上两句命令中的composer.phar
跟composer
有什么不同呢?其实没什么不同,composer
就是composer.phar
,看官方文档的全局安装,如下图,它是直接把composer.phar
移动到/usr/local/bin/
目录下,并且是重命名为composer
了,即把后缀.phar
去掉了:
因为/usr/local/bin/
肯定是在环境变量里,所以在终端哪个位置都能执行composer
了(即可以全局执行了)。
可是,php写的包,为什么不用php去执行它也可以呢??比如我们执行一个php文件,肯定得php index.php
这样执行它呀,直接./index.php
是不行的,但真的不行吗?其实并不是不可以,我们可以看看shell脚本和python是怎么做的!
把以下代码保存成test.sh
,并赋予权限chmod 755 test.sh
,然后用./test.sh
运行:
#!/bin/sh
echo "this is a test!"
其实,你不写#!/bin/sh
,只写代码,最后用bash test.sh
一样可以执行的。
python脚本也一样:
#!/usr/bin/env python
print('this is a test!')
你在里面写了#!/usr/bin/env python
,并且赋予了执行权限,那就可以用./test.python
这样运行,但如果你不写#!/usr/bin/env python
,也是可以直接python test.python
这样执行的。
php也是同理,里面也可以写#!/usr/bin/env php
,并且赋予权限,就可以用./test.php
这样执行的。
还有人会觉得,为什么去掉.phar
后缀,也能执行呢?其实稍微有点Linux常识的童鞋应该都知道,后缀在Linux里只是给人看的,机器是完全不看后缀的,比如php test.php
,你完全可以把.php
去掉,变成php test
,一样可以执行的,又比如test.tar.gz
,你去掉.tar.gz
,一样可以用tar -zxf test
解压,只不过没有后缀,我们人就不知道它是什么文件,所以要加后缀。
我认为composer能直接执行是因为有这个stub.php
的原因,应该是.phar
包有一定要机制自动从stub.php
这里开始执行吧:
Mac/Linux安装composer
手动全局安装composer用以下:
# 用curl下载installer(它是个php写的文件),然后通过管道符交给php执行,其实也就是下载composer.phar
curl -sS https://getcomposer.org/installer | php
# 把composer.phar移动到/usr/local/bin/目录下,并且重命名为composer
mv composer.phar /usr/local/bin/composer
这种手动安装的方法,Mac和Linux都是可以的,不过Mac有更简单的安装方法:
brew install composer
其实原理是一样的,无非就是下载composer.phar
并放到合适的地方,然后加入到环境变量里。
Windows安装composer
手动方式安装:
由于Windows不能像Mac/Linux那样用#!/usr/bin/env php
的方式去指定执行,所以在Windows上的composer本质还是需要用php composer.phar
这样的方式去执行的,但我们也可以配置成全局的composer命令,其原理,就是通过批处理文件去执行php composer.phar
这个命令。
具体做法,在适当的目录新建一个名为composer
的文件夹,下载最新版的composer.phar
放到这个文件夹中,再在该文件夹下新建一个composer.bat
文件,文件的内容如下:
@D:\phpstudy_pro\Extensions\php\php7.3.4nts\php.exe "%~dp0composer.phar" %*
其中这个是php.exe
的路径:
D:\phpstudy_pro\Extensions\php\php7.3.4nts\php.exe
%~dp0
中的dp表示drive path,磁盘路径,%~dp0
就是指当前目录,相当于php的__DIR__
%*
表示参数,%0
是第一个参数,%1
是第二个参数,%2
表示第三个参数,……
最后把composer.bat
添加到环境变量中即可(点击图片可放大),注意添加后每个框都要点确定:
添加后,不用重启系统,直接就可以在cmd终端里输入composer
命令(但是如果你cmd窗口是在添加环境变量前就打开了的,就必须关闭后再打开,因为只有重新打开才会读取你刚刚设置的环境变量),回车,即可看到composer
命令添加成功了
有人可能会有疑问,按道理来说,添加的是composer.bat
所在的路径,输入的时候应该输入composer.bat
才对呀,事实上,你输入composer.bat
效果也是跟输入composer
命令是一样的,而且其本质就是执行composer.bat
文件。
至于为什么直接输入composer
命令也可以,经过测试,无论是.bat
文件还是.exe
文件,只要添加到环境变量了,都是只需要输入文件名而不用输入后缀名就能执行,当然你输入了后缀名也一样可以执行,这个应该是windows对它可以执行的文件自动处理了。
其实前面那个php的路径,也可以用前面的方法加入到环境变量,这样composer.bat里就可以像下边这样写,就简单多了:
@php "%~dp0composer.phar" %*
可执行文件方式安装:
下载Composer-Setup.exe,然后自己安装即可。
创建自己的composer包
只要你的代码文件夹里有一个composer.json
文件,那么这个文件夹就是一个composer包了,关键就在于composer.json要怎么写,最简单的方法,其实就是看一看现有项目中的vendor里安装的包,看它们的composer.json是怎么写的,看它们都是什么结构,多看几个,模仿模仿就清楚了。
composer包的结构
包名的格式是“供应商名称/项目名”,比如阿里云对象存储的php-sdk:oss-sdk-php,它的包名是“aliyuncs/oss-sdk-php”,其中“aliyuncs”是供应商名称,“oss-sdk-php”是项目名称。
供应商名(文件夹) # 供应商一般是公司名(个人项目可写github名或与包名相同的名称)
└── 包名(文件夹) # 即你的项目名称
├── LICENSE # 版权文件,非必须,但如果没特殊版权一般都建议用MIT协议
├── README.md # 使用文档,非必须,但基本上都要有比较好,你没文档人家不知道怎么用
├── composer.json # 必须,一个包它是不是composer包,就靠这个文件
└── src(文件夹) # 源码文件夹,非必须,如果没有文件夹直接在包名文件夹下也可以
└── tests(文件夹) # 测试代码文件夹,里面放测试类、测试代码
在包名文件夹下,还可以写自己想写的文件,上边没有列出,因为每个人想写的不一样,所以没固定规则,比如CHANGELOG.md
,又比如有中英文文档的,除了README.md
,还会有README-CN.md
等等。
文件夹结构如下图所示:
源码可以直接放在src
目录下,也可以以一个文件夹的形式放在src目录下,而且很多包都这么做(原因可能是以前已经写好了,只不过是要搬到composer上来,所以把整个文件夹复制到src里即可),比如:
阿里云的对象存储php-sdk:
腾讯云对象存储php-sdk:
七牛云对象存储php-sdk:
创建一个composer包
如果想要别人能用composer下载你的包,那么你必须把你的包上传到Packagist上,而Packagist是无法直接上传的,一般情况下都是从github同步过去的,所以我们要把我们写好的包上传到Github,而想要把写好的代码上传到github,最好是先在github上创建一个项目再克隆下来,添加代码后再提交,这样是最方便的,当然也有另一种方法,请查看:怎样把一个新项目/本地项目提交到github?
在github创建项目特别简单,如下图所示,点击右上角的+号,点击New repository
,然后按下图的提示填写,最后点击Create repository
即可创建:
上图创建成功后来到这里,这里已经有项目的git地址了:
根据上边得到的git项目地址,在本地你想保存代码的地方git clone
下载(我是在~/www/personal/
目录下执行以下代码的):
git clone https://github.com/xiebruce/learn-composer.git
如下图所示:
在上面git clone
下来的“learn-composer/”目录里创建src
和tests
两个目录,并创建一个.gitignore
文件,然后在src
目录下创建一个类文件HelloWorld.php
:
结构如下:
├── src # 源码目录
│ └── HelloWorld.php # 源码类文件
├── tests/ # 测试代码目录
├── .gitignore # git忽略文件
└── LICENSE # 版权文件
└── README.md # 说明文档
HelloWorld.php
类里的代码如下:
<?php
/**
* Created by PhpStorm.
* User: Bruce Xie
* Date: 2019-09-09
* Time: 12:31
*/
// 关于这个命名空间名称,你可以随便写。当然,如果src目录下还有一层目录,则一般都写这层目录的名字。
// 如果没有,也可以写公司简称,即“供应商名/项目名”中的“供应商名”,如果是个人项目,可以写github用户名,
// 这里我就随便写一个“pack”,这样更好的说明这是可以随意写的。
namespace pack;
class HelloWorld {
public function sayHello(){
echo 'Hello world!';
}
public static function sayHelloStatic(){
echo 'Hello world static';
}
}
如下图所示:
规则:
– 文件名必须是类名.php
,如文件名为HelloWorld.php
,则该文件里的类名一定是HelloWorld
;
– 类名用驼峰命名法,并且首字母大写,如HelloWorld
(驼峰命名法跟首字母大写都不是强制,而是一种规范,也就是你不按驼峰命名法,首字母不大写,程序一样可以运行)。
初始化为composer包
上边只是写了代码,但它还不是composer包,因为它缺少一个composer必须的文件composer.json
,创建composer.json
文件有两种方法:
- 方法一:自己新建文件
composer.json
文件,然后自己写; - 方法二:用
composer init
命令创建。
方法一需要你知道怎么写一个composer.json,一般可以从别人的包里拷贝一个过来,再改改就可以。
方法二相当于是一个向导,以下我们用向导创建。在终端中进入项目文件夹learn-composer/
,运行composer init
即会开启向导,如下图所示:
它要你填的信息有以下信息(这些信息有哪些值可以查看:composer.json 架构):
- Package name,即包名,前面说过,composer包名格式为
供应商名/项目名
,它的默认值,就是当前电脑用户名/项目名
。一般来说,如果是公司项目,供应商名就是公司英文简称或拼音之类的,比如亚马逊的aws(Amazon Web Services)、阿里云的aliyuncs(aliyun cloud services)、腾讯云的qlcoud(q我不知道意思,我觉得是qq的意思)、网易云的netease、七牛云直接就是七牛的拼音qiniu,如果是个人项目,一般你可以直接用你github上的用户名,因为这样能让你的包跟github名对应,; - Description,项目描述,简单的说就是你这个项目是干嘛用的,不用太长,简述就行;
- Author,项目作者,我这里是自动提示是否使用给出的姓名和邮箱,我也不知道它为什么知道我的姓名和邮箱的;
- Minimum stability,最低稳定版本,它有
dev
(开发版)、alpha
(内测版)、beta
(公测版)、RC
(Release Candidate,即候选发布版)、stable
(正式发布版,即稳定版),具体可查看Package links以及理解Composer的稳定性(Stability)标识; - Package Type,一般情况下都是填
library
(不填则默认是library
),这种包类型,用户使用composer require xxx/xxx
的形式安装后,会自动放在vendor/
目录下,这也是我们最常用的类型。 具体可查看:安装类型 type; - License,版权协议,下图是阮一峰博客的图:
- Define your dependencies,定义你这个包依赖于哪些包,这个填no就行,因为如果有依赖的话,我们可以自己修改
composer.json
;
最后它会显示出一个json,问你是否确定要生成,输入yes,回车,即可生成一个composer.json如下:
{
"name": "xiebruce/learn-composer",
"description": "This is a test.",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Bruce Xie",
"email": "[email protected]"
}
],
"minimum-stability": "dev",
"require": {}
}
我们可以看到,其实这个composer.json
非常简单,不用composer init
,自己新建自己从别的地方复制一个来改改也行。
但其实这个composer.json还无法让你这个项目成为一个“composer包”,因为最重要的自动加载(autoload)没有写,我们加上autoload和require,变成这样:
{
"name": "xiebruce/learn-composer",
"description": "This is a test.",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Bruce Xie",
"email": "[email protected]"
}
],
"minimum-stability": "dev",
"require": {
"php": "7.0"
},
"autoload": {
"psr-4": {"pack\": "src/"}
}
}
require
就是指这个包要依赖什么,目前暂时写php需要大于7.0,其它的没有依赖。
autoload
是关键中的关键,composer的工作原理就是靠这个。psr-4
是PHP Standard Recommendations-4(第四条推荐规范),这个是推荐规范,并非标准,不过也跟标准差不多了。
{"pack\": "src/"}
表示pack\
命名空间对应src/
目录,pack\
中两个反斜杠是因为反斜杠本身是有特殊意义的,表示“转义”,如果要表示反斜杠本身就得再加一个反斜杠,相当于前面的反斜杠把后面的反斜杠转义成普通反斜杠。
写好composer.json后,在终端中进入项目目录,我这里是“~/www/personal/learn-composer”,运行:
composer install
运行之后,就会多一个vendor
目录,vendor目录结构如下:
vendor
├── autoload.php
└── composer
├── ClassLoader.php
├── LICENSE
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
├── autoload_static.php
└── installed.json
其中的autoload.php
就是我们要使用vendor中的各种包时要引用的文件。
现在我们在tests
目录建一个测试文件test.php
,里面代码为:
<?php
/**
* Created by PhpStorm.
* User: Bruce Xie
* Date: 2019-09-09
* Time: 12:48
*/
// 引入autoload.php文件,用于自动加载vendor中的库
require '../vendor/autoload.php';
// use HelloWorld类
use packHelloWorld;
// 实例化HelloWorld类
$obj = new HelloWorld();
// 使用对象调用sayHello()方法
$obj->sayHello();
echo PHP_EOL . "--------" . PHP_EOL;
//使用类名调用sayHello()方法(静态调用)
HelloWorld::sayHelloStatic();
如图所示:
在这里我们可以不使用网页来测试(当然你用网页也一样的),直接在终端进入tests/
文件夹,然后执行:
php test.php
输出结果(说明调用成功):
Hello world!
--------
Hello world static
到这里就已经创建好一个包了,现在就可以提交了,提交前可以去看看.gitignore
文件,把/vendor/
和/composer.lock
添加到.gitignore
文件中,表示/vendor/
整个目录和/composer.lock
文件都不提交到github(因为在用composer下载的时候vendor会自动生成的):
/vendor/
/composer.lock
如果是真实项目,还要把使用文档在README.md
文件中写一下,最后把这个包推送到github。
操作截图(点击图片可放大):
推送到github后,刷新一下github,代码已经在这里了:
把代码从github同步到Packagist
进入Packagist,先登录(可以用github账号登录),然后点击右上角的Submit
,填上前面提交的github地址,点击Check
:
如果有检测到有类似的包,它会显示出来,如下图所示,我这个包就有两个一样名字的,当然只是项目名相同,因为包名是用户名/项目名
,所以包名肯定是不同的,最后点Submit
,它就会自动从github克隆到Packagist中:
上图稍等片刻即会跳转到这里,表示已经同步过来了:
每次更新代码到github后,需要手动点击一下上图中的Update
按钮,把Github的更新同步过来。
设置自动从Github更新到Composer
即设置每次你git push
把新代码上传到github时,让github自动把这些更新的代码推送到Packagist中,这样你就只管更新github就好了,否则的话,你每次更新github代码后,还要到Packagist中手动点Update
。
在Packagist中查看你的包,如下图所示,点击GitHub Hook:
上图点击GitHub Hook,往下滑,找到下图所示的位置,即可看到如何设置自动从Github更新到Packagist的方法,红框中的参数,就是一会儿在Github那边要填的:
在Github上找到你要自动同步到Packagist的包,点击Settings
:
再点击Webhooks
→Add webhook
:
上图点击Add Webhook
后要输入Github登录密码:
上图输入密码后就会来到下图所示页面,其中的Payload Url
、Content Type
、Secret
、Which Event
就是前面GitHub Hook页面中的内容:
上面的Secret
就是这里的API Token:
点击Add webhook
添加后,就来到这个页面,这就表示添加成功了:
添加后,回去刚才Packagist这里(这是刷新前,可以看到有个提示“This package is not auto-updated”,意思是这个包不是自动更新的。):
刷新后(刷新后就没有那个提示了):
然后就可以试试git push
更新代码到Github后会不会自动更新到Packagist里,最简单的就是修改一下README.md
,推送到github后,去Packagist里刷新一下就可以看到是否已经更新过来。
下载使用
假设我有一个项目叫“test-learn-composer”,新建test-learn-composer
目录,在终端中进入该目录下,运行以下命令安装我们刚上传的“learn-composer”包:
composer require xiebruce/learn-composer
上边这句命令直接执行是报错的:
报错的原因是因为我们不指定版本它默认是稳定版本,所以以上命令其实相当于:
composer require xiebruce/learn-composer:stable
而我们提交的版本是dev-master的(如下图):
所以我们要指定这个版本才能下载:
composer require xiebruce/learn-composer:dev-master
在项目文件夹下,即“test-learn-composer”文件夹下新建一个index.php
,内容如下:
<?php
// 引入autoload.php文件,用于自动加载vendor中的库
require 'vendor/autoload.php';
// use HelloWorld类
use packHelloWorld;
// 实例化HelloWorld类
$obj = new HelloWorld();
// 使用对象调用sayHello()方法
$obj->sayHello();
echo PHP_EOL . "--------" . PHP_EOL;
//使用类名调用sayHello()方法(静态调用)
HelloWorld::sayHelloStatic();
其实就是把“learn-composer”里的test.php
复制过来,只不过把require autoload.php的相对路径换一下:
然后调用一下看是否正常:
php index.php
正常的话会像下图一样输出所调用的方法中echo的字符串:
如果想不用加:dev-master
,那就要打tag发版本:
git tag -a v0.1 -m "v0.1"
把tag推送到github:
git push origin v0.1
这样就可以用以下命令这样安装了:
composer require xiebruce/learn-composer
如果要删除tag:
#先删除本地
git tag -d v0.1
#再删除github
git push origin :refs/tags/v0.1
但其实这还有问题,就是composer.json里的“minimum-stability”的值,一开始我们设置的是dev
,现在要把它改成stable
,重新打tag再提交。
因为composer完整命令格式其实是这样的:
composer require 供应商名/项目名:分支名@最低稳定性
对应到真实的例子就是:
composer require xiebruce/learn-composer:dev-master@dev
平时我们安装的时候是不写:分支名@最低稳定性
的,分支名不写就默认当前最新版本,所以如果没有发版本(即没有打tag)的情况下你不写分支名它是会报错的(就跟上面那样),而“最低稳定性”如果不写,它默认是stable
,也就是说我们前面用的这个命令:
composer require xiebruce/learn-composer:dev-master
按道理应该相当于这样的:
composer require xiebruce/learn-composer:dev-master@stable
如果你不写@stable
,它会自动识别出dev-master是dev-
开头的,所以会认为你要下载的是dev
版本的。
像yii2的安装这种,其实就相当于git clone
,没啥区别,只不过库在packagist.org,但即使是在packagist.org,也是从github同步过去的(前面说过)
composer create-project yiisoft/yii2-app-advanced
上边的命令会把项目下载到yii2-app-advanced
目录里(事实上它本身就是这个目录),如果再加一个参数,就相当于把目录重命名,比如这样,就会下载到my-project-name
文件夹(不存在会自动创建)
composer create-project yiisoft/yii2-app-advanced my-project-name