插件开发 - 理论篇

QueryList支持安装插件来帮助丰富你的采集功能,目前收录的一些QueryList插件: https://github.com/jae-jae/QueryList-Community

如果大家在使用的过程中有在QueryList的基础上添加一些自己的功能,不妨做成一个QueryList插件,这样可以给其它QueryList使用者提供帮助,并且在其它人的使用反馈中的提升插件的质量。

接下来会以开发一个MyHttp扩展为例,一步一步的开发一个扩展,并且发布给他人使用。

前言

我们知道可以轻松通过bind绑定自定义方法到QueryList对象,从而用来扩展QueryList的功能,像下面这样:

  1. //注册一个myHttp方法到QueryList对象
  2. QueryList::bind('myHttp',function ($url){
  3. $html = file_get_contents($url);
  4. $this->setHtml($html);
  5. return $this;
  6. });

遇到比较复杂的扩展情况,代码量比较多,如果都写到这里的话就很不优雅了,所以有一个优雅的做法,像下面这样:

  1. QueryList::bind('myHttp',function(){
  2. return new MyHttp($this);
  3. })

把代码独立到一个class中去,然后在这里引用,这样就优雅多了,而且移植性也强了,下次如果其他项目里面也需要用到,只需要把这个class拷贝过去,然后bind一下就行了。

但这样写还是不够完美,如果把你封装的功能开源给别人使用,你还得告诉别人该如何bind,用起来还是比较麻烦,有没有更简单的方法呢?当然有,那就是你替用户把bind过程也写好,这样用户只需要引入你的扩展文件就可以使用了。

实现

下面来看具体实现:

  1. class MyHttp
  2. {
  3. protected $ql;
  4. public function __construct($ql)
  5. {
  6. $this->ql = $ql;
  7. }
  8. //定义一个静态的install方法,用于安装你的扩展功能
  9. public static function install(QueryList $queryList)
  10. {
  11. //在这个方法中实现你的`bind`
  12. $queryList->bind('myHttp',function($url){
  13. return (new MyHttp($this))->get($url);
  14. });
  15. }
  16. //定义一个http get方法
  17. public function get($url)
  18. {
  19. $html = file_get_contents($url);
  20. $this->ql->setHtml($html);
  21. return $this->ql;
  22. }
  23. }

这样用户使用你的扩展功能只需要这样调用即可:

  1. MyHttp::install($ql);

用户不用关心任何细节。

插件

根据上面的推导,我们实际上就推导出插件的写法了,上面的MyHttp就是一个独立的插件了。QueryList实现了一个use方法用于安装插件:

  1. QueryList::use(MyHttp::class);
  2. //相当于执行了:MyHttp::install($ql);

QueryList插件需要实现QL\Contracts\PluginContract这个接口,接口源码:

  1. <?php
  2. namespace QL\Contracts;
  3. use QL\QueryList;
  4. interface PluginContract
  5. {
  6. public static function install(QueryList $queryList,...$opt);
  7. }

所以上面的MyHttp需要改成下面这样:

  1. <?php
  2. use QL\Contracts\PluginContract;
  3. use QL\QueryList;
  4. class MyHttp implements PluginContract
  5. {
  6. protected $ql;
  7. public function __construct($ql)
  8. {
  9. $this->ql = $ql;
  10. }
  11. //定义一个静态的install方法,用于安装你的扩展功能
  12. public static function install(QueryList $queryList,...$opt)
  13. {
  14. //在这个方法中实现你的`bind`
  15. $queryList->bind('myHttp',function($url){
  16. return (new MyHttp($this))->get($url);
  17. });
  18. }
  19. //定义一个http get方法
  20. public function get($url)
  21. {
  22. $html = file_get_contents($url);
  23. $this->ql->setHtml($html);
  24. return $this->ql;
  25. }
  26. }

一个完整的插件就这么诞生了!

然后你就可以这样使用:

  1. $ql = QueryList::use(MyHttp::class);
  2. $ql->myHttp('https://querylist.cc');
  3. echo $ql->getHtml();

插件安装参数


插件安装的时候可以让用户指定一些个性化参数,比喻提供让用户自定义bind的名字:

  1. public static function install(QueryList $queryList,...$opt)
  2. {
  3. //接受用户自定义name,默认name:'myHttp'
  4. $name = $opt[0] ?? 'myHttp';
  5. $queryList->bind($name,function($url){
  6. return (new MyHttp($this))->get($url);
  7. });
  8. }

然后用法就变成这样了:

  1. $ql = QueryList::use(MyHttp::class,'httpGet');
  2. $ql->httpGet('https://querylist.cc');
  3. echo $ql->getHtml();

复杂情况

编写一个高级的插件可能就不止一个class文件,也不止bind一个方法;这些情况QueryList都是支持的,对于多个class文件,只要主文件实现QL\Contracts\PluginContract接口即可,也可以同时bind多个方法:

  1. public static function install(QueryList $queryList,...$opt)
  2. {
  3. $name1 = $opt[0] ?? 'myHttpGet';
  4. $name2 = $opt[1] ?? 'myHttpPost';
  5. $queryList->bind($name,function($url){
  6. return (new MyHttp($this))->get($url);
  7. });
  8. $queryList->bind($name2,function($url,$params = []){
  9. return (new MyHttp($this))->post($url,$params);
  10. });
  11. }

使用:

  1. $ql = QueryList::use(MyHttp::class,'httpGet','httpPost');
  2. $ql->httpGet('https://querylist.cc');
  3. $ql->httpPost('https://querylist.cc',['param' => 'val']);
  4. echo $ql->getHtml();

互相依赖


一个插件可以依赖另一个插件,一个bind也可以依赖另一个bind:

  1. public static function install(QueryList $queryList,...$opt)
  2. {
  3. //安装一个其它的插件
  4. $queryList->use(Curl::class);
  5. $queryList->bind('myHttpGet',function($url){
  6. // 使用刚刚安装的插件
  7. return $this->curl()->get($url);
  8. });
  9. $queryList->bind('getAndEncode',function($url){
  10. //使用上一个bind的功能
  11. $html = $this->myHttpGet($url)->getHtml()
  12. $html = iconv('GBK','UTF-8',$html);
  13. $this->setHtml($html);
  14. return $this;
  15. });
  16. }

版本号约定


希望大家有一个规范的插件版本号,建议插件大版本号与QueryList大版本号对应,比喻你的插件的第一个版本号可以取为:4.0.0,然后更新的时候大版本号保持不变,依次递增小版本号。

快速上手

查看现有的QueryList插件源码是一个快速上手的好方式,QueryList插件Demo:https://github.com/jae-jae/QueryList-AbsoluteUrl

提交你的插件

欢迎提交你的插件,让更多人发现和使用:查看提交说明

QueryList插件列表:https://github.com/jae-jae/QueryList-Community

总结

说了这么多总结起来其开发一个QueryList插件非常简单,只需要实现一个install方法即可,然后在install方法中调用bind方法注入你的扩展功能即可。最后就可以做成Composer包然后推送到GitHub和Packagist上面,方便的通过Composer安装。