技术文档 | OpenSCA技术原理之composer依赖解析

原创
2023/02/01 17:07
阅读数 126

OpenSCA知识小课堂开课了!

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

composer介绍

composerPHP的依赖管理工具。

开发者受到Node.jsnpmRubybundler启发,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为包的类型,有libraryprojectmetapackagecomposer-plugin四种类型,默认情况下为librarylicense为项目声明的许可证,可以是一个字符串或是一个字符串数组。

require-dev为开发环境或测试使用的依赖,require为生产环境使用的依赖,依赖写法为"name":"version",版本可以指定准确版本或一个范围。

 

解析算法

composer.lock

composer.lock文件为自动生成的文件,可以准确定位到PHP项目使用的依赖及版本,所以优先解析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": []}

其中packagespackages-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.1.0且<1.2.0
  2. ^1.0.2代表版本号需要>=1.0.2且<2.0.0

由此可以构建出当前项目的依赖结构:

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

图:composer.lock检测结果示例

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版本。

因此组件依赖结构就变成了:

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

图:composer.json检测结果示例

 


 

感谢每一位开源社区成员对OpenSCA的支持和贡献。

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

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

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

OpenSCA官网: https://opensca.xmirror.cn/

扫码加入OpenSCA社区技术交流群

或关注公众号:OpenSCA社区

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部