OpenSCA常识小课堂开课了!

明天次要介绍基于composer包管理器的组件成分解析原理。

composer介绍

composer是PHP的依赖管理工具。

开发者受到Node.js的npm及Ruby的bundler启发,composer设计上与两者有诸多类似。

composer的依赖管理文件是composer.json。开发者能够在composer.json中指定每个依赖项的版本范畴或应用composer require/update/remove ${name}命令治理依赖项。

如果一个我的项目中存在composer.json文件,便能够执行composer install命令主动装置以后我的项目所需的依赖项并生成composer.lock文件

composer.json残缺文件构造如下:

{  "name": "cakephp/app",  "type": "project",  "license": "MIT",  "require": {      "php": ">=7.2",      "cakephp/cakephp": "^4.3",      "cakephp/migrations": "^3.2",      "cakephp/plugin-installer": "^1.3",      "mobiledetect/mobiledetectlib": "^2.8"  },  "require-dev": {      "cakephp/bake": "^2.6",      "cakephp/cakephp-codesniffer": "^4.5",      "cakephp/debug_kit": "^4.5",      "josegonzalez/dotenv": "^3.2",      "phpunit/phpunit": "~8.5.0 || ^9.3"  },}

其中name为项目名称;type为包的类型,有library、project、metapackage和composer-plugin四种类型,默认状况下为library;license为我的项目申明的许可证,能够是一个字符串或是一个字符串数组。

require-dev为开发环境或测试应用的依赖,require为生产环境应用的依赖,依赖写法为"name":"version",版本能够指定精确版本或一个范畴。

解析算法

composer.lock

composer.lock文件为主动生成的文件,能够精确定位到PHP我的项目应用的依赖及版本,所以优先解析composer.lock文件。

composer.lock文件构造如下:

{  "packages": [    {      "name": "a",      "version": "1.1.0",      "require": {          "c": "1.1.*"      }    },    {      "name": "b",      "version": "1.2.2",      "require": {          "c": "^1.0.2"      }    },    {       "name": "c",      "version": "1.1.2"    }  ],  "packages-dev": []}

其中packages和packages-dev字段蕴含我的项目应用的所有间接和间接依赖,而且记录了组件间的依赖关系,packages为生产环境的依赖,packages-dev为开发环境的依赖。

示例:

{  "name": "a",  "version": "1.1.0",  "require": {      "c": "1.1.*"  }}

代表我的项目依赖1.1.0版本的组件a,且该组件依赖版本束缚为1.1.*的组件c。

同理可知我的项目依赖1.2.2版本的组件b,且该组件依赖版本束缚为^1.0.2的组件c。

且组件a和组件b都没有被其余依赖所依赖,所以可知这两个组件是我的项目的间接依赖。

注:

1.1.*代表版本号须要>=1.1.0且<1.2.0

^1.0.2代表版本号须要>=1.0.2且<2.0.0

由此能够构建出以后我的项目的依赖构造:

实线代表间接依赖,虚线代表间接依赖

composer.json

composer.json为开发者治理的依赖管理文件,在未找到composer.lock文件时将解析该文件。

composer.json仅蕴含间接依赖,在我的项目构建时会从composer仓库下载须要的间接依赖并构建为composer.lock文件,因而能够模仿composer构建流程来获取我的项目援用的组件依赖。

composer.json文件构造如下:

{  "name": "foo",  "type": "project",  "license": "MIT",  "require": {      "a": "^1.1.0",      "b": "^1.2.0",  },  "require-dev": {},}

require为我的项目理论应用的间接依赖,require-dev为我的项目开发时应用的间接依赖。

例如:

"a": "^1.1.0"代表我的项目依赖版本束缚为^1.1.0的组件a。

"b": "^1.2.0"代表我的项目依赖版本束缚为^1.2.0的组件b。

剖析到这里咱们能够总结出如下图依赖关系:

实线代表间接依赖

通过该依赖关系能够看出我的项目组件的间接依赖及组件的版本范畴,但无奈得悉组件依赖的具体版本。

在没有composer.lock文件的状况下,为了进一步获取依赖的精确版本及间接依赖,须要从composer仓库下载对应组件的详细信息。

例如组件a的详细信息构造为:

{  "packages": {    "a": [      {        "version": "1.0.1",        "require": {          "c": "^1.0.0"        }      },      {        "version": "1.1.0",        "require": {          "c": "^1.1.0"        }      }    ]  }}

其中packages字段为组件及各个版本信息的映射,require字段为组件的依赖信息。

对于本例来说,组件a的束缚为^1.1.0,要求版本号>=1.1.0且<2.0.0,所以抉择1.1.0版本。

因而组件依赖构造就变成了:

依照这种形式层级解析便可获取整个我的项目的依赖信息。


感激每一位开源社区成员对OpenSCA的反对和奉献。

OpenSCA的代码会在GitHub和Gitee继续迭代,欢送Star和PR,成为咱们的开源贡献者,也可提交问题或倡议至Issues。咱们会参考大家的倡议不断完善OpenSCA开源我的项目,敬请期待更多功能的反对。

OpenSCA官网:

https://opensca.xmirror.cn/

GitHub:

https://github.com/XmirrorSecurity/OpenSCA-cli/releases

Gitee:

https://gitee.com/XmirrorSecurity/OpenSCA-cli/releases