composer 如何处理同一个包的多个版本?

Posted

技术标签:

【中文标题】composer 如何处理同一个包的多个版本?【英文标题】:How does composer handle multiple versions of the same package? 【发布时间】:2015-01-24 20:55:52 【问题描述】:

这可能(应该)以前在某个地方被问过,但我似乎找不到答案。如果有人提供链接,我可以删除此帖子!:

只是想了解一些作曲家的(可能也适用于其他包管理器)功能。

基本上我只是想知道作曲家在以下场景中做了什么:

1.

我的主项目有一个依赖:

"guzzlehttp/guzzle": "5.0.*",

我的外部包依赖于

"guzzlehttp/guzzle": "5.0.*",

composer 是否会安装一次 guzzlehttp/guzzle,因为它知道它只需要一次?

2。 相同的场景,但将来如果有人更新要使用的主项目:

"guzzlehttp/guzzle": "6.0.*",

composer 现在会安装 2 个版本的 guzzle(5 和 6)(我认为这是它应该做的),还是会安装最高版本(即 6)?此外,如果有 2 个版本,这是否会导致任何冲突,因为命名空间可能相同?

谢谢

【问题讨论】:

【参考方案1】:

至问题 1

是的,Composer 只能安装每个扩展/包的一个版本。

至问题 2

因为答案 1:Composer 会认为您的主项目和外部包不兼容。

在这种情况下你可以

在您的主项目中也保留第 5 版。 如果兼容,请让外部包所有者也升级到版本 6。 fork 外部包并自行使其与版本 6 兼容

【讨论】:

有没有人找到解决这个问题的方法?还是作曲家团队认为这是一个特性而不是一个错误? 我想这永远不会起作用,因为不同版本中的相同扩展/包将具有相同的命名空间和几乎相同的功能,这使得它们不兼容。这不是功能也不是错误。您根本不能在同一个命名空间中拥有 2 次相同的函数。 @peh,是的,这是真的,但是您的代码可能有两个完全不同的部分,需要两个完全不同版本的库。如果它们在加载时从不发生碰撞,那么从技术上讲可以有两个不同的版本。但就作曲家而言,它不是自动加载的,据我所知,它没有内置方法来区分自动加载的不同版本。【参考方案2】:

我们今天遇到了一种情况,我们使用了多个库,其中一个使用了 Guzzle v5,另一个使用了 Guzzle v6。升级(或降级)不是一个可行的选择,因为它是第三方代码,所以我们必须能够安装两个版本的 Guzzle。

这就是我们所做的。这是一个TOTAL FRACKING HACK,我建议仅将其作为绝对的最后手段。它可以工作,但更新您的调用代码以仅使用一个版本是一个更好的选择。

诀窍是您需要重新命名两个版本之一。在我们的例子中,我们决定将 v6 更改为 GuzzleHttp6。这样做的方法如下:

    确保您的 composer.json 已启用 v6:

"require": 
        "guzzlehttp/guzzle": "^6.2"
        // possible other stuff
    ,
    composer install 安装 Guzzle v6 的所有依赖项。 将/vendor/guzzlehttp 目录移至新的/vendor-static/guzzlehttp 目录。 在/vendor-static 目录中进行区分大小写的查找和替换,以将 GuzzleHttp 替换为 GuzzleHttp6。这有效地将 Guzzle 6 代码带入了一个新的命名空间。 现在更新您的composer.json 以手动包含Guzzle 自己的依赖项,然后自动加载/vendor-static 文件夹中的代码。请注意,您需要删除主要的 guzzle 要求语句(或将其更改为包括 guzzle 5);

"require": 
            "guzzlehttp/guzzle": "~5",
            "psr/http-message": "~1.0",
            "ralouphie/getallheaders": "^2.0.5"
        ,
        "autoload": 
            "files": ["vendor-static/guzzlehttp/guzzle/src/functions_include.php",
                "vendor-static/guzzlehttp/psr7/src/functions_include.php",
                "vendor-static/guzzlehttp/promises/src/functions_include.php"],
            "psr-4": 
            	"GuzzleHttp6\\": "vendor-static/guzzlehttp/guzzle/src/",
            	"GuzzleHttp6\\Psr7\\": "vendor-static/guzzlehttp/psr7/src/",
            	"GuzzleHttp6\\Promise\\": "vendor-static/guzzlehttp/promises/src/"
            
        ,

    composer update 删除旧的 Guzzle v6,并安装 Guzzle v5。这还将安装 psr/http-messageralouphie/getallheaders 依赖项。

    您可能需要执行composer dump-autoload 来强制自动加载器添加新的包含路径。理论上这应该发生在composer update,但我不得不强制它。

    现在更新您的呼叫代码;您将调用 \GuzzleHttp6,而不是调用 \GuzzleHttp

就是这样。您应该能够同时运行两者。请注意,您在 /vendor-static 目录中获得的任何版本的 Guzzle v6 都将永远存在,因此您可能需要不时更新它。

【讨论】:

您是否有可能分叉了使用奇怪版本的 guzzle 并根据需要对其进行修改以使其兼容的包,然后 composer 安装了那个包?在一个完美的世界中,这将是最好的方法,它需要做更多的工作,但可以让你不必做这些变通方法。 在我们的例子中不是——使用 Guzzle5 的父库是专有的第三方代码,已使用 Ioncube 加载程序加密,并且不可更改。我们必须有一个支持 Guzzle5 和 Guzzle6 的解决方案。 这并不总是像查找和替换那样简单

以上是关于composer 如何处理同一个包的多个版本?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理多个单独的 json 响应?

Jetpack Compose 和 Compose Navigation 如何处理 Android 活动?

如何处理 Jetpack Compose 中的导航?

configure.ac中如何处理多个版本的Automake

将 docker-compose 与 CI 结合使用 - 如何处理退出代码和守护程序链接容器?

如何处理将作为另一个包的依赖项的命名空间包