问题描述
我想知道是否有办法根据 PHP 版本加载不同的文件夹。我可以编写自己的自动加载器,但我想知道是否可以使用 Composer 来做到这一点?
附言。我之前在模块/插件应用程序中看到过这种情况,这些应用程序在全球重新分发以与广泛的环境一起使用。那些使用自己的自动加载类的脚本。
我很好奇有没有办法以类似的方式使用作曲家。
场景:类/文件夹结构:
class >
PHP5.6 >
- SomeClass.PHP
...
PHP7.x >
- SomeClass.PHP
...
PHP8.x >
- SomeClass.PHP
...
比较PHP版本并做一些事情:
$classpathForAutoloader = '';
if (version_compare(PHP_VERSION,'8.0.0') >= 0) {
$classpathForAutoloader = 'PHP8.x';
// do something to composer autoload or
// use declaration
}else if(version_compare(PHP_VERSION,'7.0.0') >= 0){
$classpathForAutoloader = 'PHP7.x';
// do something to composer autoload or
// use declaration
}else if(version_compare(PHP_VERSION,'5.6.0') >= 0{
$classpathForAutoloader = 'PHP5.6';
// do something to composer autoload or
// use declaration
}else{
// throw Exception ...
}
标准作曲家设置:
{
"name": "some/name","require": {
},"authors": [
{
"name": "Some Name","email": "some@email.com"
}
],"autoload": {
"psr-4": {
"Devwl\\": "class/","Tools\\": "tools/"
},"classmap": [
"class/"
],"exclude-from-classmap": []
}
}
解决方法
我认为 Composer 没有提供动态自动加载不同路径的方法。
您能否在单个 version_compare
类中仅在功能因 PHP 版本而异的情况下使用 SomeClass.php
?还是将整个 SomeClass.php
写成向后兼容?
对我来说,根据 PHP 版本加载不同的类在跨环境的可重现性方面会带来麻烦。
另一种选择是使用 require_once
来加载不同的类,但为了可维护性,我真的倾向于只在绝对必要时进行版本检查的单个类。
所以我创建了一个类来满足我的需求。它可以与 Composer 自动加载器一起使用。
<?php
class PhpVersionAutoloader{
private $baseClassesDirPath = null;
private $phpVersionArr = [];
private $phpDir = null;
private $classes = []; /** Keep a record of all loaded classes */
/**
* Allows to change base dir path.
* If not set the path will be set to file _DIR_
*
* @see $this->baseClassesDirPath
*
* @param string $path
* @return void
*/
public function setBaseClassesDirPath($path)
{
$this->baseClassesDirPath = $path;
}
/**
* Map available dir name and php version
*
* @see $this->phpVersionArr
*
* @param string $directory name
* @param string $phpVersion
* @return void
*/
public function registerPhpDir($dir,$phpVersion){
$this->phpVersionArr[] = [$dir => $phpVersion];
}
/**
* Compare curent php version with $this->phpVersionArr to determin the right path for class load
*/
public function selectPhpDir(){
foreach ($this->phpVersionArr as $key => $phpVDir) {
$this->position = $key;
foreach($phpVDir as $key => $value){
if (version_compare(PHP_VERSION,$value) >= 0){
$this->phpDir = $key;
break 2;
}
}
}
}
/**
* Register autloader
*
* @return void
*/
public function register(){
spl_autoload_register(function($className)
{
$namespace = str_replace("\\","/",__NAMESPACE__);
$className = str_replace("\\",$className);
$this->baseClassesDirPath = ($this->baseClassesDirPath === null) ? str_replace("\\",__DIR__) : $this->baseClassesDirPath;
$class = $this->baseClassesDirPath."/classes/".$this->phpDir.'/'. (empty($namespace)?"":$namespace."/")."{$className}.php";
$this->classes[] = $class;
if (file_exists($class)){
include_once($class);
}else{
// ... if not exsist try to load lower php version file?
// ... or throw new Error("Error Processing Request",1);
}
});
}
}
像这样使用 PhpVersionAutoloader 对象:
/**
* Use example
*/
$loader = new PhpVersionAutoloader(); // init PhpVersionAutoloader object
$loader->setBaseClassesDirPath('C:/xampp/htdocs/blog/blog autoloading'); // if not used will use _DIR_ to create path
$loader->registerPhpDir('php8.x','8.0.0'); // as "folder name" => "php version"
$loader->registerPhpDir('php7.x','7.0.0'); // ...
$loader->registerPhpDir('php5.6','5.6.0'); // ...
$loader->selectPhpDir(); // compare system php version and selects the correct phpX.X subfolder
$loader->register(); // register autoloader
我还创建了这个加载器的更多功能类,它允许强制从特定目录加载,甚至允许从旧版本的 PHP 加载类(如果在所选版本中找不到)。
见github [here]