SDK 开发高级版!揭开 Foundation-SDK 的神秘面纱
98

foundation-sdk 其实源自于超哥的 easyWeChat,从中提取了大量的设计理念,并且融入我对 SDK 的理解而诞生。

初级教程看这里 手把手教你开发 SDK

what's created by foundation-sdk

这里必须要列一下 foundation-sdk 在背后默默的为多少个 SDK 提供支持。

其实有点小失落,发现自己开发的 SDK 没有自己想象中那么多 T_T

file

总共有 11 个包依赖 foundation-sdk ,而我自己开发的有7个,安装了接近 3 K 次。

简介

根据本人对 SDK 的理解,此包融入了几大关键的 SDK 元素。

  • 容器
  • 配置
  • 日志
  • 缓存
  • 请求
  • token

上面并非必要元素,但都极有可能用上。

来看看 foundation-sdk 的目录
file

这是 GitHub 插件 https://github.com/buunguyen/octotree

文件意思大概都不用解释,这里重点是 高级用法

正片

Foundation

Foundation 是整个项目的核心,继承了 Pimple\Container,也就是一个容器。你所使用的 $foundation->order $foundation->config $foundation->user 等等都是因为这个是一个容器。

Foundation 中含有一个 $providers 服务提供者的数组属性。

服务提供者

这是 Foundation 的第二个精华所在,容器的最佳表现。接上面所说的,你可以在 Foundation 中注册多个服务提供者, 参考这里

新建的服务提供者需要实现 Pimple\ServiceProviderInterface,并补充完 register 方法,其实就是 new 一个类在 Foundation 中,参考这里

Config & Log & Cache

Foundation 的构造函数需要传入一个配置数组,例如 app_id, secret 等,名称不局限。此时将存入 $foundation->config 里面。

Foundation 有几个默认的读取配置。

  • debug
  • log
  • cache

举个栗子,假如你是这样使用的:

<?php

$app = new Foundation([
    'app_id' => 'xxx',
    'secret' => 'xxx',
    'debug' => true,
    'log' => [
        'name' => 'foundation',
        'file' => storage_path('logs/foundation.log'),
        'level' => 'debug',
        'permission' => 0777,
    ],
    'cache' => new Doctrine\Common\Cache\FilesystemCache(sys_get_temp_dir());
]);

从上面可以看出,上面指定了 debug 状态,日志以及缓存。

当 debug 为 false 的时候,将不会输出日志(request 请求,response 返回,假如是控制台还会有输出)。

可以看出缓存方面使用的是 Doctrine\Common\Cache\FilesystemCache ,这一部分也可以完全参考 easywechat 的缓存文档

Api & Http

凡是写 SDK 都需要去新建一个 Api 的类,这个类需要去继承 Hanson\Foundation\AbstractAPI

API 类可以实现父类的 middlewares 去增加 guzzle 的中间件,例如 header 增加 Authorization 参考这里

请求类使用的包是 guzzle 是毫无疑问的了,此外封装几个常用方法,目前来看是够用的了。

然后在 API 类里面通过 $http = $this->getHttp(); 去获得一个 Http 请求类,去根据实际场景调用不同的请求方法。

  • $http->get($url, $query = []) get 请求
  • $http->post($url, $data = []) post 请求,针对表单,使用的是 form_params
  • $http->json($url, $data = []) post 请求,针对 json
  • $http->upload($url, $query = [], $files = [], $form = []) post 请求,用于上传文件,具体我自己都觉得复杂,可以自己看看

小结

看到这里其实就介绍的差不多了,相信不少人看完会觉得有点懵,写的也不算好,讲的有点抽象,需要大家动手尝试才能加深印象。上面的 SDK 列表也可以当做学习的参考,每个 SDK 的情况都不一样,但 foundation-sdk 还是非常出色的兼容到各种不一样的情况。

篇幅有限,如果想再深入学习的同学需要去读读源码。

希望你们喜欢,来 star 一个 https://github.com/hanson/foundation-sdk

Learn from the best !

php吹水交流群 570769430

本帖由系统于 4个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 21
96qbhy

我用过几次 foundation-sdk ,确实很好用。

