【问题标题】:How does composer handle multiple versions of the same package?composer 如何处理同一个包的多个版本?
【发布时间】: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 个版本,这是否会导致任何冲突,因为命名空间可能相同?

谢谢

【问题讨论】:

    标签: symfony composer-php


    【解决方案1】:

    至问题 1

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

    至问题 2

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

    在这种情况下你可以

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

    【讨论】:

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

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

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

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

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

    "require": {
            "guzzlehttp/guzzle": "^6.2"
            // possible other stuff
        },
    1. composer install 安装 Guzzle v6 的所有依赖项。
    2. /vendor/guzzlehttp 目录移至新的/vendor-static/guzzlehttp 目录。
    3. /vendor-static 目录中进行区分大小写的查找和替换,以将 GuzzleHttp 替换为 GuzzleHttp6。这有效地将 Guzzle 6 代码带入了一个新的命名空间。
    4. 现在更新您的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/"
                }
            },
    1. composer update 删除旧的 Guzzle v6,并安装 Guzzle v5。这还将安装 psr/http-messageralouphie/getallheaders 依赖项。

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

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

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

    【讨论】:

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