文章转发自专业的Laravel开发者社区,原文链接:https://learnku.com/laravel/t...在创建PHP应用中在构建或库时,通常存在三种依赖项:硬依赖项:您的应用程序/库需要这种依赖项才能正常运行可选依赖项:例如PHP库可以为不同的框架提供功能开发依赖项:调试工具,测试框架,等等......你如何管理这些依赖关系?硬依赖:{"require":{"acme/foo":"^1.0"}}可选依赖:{"suggest":{"monolog/monolog":"Advancedlogginglibrary","ext-xml":"Required支持XML"}}开发依赖项:{"require-dev":{"monolog/monolog":"^1.0","phpunit/phpunit":"^6.0"}}到目前为止一切顺利。那么会出什么问题呢?主要是require-dev会有一定的限制。问题和过度限制的依赖关系使用包管理器来解决依赖关系是很好的。这种方法非常适合更新和重用代码。但是,您对导入的包的数量和数量负责。您导入的包存在错误和不安全的风险。除了受到第三方问题的困扰之外,您还变得依赖于别人写下的内容,而您可能无法控制这些内容。Packagist和GitHub在减轻这些风险方面做得很好,但它们仍然存在。Left-padfiasco是JavaScript社区中的一个很好的例子,其中添加一个包并不是完全无害的,因为它可能导致出现错误。依赖关系的第二个问题是它们需要兼容。这是作曲家的工作。但是Composer就是这样,有一些依赖是不能同时安装的,而且添加的依赖越多越容易发生冲突。如果你觉得这段话很长,就看这里:对你引入的依赖负责,并努力减少它们。强关系冲突我们看下面的例子:{"require-dev":{"phpstan/phpstan":"^1.0@dev","phpmetrics/phpmetrics":"^2.0@dev"}}这两个包是静态的分析工具,它们不能同时安装,即会产生冲突,因为它们依赖于不同且不兼容的PHP-Parser版本。这可以称为“愚蠢_”_冲突:仅当您尝试包含与现有应用程序不兼容的依赖项时才会发生冲突。这两个包不需要相互兼容,您的应用程序不会直接使用它们,它们也不会执行您的应用程序代码。另一个例子你写了一个Symfony和Laravel链接器库。你想包括Symfony和Laravel作为依赖项来测试链接它们:{"require-dev":{"symfony/framework-bundle":"^4.0","laravel/framework":"~5.5.0"#gentle提醒Laravel#packagesarenotsemver}}在某些情况下可能有效,但在大多数情况下可能会失败。这个场景可能有点傻,因为你不太可能同时包含这两个包,你更不可能想要支持这个场景。请参阅示例composer.json以获取不可测试的依赖项:{"require":{"symfony/yaml":"^2.8||^3.0"},"require-dev":{"symfony/yaml":"^3.0"}}如上例...只能安装指定版本的SymfonyYAML组件(可用的symfony/yaml包版本是[3.0.0,4.0.0[。在一个应用程序中,你不需要关心大多数情况下都是关于这些的。但是作为一个组件库,这可能是一个问题。实际上,这意味着您无法在组件库中测试symfony/yaml[2.8.0,3.0.0[]。这是否真的是一个问题在很大程度上取决于您的情况。请注意,这可能会发生,并且没有有效的方法来阻止它。上面的例子很简单,但是如果这个symfony/yaml:^3.0隐藏在依赖树的更深处,eg:{"require":{"symfony/yaml":"^2.8||^3.0"},"require-dev":{"acme/foo":"^1.0"#requiressymfony/yaml^3.0}}至少现在你无法知道。解决方案不要使用依赖项,没关系,毕竟你并不真的需要这种依赖项PHP官网。以PhpMetrics为例,静态分析工具:$wget-ophpmetrics.phar$chmod+xphpmetrics.phar$mvphpmetrics.phar/usr/local/bin/phpmetrics版本1.9.0#或者如果你想保持PHAR关闭并且不介意.phar#扩展名:$phpmetrics.phar--versionPhpMetrics,版本1.9.0警告:将代码打包为PHAR不会像JAR那样隔离代码在Java中嘛,即便如此还有一个正在开发的项目PHP-Scoper来解决这个问题。我们举个例子来说明这个问题。您构建了一个控制台应用程序myapp.phar,它依赖于SymfonyYAML2.8.0来执行给定的PHP脚本:$myapp.pharmyscript.php您的脚本myscript.php使用的是Composer引入的SymfonyYAML4.0.0。可能发生的情况是PHAR加载了一个SymfonyYAML类,例如,SymfonyYamlYaml来执行你的脚本,你的脚本也依赖于SymfonyYamlYaml,但是你猜怎么着,这个类已经被加载了。问题是加载了symfony/yaml2.8.0包,而不是您的脚本需要的4.0.0。因此,如果API不同,这将很难破解。长话短说:博士;PHAR适用于PhpStan或PhpMetrics等静态分析工具,但由于存在一些依赖冲突,一旦代码运行,它就会变得不那么可靠(至少目前如此!)。使用PHAR时,还有一些其他事项需要牢记:它们很难跟踪,因为Composer中没有对它们的原生支持。但是,存在一些解决方案,例如此Composer插件tooly-composer-script或PhiVePHAR安装程序如何管理版本取决于项目。一些项目提供具有不同稳定渠道的“自我更新”命令,一些项目提供独特的下载端点和最新版本,一些项目使用GitHub版本并为每个版本发布PHAR。使用多个存储库是迄今为止最流行的技术之一。因此,我们不需要将所有桥依赖项都放在一个composer.json中,而是将这个包拆分到多个存储库中。如果我们调用前面的示例acme/foo,那么我们将为Symfony创建另一个bundleacme/foo-bundle并为Laravel创建acme/foo-provider请注意,所有内容实际上仍然可以在单个存储库中,并且只有只读存储库其他软件包,如Symfony。这种方法的主要优点是它仍然相对简单并且不需要任何额外的工具,除了像splitsh这样的最终存储库拆分器,例如Symfony、Laravel和PhpBB。缺点是您现在需要维护多个包,而不是一个。调整配置的另一种方法是使用更高级的安装和测试脚本。与前面的例子相比,我们可以做一些其他的事情:#!/usr/bin/envbash#bin/tests.sh#测试核心库vendor/bin/phpunit--exclude-group=laravel,symfony#测试Symfony框架composerrequiresymfony/framework-bundle:^4.0vendor/bin/phpunit--group=symfonycomposerremovesymfony/framework-bundle#测试Laravel框架composerrequirelaravel/framework:~5.5.0vendor/bin/phpunit--group=根据我的经验,symfonycomposerremovelaravel/framework是可行的,但这会导致测试脚本臃肿,运行速度相对较慢,难以维护并且对新贡献者不太友好。使用多个作曲家.json方法相对较新(在PHP中),主要是因为所需的工具不容易获得,所以我将进一步讨论这个解决方案。这个想法比较简单,例如下面:{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0","phpstan/phpstan":"^1.0@dev","phpmetrics/phpmetrics":"^2.0@dev"}}我们将使用不同的方式安装phpstan/phpstan和phpmetrics/phpmetrics作曲家.json文件。但这首先提出了一个问题:我们把它们放在哪里?使用哪种结构?composer-bin-plugin应运而生。一个非常简单的Composer插件,允许您与不同目录中的一个composer.json进行交互。因此,假设我们有一个composer.json根文件{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0"}}我们能够安装插件:$composerrequire--devbamarni/composer-bin-plugin现在插件已经安装,无论何时执行composerbinacmesmth,composersmth命令将在子目录vendor-bin/acme中执行。所以我们现在可以像这样安装PhpStan和PhpMetrics:$composerbinphpstanrequirephpstan/phpstan:^1.0@dev$composerbinphpmetricsrequirephpmetrics/phpmetrics:^2.0@dev这将创建以下目录结构:...#projects文件/目录composer.jsoncomposer.lockvendor/vendor-bin/phpstan/composer.jsoncomposer.lockvendor/phpmetrics/composer.jsoncomposer.lockvendor/其中vendor-bin/phpstan/composer.json看起来像这样:{"require":{"phpstan/phpstan":"^1.0"}}和vendor-bin/phpmetrics/composer.json看起来像这样:{"require":{"phpmetrics/phpmetrics":"^2.0"}}所以现在我们可以调用vendor-bin/phpstan/vendor/bin/phpstan和vendor-bin/phpmetrics/vendor/bin/phpstan来轻松使用PhpStan和PhpMetrics。现在我们更进一步,以一个库在不同框架中的引用为例{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0","symfony/framework-bundle":"^4.0","laravel/framework":"~5.5.0"}}因此,Symfony上面引用的供应商-bin/symfony/composer.jsonfile:{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0","symfony/framework-bundle":"^4.0"}}Laravel引用的vendor-bin/laravel/composer.json文件:{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0","laravel/framework":"~5.5.0"}}我们的根composer.json现在应该是这样的:{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"bamarni/composer-bin-plugin":"^1.0""phpunit/phpunit":"^6.0"}}用于测试核心库之间的引用关系,你需要创建3个不同的单元测试文件,每个文件都有autoload.php(eg:Symfony的参考文件vendor-bin/symfony/vendor/autoload.php)如果你真的去尝试,你会发现这种方法的一大缺点可以发现:冗余配置。确保你需要将根配置文件composer.json复制到另外两个vendor-bin/{symfony,laravel}/composer.json,调整自动加载变化的文件路径,当你需要一个新的依赖时,你需要它在另一个composer.json中包含它。这是不可控的,所以出现了composer-inheritance-plugin。这个小型包装器插件composer-merge-plugin将vendor-bin/symfony/composer.json内容合并到根composer.json中。因此,而不是以下内容:{"autoload":{...},"autoload-dev":{...},"require":{...},"require-dev":{"phpunit/phpunit":"^6.0","symfony/framework-bundle":"^4.0"}}现在这个:{"require-dev":{"symfony/framework-bundle":"^4.0","theofidry/composer-inheritance-plugin":"^1.0"}}其他配置、自动加载和依赖项将包含在根composer.json中。无需配置,composer-inheritance-plugin是一个精简包composer-merge-plugin,用于预配置任何使用composer-bin-plugin的东西。你可以通过以下方式检查它需要安装的依赖项:$composerbinsymfonyshow我在很多项目中都使用这种方法,比如alice,不同的静态分析工具和框架桥,比如PhpStan和PHP-CS-Fixer。另一个例子是alice-data-fixtures,它有许多不同的ORM桥梁持久层(DoctrineORM、DoctrineODM、EloquentORM等)和框架集成。作为phars的替代品,我已经在几个私人项目中使用过它,而且效果很好。结论我敢肯定有些人会发现某些方法很奇怪或弃用它们。这里的目标不是去判断或推荐一个特定的东西,而是列出一些可能的方式来管理一些依赖,以及每个依赖的优点和缺点。因此,根据您的问题和个人喜好,选择最适合您的那一款。正如他们所说,没有解决方案,只有权衡取舍。