4个月前
丁海军

我也基于楼主的foundation-sdk 写了好几个项目,比如:justmd5/tencent-aijustmd5/duoduoke-sdk ,好用的怒赞 :stuck_out_tongue_closed_eyes:

4个月前

log和cache都是业务系统做的事情,sdk作为底层不应该有这样的操作;

举个例子某段支付操作,如果支付网关拒绝,sdk最好得做法是抛出异常向上传递错误信息,业务系统有需要会自己把信息记录下来,或写入文件或发到日志中心;sdk帮用户记下来略显多余。

我没想到cache在sdk中得应用场景,如果是用来保存操作状态,同理,逻辑分拆,交给上层处理就好。

4个月前
Hanson

@slince 你认真看这里的log,会发现记录的是 request 和 response,在你开发的时候把 debug 打开就会非常的爽

4个月前
Hanson

@slince cache 是保存 token,也是 SDK 应做的事情

4个月前

@Hanson 单纯调试的话生产环境是用不到的,那把这块依赖写到require-dev比较合适,依赖太多看起来比较头疼。
token获取和设置可以分为俩操作,$client->getToken(), $client->setToken(); 让业务系统自己存储,

4个月前
Hanson

@slince 不知你用过哪个 SDK 是需要自己存储 token 的?

4个月前

@Hanson 很多啊,比较典型oauth登录,token, refresh token都是存数据库的啊;shopify店铺共有应用授权方式,也要自己存;

4个月前

主要是你没办法区分业务系统cache是什么样的机制,也许人家自己设计了一套cache机制,比如遵从psr6或者psr16,没有使用doctrine/cache; sdk过多的依赖会对上层设计产生过多的干扰,增加其它工作量。

4个月前
Hanson

@slince 当你开发 SDK 情况多了,就会发现不是所有 SDK 都一概而论的,而且我的 token 也没帮你的存数据库,缓存也不是所有情况都需要。例如 微信的 token,7200 秒过期,存缓存就很好的解决了,你也可以去看看 easyWeChat的源码

4个月前
Hanson

@slince 提议很好,建议去 https://github.com/overtrue/wechat/issues easyWeChat 也提一下意见,很期待大神们的思想碰撞

4个月前

对于 @slince 的意见,我表示附议!


我觉得sdk尽量只负责自己的事是坠吼的(不知道log和cache算不算违反了单一职责原则?)

4个月前
Hanson

@nff93 @slince 我觉得你们是看见 log 和 cache 就把这两玩意所做的范围想大了,log 只是记录 request 和 response,cache 是记录token,至于数据库存储 token的,该用还是要用,并不是代替。

举个栗子,用 微信 举例。
调用微信接口需要使用 token,那么现在的做法是存储在 cache 中,开发者甚至感知不到 token 的存在直接调用 $wechat->card->create(); 就可以调用卡券创建了。假如不存cache,你说我不喜欢这样,我要自己存储 token,那么你可以存储在 cache 或者 database 中。

这样同样可以实现,但问题会出现在哪里?开发者的调用需要多了一层。

// 缓存
$token = Cache::get('wechat-token');

// 数据库
$token = DB::where('app_id', 'xxx')->first()['token'];

$wechat->setToken('xxx')->card->create();

而且是每次调用都需要去缓存或者数据库去取出来。
再回头对比一下 $wechat->card->create();
看到这里,不知作为开发者的你会更偏向哪个?

希望我的回答能给大家帮助,如果有什么不对也欢迎提出。

4个月前

学习了

4个月前

你确定你的 config 可以使用 点语法么?

file

4个月前
Hanson

@DanceSmile 要不你先试试?

4个月前
overtrue

@slince @nff93 @Hanson
我也来参与讨论一下咯 :smile:

我觉得这是一个边界取舍的问题,往标准了走,用起来可能就不够爽,往简化内置方向走,就不够灵活。其实之前也有跟 @leo 讨论过这类似的问题,基于固定配置格式的灵活性与基于代码 API 的灵活性比较起来是会差一些,在开源框架当中也有两个代表作:Symfony 与 Laravel,symfony 的所有组件基本都没有固定的配置文件或者说配置模板的存在,几乎可以应用在所有的开发环境中,你可以以任何方式来存储配置参数,只要最终使用类 API 的调用去应用即可,非常灵活。反观 Laravel 其实看起来就会比较固定一些,大部分是靠配置文件驱动。

