在一个非常抽象的例子中:我有一些模板或文件,我想在包A中加载,它们在第三方包B中,它们都加载在应用程序Z中,例如:
{
"name": "application/Z",
"require": {
"lib/A": "*"
}
}
和:
{
"name": "lib/A",
"require": {
"libB": "*"
}
}
现在我喜欢做这样的事情:
<?php
namespace libA;
class TemplateLoader {
public function getInclude() {
return file_get_contents(__DIR__ . '/../../vendor/lib/b/templates/my.inc');
}
}
这有效,但使用相对路径似乎是一个坏主意,尤其是在单元测试中,路径以 '../vendor'
开头。所以路径不可靠。
现在我的问题:是否可以像在自动加载器中使用的那样从作曲家那里获取供应商路径?或者对于这样的实现是否有任何最佳实践?
创建函数LibBTemplates::getIncludePath()
的明显解决方案是不可能的,因为我无法访问此库。
假设lib/B
的composer.json包含:
"autoload": {
"psr-0": {
"lib\B": "src/"
}
},
要回答这个问题,您可以使用作曲家的类加载器,因此您的libATemplateLoader
可能如下所示:
namespace libA;
use ComposerAutoloadClassLoader;
class TemplateLoader {
/** @var ClassLoader */
protected $loader;
public function __construct(ClassLoader $loader)
{
$this->loader = $loader;
}
public function getInclude() {
$libs = $this->loader->getPrefixes();
if (!isset($libs['lib\B'])) {
throw new RuntimeException("Package lib/B is not loaded. Be sure it is included in composer.json and you ran 'compose install'.");
}
$path = $libs['lib\B'][0];
// At this point $path contains base path for classes in lib/B package,
// depending on 'autoload' directive of lib/B's composer.json above.
// In this example it is '/full/path/to/vendor/lib/B/src'
$parts = explode('/', $path);
array_pop($parts);
$path = implode('/', $parts);
return file_get_contents($path . '/templates/my.inc');
}
}
正确的方法是分叉lib/B
并在那里添加一个模板加载器:
namespace libB;
class TemplateLoader {
public static function getTemplate($name) {
return file_get_contents(__DIR__ . '/../templates/'.$name);
}
}
所以在libA
中很简单:
namespace libA;
class TemplateLoader {
public function getInclude() {
return libBTemplateLoader::getTemplate('my.inc');
}
}
至于找到作曲家的方法vendor-dir
,我见过一些尝试做这样的事情,这些尝试非常脆弱,我不推荐。
-
一个非常糟糕的方法是通过在两个常见位置查找
autoload.php
来对上下文做出假设。 -
稍微好一点的方法是对
spl_autoload_functions
结果使用反射来确定自动加载器来自的文件。
理想的方法是将项目中的composer/composer
用作库,并在本地存储库中查找包元数据。
我推荐的一种非常简单的方法是使用模板加载器配置路径。这可能允许你拥有多个第三方库,这些库在存储库中的放置位置具有不同的约定。