日期:2018-3-2113:22:16标题:Swoft|Swoft框架组件改造说明:经过一年多的发展,Swoft框架在功能上越来越完善,但也越来越复杂。初期单一的应用已经无法支撑项目的快速发展,因此开发团队在几年前就制定了1.0-beta版本的组件化改造计划。内容速览:组件化原理:PHP包管理基础知识组件化方案:来自laravel/symfony等成熟框架的组件化实现方案swoft框架组件化实现组件化原理编程一直需要解决的一个问题:代码复用。好的代码,基本要求正确,就能得到预期的结果,bug少。语言层的代码重用解决方案通常称为包管理(或依赖管理)。流行的编程语言为包管理提供了很好的工具链支持:用于获取/管理包的命令行工具,例如composerforphp,pipforpython,npmforjs,mavenforjava,gogetforgoget包管理配置文件,用于说明需要使用(依赖)包,比如PHP中的composer使用composer.josn,js中的npm使用package.json查看包信息,比如php中的packgist,python中的pypi等等,当我们需要不同功能的时候,可以去看看有没有已经提供类似功能的包,不用重新发明轮子,站在巨人的肩膀上。回到PHP,PHP中的包管理是如何实现的?关于命名空间,您需要了解的第一件事基本概念是命名空间。命名空间的引入是为了解决同名冲突——两个包中存在同名类,同时使用时,会提示类被重复定义.使用命名空间后,因为不同的包有不同的命名空间,所以不会有冲突。使用B\Far作为BFar;autoload&PSR4第二个需要知道的基础概念是自动加载。PHP中最基本(或原始)的代码重用方法:include/include_one/require/requ一次。然而,多亏了PHP的SPL库中的spl_autoload_register()方法,现在有了一种更优雅的代码重用方式——自动加载。autoload规范也经历了一段时间的升级打磨。最新的是PSR4标准。关于自动加载有一个很好的教程:5-1SPL使用spl_autoload_register函数加载类(10:03)composer中的包管理在了解了基础知识之后,您可以掌握如何使用工具。在composer中,包管理是根据composer.json文件中的autoload/require/require-dev配置项进行管理的。autoload定义了自动加载,项目本身的代码也要按照包管理规范来组织,比如SwoftFiles的composer.json配置:..."autoload":{"psr-4":{"App\\":"app/"},"files":["app/Swoft.php"]},...composersupportedmultiple这个有一定的历史原因,因为需要兼容一些老代码.现在常用的方法有两种:psr-4:PSR4标准,优先文件的推荐方法:直接加载文件,通常带有加载辅助函数,类似于PHP的require语法用于代码复用require标识需要的包依赖,格式为包名-版本限制的键值对:..."require":{"php":">=7.0","swoft/framework":"^1.0","swoft/rpc":"^1.0","swoft/rpc-server":"^1.0","swoft/rpc-client":"^1.0","swoft/http-server":"^1.0","swoft/http-client":"^1.0","swoft/task":"^1.0","swoft/http-message":"^1.0","swoft/view":"^1.0","swoft/db":"^1.0","swoft/cache":"^1.0","swoft/redis":"^1.0","swoft/console":"^1.0","swoft/devtool":"^1.0","swoft/session":"^1.0","swoft/i18n":"^1.0","swoft/process":"^1.0","swoft/memory":"^1.0","swoft/service-governance":"^1.0"},...关于版本控制的知识,还有>=^~之类的特殊字符,alphabetadevdev-master之类的标识,只是一些约定俗成的定义,大家可以看清楚。require-dev标识了开发环境需要依赖的包,也就是正式环境不需要使用的包,比如单元测试等:..."require-dev":{"eaglewu/swoole-ide-helper":"dev-master","phpunit/phpunit":"^5.7"},...类似的,还有autoload-dev,表示测试时使用自动加载环境。组件方案:laravel和symfony使用的方案,参考symfony中的composer.json配置文件和laravel中的composer.json配置文件,会发现里面有一个配置项:replace.replace这个配置item在普通项目中很难见到,但是在组件改造中却是一个重要的配置。其定义如下:使用项目中已有的包替换symfony.json配置文件中composer等依赖包:..."replace":{"symfony/asset":"self.version","symfony/browser-kit":"self.version","symfony/cache":"self.version","symfony/config":"self.version","symfony/console":"self.version","symfony/css-selector":"self.version","symfony/dependency-injection":"self.version",...其中"symfony/asset"包有一个单独的github仓库symfony/asset,symfony项目本身也包含“symfony/asset”包。使用replace,symfony可以使用它包含的包而无需单独获取它。这样做带来的好处:主包包含了所有的子包,使用的时候使用replace配置,所有的修改和提交都在主包中进行。其他项目依然可以使用require,单独使用分包;子包只接受主包分发的代码,不接受对子包的修改。Swoft框架组件化实现Swoftin1.0-Dependenciesinbeta,Swoft项目:..."require":{"php":">=7.0","swoft/framework":"^1.0","swoft/rpc":"^1.0","swoft/rpc-server":"^1.0","swoft/rpc-client":"^1.0","swoft/http-server":"^1.0","swoft/http-client"":"^1.0","swoft/task":"^1.0","swoft/http-message":"^1.0","swoft/view":"^1.0","swoft/db":"^1.0","swoft/缓存":"^1.0","swoft/redis":"^1.0","swoft/console":"^1.0","swoft/devtool":"^1.0","swoft/session":"^1.0","swoft/i18n":"^1.0","swoft/process":"^1.0","swoft/memory":"^1.0","swoft/service-governance":"^1.0"},...改造后,swoft项目,主项目只依赖"swoft/framework":..."require":{"php":">=7.0","swoft/framework":"^1.0"},..."swoft/framework"项目,包括其他子包:..."replace":{"swoft/rpc":"self.version","swoft/rpc-server":"self.version","swoft/rpc-client":"self.version","swoft/http-server":"self.version","swoft/http-client":"self.version","swoft/task":"self.version","swoft/http-message":"self.version","swoft/view":"self.version","swoft/db":"self.version","swoft/cache":"self.version","swoft/redis":"self.version","swoft/console":"self.version","swoft/devtool":"self.version","swoft/session":"self.version","swoft/i18n":"self.version","swoft/process":"self.version","swoft/memory":"self.version","swoft/service-governance":"self.version"}...其中子项目声明提交给主项目修改:在Swoft主仓库报告问题并发送PullRequests整个开发过程如下如下:在daydaygo/swoft-framework项目新建component2分支development这个组件改造修改swoft项目的composer.json文件,快速获取所有swoft组件的master分支代码:"require":{"php":">=7.0","swoft/framework":"dev-master","swoft/rpc":"dev-master","swoft/rpc-server":"dev-master","swoft/rpc-client":"dev-master","swoft/http-server":"dev-master","swoft/http-client":"dev-master","swoft/task":"dev-master","swoft/http-message":"dev-master","swoft/view":"dev-master","swoft/db":"dev-master","swoft/cache":"dev-master","swoft/redis":"dev-master","swoft/console":"dev-master","swoft/devtool":"dev-master","swoft/session":"dev-master","swoft/i18n":"dev-master","swoft/process":"dev-master","swoft/memory":"dev-master","swoft/service-governance":"dev-master"},将各个组件的代码复制到swoft-framework项目中,修改composer.json中的autoload/replace配置(点击链接查看具体修改)Swoft组件依赖关系图:http://naotu.baidu.com/file/7...提交swoft-framework代码下面是推送swoft-view的例子component到对应仓库:由于github网速的原因,在测试过程中使用gitee加速将子项目推送到对应的github仓库。参考:dflydev/git-subsplit:将gitsubtree封装为gitsubplite命令,方便使用laravel中使用gitsubsplit的例子](https://github.com/laravel/fr...#buildgitee.com:daydaygo/swoft-frameworkwarehousegitremoteaddgiteegit@gitee.com:daydaygo/swoft-framework.gitgitpushgiteecomponent2#拆除分水岭gitsubsplitinitgit@gitee.com:daydaygo/swoft-framework.git#更多项,填一次即可gitsubsplitpublish--heads="component2"--no-tagsview:git@gitee.com:daydaygo/swoft-view.git#清除生成的临时文件rm.subsplit这个拆分过程耗时较长,拆分后的效果:gitee.com/daydaygo/swoft-view,gitee.com/daydaygo/swoft-framework可以通过添加githubwebhook实现自动化,具体参考:dflydev/dflydev-git-subsplit-github-webhook最后测试split代码:修改swoft项目的composer.json文件,使用新版本的swoft-framework项目...//现在只需要依赖swoft/framework,版本号需要分支,composer会默认为带有dev-前缀的分支名称"require":{"php":">=7.0","swoft/framework":"dev-component2"},...."repositories":{"packagist":{"type":"composer","url":"https://packagist.phpcomposer.com"},//指定包地址,这里指向我的giee仓库地址"0":{"type":"vcs","url":"https://gitee.com/daydaygo/swoft-framework"}}...#删除之前的依赖rm-rfcomposer.lockvendor#更新composerinstall--no-dev为运行测试,可以参考到目前为止项目下的.travis.yml文件,就大功告成了。写在最后把项目拆分成组件,分包推送到不同的github仓库,这种情况可能只有在写大型框架的时候才会遇到。但这也是手写框架的乐趣所在。PHP中包管理的基础知识。
