【问题标题】:How to create a monolithic Composer package with a built-in composer-plugin?如何使用内置的 composer-plugin 创建一个整体的 Composer 包?
【发布时间】:2017-01-16 07:17:57
【问题描述】:

我希望我的包附带一个内置的作曲家插件。

我有这样的结构:

composer.json
src/
    ...
plugin/
    composer.json
    src/
        ...

composer.json配置如下:

{
    "name": "foo/bar",
    "type": "library",
    "autoload": {
        "psr-4": {
            "Foo\\Bar\\": "src/"
        }
    },
    "repositories": [
        {
            "type": "path",
            "url": "./tools",
            "options": {
                "symlink": false
            }
        }
    ],
    "require": {
        "foo/bar-plugin": "*"
    }
}

而内置的composer-plugin的plugin/composer.json是这样的:

{
    "name": "foo/bar-plugin",
    "type": "composer-plugin",
    "require": {
        "composer-plugin-api": "^1",
        "composer/composer": "^1",
        "foo/bar": "*"
    },
    "autoload": {
        "psr-4": {
            "Foo\\Bar\\Plugin\\": "src/"
        }
    },
    "extra": {
        "class": "Foo\\Bar\\Plugin\\MyComposerPlugin"
    }
}

注意这里有一个双向依赖——插件依赖于foo/bar,而项目本身依赖于foo/bar-plugin

这就是奇怪的地方。在全新安装期间,例如composer installcomposer update,一切都很好 - 插件完成了它的工作,现在,这意味着只是在控制台上宣布自己。

现在,在安装之后,如果我只输入composer,我希望看到插件自己宣布,和以前一样,对吧?

相反,一旦它尝试引用属于 foo/bar 包的任何类,它就会生成一个致命的“找不到类错误”。

就好像作曲家忘记了 foo/bar-plugin 需要 foo/bar 的事实,并且由于某种原因它的类不能自动加载。

有什么理由不应该这样做吗?为什么不呢?

当然,我可以将这些东西打包在单独的外部包中,但这没有多大意义,因为这些包只是相互依赖 - 它们实际上是一个单元,将它们打包为两个包都会导致主要版本随着每一个小的变化而增加,因为基本上foo/bar 的每个版本都会破坏foo/bar-plugin

理想情况下,我只想将 composer-plugin 直接添加到主包中,但由于某种原因,这似乎是不可能的?好像只有composer-plugin类型的包才允许添加插件吧?

【问题讨论】:

  • 那么为什么不将composer-plugin类型添加到主包中呢?如果安装了它,它应该仍然可以作为普通库使用,不是吗?
  • 不幸的是,这个包已经有一个自定义类型,因为它是由自定义安装程序安装的。 (但这不是自定义安装程序造成的问题 - 我也尝试过使用香草library 包。)
  • 这个问题中的变量太多,无法得到明确的答案。什么作曲家版本?运行composer 的确切输出显示错误?列出的文件树与您的根 composer.json -“./tools”与“./plugin”之间存在明显差异?为什么需要循环依赖?一般是代码味道。
  • “它们实际上是一个单元” - 如果您有两组非常依赖的代码需要始终一起发布,我认为它们应该是一个项目。你总是可以用 git 子模块或类似的,但是一个 composer 项目来分解 repos。
  • 请记住,您始终可以使用Scripts 而不是Plugin。您可以将脚本类插入到主项目中composer.json 的脚本部分,甚至在custom-installer 的上一层。在事件处理方面,您可以使用脚本获得相同的效果;当注册到正确的事件处理程序时,它们也会在安装和更新时自动运行。

标签: php json composer-php composer-plugin


【解决方案1】:

如果插件本质上是你的包的一部分,你不应该这样使用它。 Composer 提供了替代方案。

正如 Jens 在对您的问题的评论中提到的,composer.json 中有“脚本”键。你可以在里面调用shell命令,也可以调用静态类方法。

关于插件解决方案 - 作曲家在其网站上明确提到了这一点:

Composer 在安装或更新之前不对依赖项的状态做出任何假设。因此,您不应在 pre-update-cmd 或 pre-install-cmd 事件挂钩中指定需要 Composer 管理的依赖项的脚本。如果您需要在安装或更新之前执行脚本,请确保它们在您的根包中是独立的。

(我的旁注——这也大致适用于插件)。

无论如何 - 为您提供解决方案:丢弃'插件'方法。而是修改您的 composer.json 文件,使其如下所示:

composer.json

{
    "name": "foo/bar",
    "type": "library",
    "autoload": {
        "psr-4": {
            "Foo\\Bar\\": "src/"
        }
    },
    "require": {
    },

    "scripts": {
        "post-install-cmd": [
            "Foo\\Bar\\Composer\\Plugin::postInstall"
        ],
        "post-update-cmd": [
            "Foo\\Bar\\Composer\\Plugin::postUpdate"
        ]        
    }

}

另外,在src/Composer 文件夹中创建Plugin.php

src/Composer/Plugin.php

<?php

namespace Foo\Bar\Composer;

use Foo\Bar\Test;

/**
 * Composer scripts.
 */
class Plugin
{
    public static function postInstall()
    {
        print_r("POST INSTALL\n");
        print_r(Test::TEST_CONST);
        print_r("\n");
    }

    public static function postUpdate()
    {
        print_r("POST UPDATE\n");
        print_r(Test::TEST_CONST);
        print_r("\n");
    }
}

如您所见,它从 Test 类打印常量。在src/中创建它:

src/Test.php

<?php

namespace Foo\Bar;

/**
 * Test class.
 */
class Test
{
    const TEST_CONST = "HERE I AM";
}

运行它并检查它是如何发挥作用的。

【讨论】:

    猜你喜欢
    • 2015-12-15
    • 2016-06-25
    • 1970-01-01
    • 2018-12-05
    • 2016-12-05
    • 2013-09-01
    • 2014-07-16
    • 2013-02-20
    • 2015-09-26
    相关资源
    最近更新 更多