其实这两种形式没有绝对的好与坏,我个人认为,如果你开发的东西是让别人在各种不同的复杂环境下使用,可能 Symfony 这样的更容易接入一些,当然对开发者水平会有稍稍的要求(我见过很多连类的 API 文档都不知道是什么东西的人不少),如果说你做的东西比较独立,不需要太多的集成动作存在,那配置驱动或许是不错的选择。

这两种形式其实是可以融合的,它们的边界可以模糊,Laravel 其实就是这样的例子,它所有的组件都提供了一个用法示例,比如 laravel/laravel 这个应用骨架,基于标准格式的配置文件来拼装一个完整的应用框架。同时,它的所有组件都提供了单独使用的功能,并不需要固定配置文件的位置与格式。

所以,其实两者并没有一定的好坏,对于喜欢开箱即用的人来讲,内置完整的功能模块,拿来直接就可以跑起来,比起非得写一个或者引用一个缓存驱动才能跑起来,肯定是前者带来的愉悦感更强一些。将内置做为可替换,可选组件,也满足了喜欢自定义的用法,所以容器模式在这里应用恰到好处。我内置了 token 缓存,减少了重复的工作量,如果你不喜欢内置的缓存模式,你还可以按标准接口自己实现,还是比较不错的。

个人的看法大概就是上面说的这样,由于人类的复杂程度,导致了不同的需求同时存在,没有适合所有人的应用存在。

另外关于异常处理,我觉得分两个类型,如果是扩展性组件,异常不应该消化,而是抛出,交给应用框架层来处理是合理的。如果是应用性项目,一个完整的 web 应用这样的东西,它已经是顶层应用了,所以它才需要对异常做处理。

3个月前

吓到我了多久之前的事,既然超哥提到再补充两句: @overtrue @Hanson

$wechat->setToken('xxx')->card->create();$wechat->card->create(); 更方便的问题

其实不存在哪个更方便,要是觉着前者复杂可以包装语法糖;wechat()->card->create();

这两种形式其实是可以融合的,它们的边界可以模糊,Laravel 其实就是这样的例子,它所有的组件都提供了一个用法示例

laravel这么做是有代价的,laravel的每个独立发型的包几乎都依赖了laravel全家桶;其实相当于在你的程序里构造了一个mini laravel runtime 出来;有点本末倒置。

将内置做为可替换,可选组件,也满足了喜欢自定义的用法,所以容器模式在这里应用恰到好处。

laravel里独立包里很多处强行依赖了 Container 本身,queue, console; 我不想说这是很大的问题,但这确实是psr11不推荐的,容器的定位是指导者,是个上帝,上帝可以帮信徒实现愿望,但信徒不能让上帝实现愿望, 容器对象不应该被任何class直接依赖;当然这也是理论的问题;

不过如果以替换容器里的key的定义来实现“灵活” 这种做法本身就不灵活,因为容器对象又被强制依赖了;

所以,其实两者并没有一定的好坏,对于喜欢开箱即用的人来讲,内置完整的功能模块,拿来直接就可以跑起来

其实没有说好与坏,上面说的那些也是偏向标准去衡量的;业务系统是复杂的,但“标准”的出现就是想简化这种“复杂”;

开箱即用,需要业务系统妥协或者业务系统也不是太苛刻,可以满足sdk的运行条件;有些非使用框架的系统都有自己的基础架构,cache与log 都有惯例的写法,这种情况下非要基于 monolog 或者 doctrine cache去实现一遍会有很大阻力。

3个月前
Bin

@slince 看了你的回复,到最后也没看懂你的立场,也没看懂你要表达什么样的一个观点。给我的感觉就是,你把 Hanson 跟超哥说的几个点单独提了出来反驳了一遍。这是我认真把你的回复读了三遍后的一个感受。

3个月前

@Bin 去看上下文

3个月前

@slince 用过easywehcat和vbot,赞成你的观点。

2个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!