在composer.json中带有动态包含的克隆项目


Cloned project with dynamic includes in composer.json

我有一个应用服务器,它就像一个博客系统(我的wordpress杀手)。它是基于php托管在github和使用composer来管理依赖关系。每个安装都托管在我的服务器上(我为它们进行安装)。当客户端需要一个新的"插件/插件"时,我创建一个新的包,并将其托管在一个私有存储库托管中。当我需要添加新包时,问题就出现了:

Client 1.
- package for calculate prices
Client 2.
- package for show a welcome message
Client 3.
- package for add a calendar

我的应用程序将有每个包准备在所有实例中使用,因为我需要它们通过composer:

"require": {
        "killer/calculate": "dev-master",
        "killer/message": "dev-master",
        "killer/calendar": "dev-master"
}

现在想象一下,如果我有2K个客户,他们每个人都在请求定制包。我如何提供一个应用程序(大规模克隆),但在每个安装中只保留每个客户机需要的包?

假设的解决方案

我正在搜索(如果可能的话)类似以下的东西。对于每个安装,手动创建一个文件,其内容指定所需的包。例如,假设每个客户机的安装都有如下内容:

//composer.json
"require": {
}
//plugins.json  (this file is ignored via .gitignore)
{
    "killer/calculate": "dev-master"
}

然后,以某种方式告诉composer.json需要plugins.json的数据。通过这种方式,我避免了为所有客户端创建一个巨大的composer.json共享不必要的包。

有一个特性请求允许composer.json扩展另一个文件。你应该去评论一下,引起人们的注意。

使用该特性的方法是创建一个default.json文件,其中包含所有常规composer.json内容,包括列出所需所有常用软件包的require部分。

// default.json
{
    "require": {
        "php": ">=5.4.0"
    }
}

然后为每个扩展和/或覆盖default.json的项目创建一个composer.json文件,如下所示:

// composer.json
{
    "require": {
        "killer/calculate": "dev-master"
    },
    "extends": "default.json"
}

最后的结果将是:

{
    "require": {
        "php": ">=5.4.0",
        "killer/calculate": "dev-master"
    }
} 

如果你不能等待合并请求,那么你可以从合并请求的作者那里签出编译器分支并尝试它。

听起来你已经创建了自己的包生态系统。因此,您可以独立于Packagist工作,并简单地自己托管所有包,可能使用Satis。

我的主要建议是将您的应用程序作为一个包引入这个生态系统。

您的应用程序composer.json只包含与应用程序本身相关的包。

{
  "name": "better/application",
  "require": {
     "another/library": "1.0.0",
     "another/framework": "1.2.3",
     "another/generator": "2.1.3"
  }, 
  "require-dev" : {
     "phpunit/phpunit" : "4.*",
  }
} 

我认为,为客户/客户端"克隆"应用程序不是一个好主意,因为它忽略了插件依赖于应用程序的特定版本,而该版本并不总是"最新"或"dev-master"。让Composer为每个客户端按版本提取应用程序。

通过这种方式,我避免了为所有客户端创建一个巨大的composer.json共享不必要的包。

  • 为每个新客户/客户创建一个存储库
  • composer.json
  • 中添加应用程序本身和客户端请求的包
  • 在顶部添加客户端配置
例如,Client1的composer.json:
{
  "name": "better/application-for-client1",
  "require": {
     "better/application": "1.0.0",
     "better/app-calculate": "1.2.3"
  }
} 
例如,Client2的composer.json:
{
  "name": "better/application-for-client2",
  "require": {
     "better/application": "1.0.1",
     "better/app-calculate": "1.2.4",
     "better/app-message": "2.0.0"
  }
} 

每个客户可能有自己的设置,需要不同版本的应用程序和不同的附加应用程序包/插件(这里使用前缀"app-"表示)。

最后,您为客户提供了两个基本文件:一个编写器。Json和配置文件。

应用程序如何检测可用模块是另一回事。至少,当你在应用程序的引导过程中注册composer自动加载器时,自动加载将会开箱工作。
(旁注:如果您的应用程序是一个多站点应用程序,那么您可以将"克隆"替换为"符号链接"。多站点是指使用站点id(通常是客户id)从一个中心位置运行的应用程序。如果您将包含所有开发包的应用程序文件夹放在一个整体文件夹中,那么通过清除开发内容来构建一个发布文件夹,这样您就可以得到一个带有空白默认配置的发布版本。然后将应用程序和请求的包符号链接到客户文件夹,并在上面放置一个配置。这种方法可以节省相当多的磁盘空间,并且根本不涉及Composer。

我还鼓励您使用Jens a . Koch在他的回答中提出的方法,即拥有一个作曲家。Json文件为每个客户,并要求主应用程序和所有所需的插件。Composer很好地支持这种场景,我想向您指出起点:

<<h3>作曲家类型/h3>

Composer允许为包指定类型。您可以定义自己的类型来标记应用程序的插件。许多开源项目(例如Symfony with bundles)已经在他们的插件生态系统中采用了这种方法。

自定义安装

您可以注册自定义安装程序,可以执行特定的type的composer包(即您为您的插件定义的类型)。然后,您可以自定义插件的安装过程,这意味着您可以:

  • 移动自定义插件到指定位置,
  • 自动调整插件配置,
  • 和最重要的:提供一个机制,允许主应用程序看到哪些插件可用。

嗯…

这意味着一个大的composer.json与你的系统运行/与你的系统兼容。也许你还需要引入一个版本控制系统。所以你最终可以得到v1_composer.json ..

但是对于每个客户端只加载所需的包。例如,为每个客户端生成一个requires.php,其中包含链接到共享库的必要require语句。这将是最快和最有效的解决方案,因为你共享了你可以共享的代码,你只在需要的时候加载它

结论

尽可能多地共享代码…但是当你不需要的时候就不要使用它。