从PHP中的自动加载类加载扩展类


Loading an extended class from an autoloaded class in PHP

我试图找到类似的问题或场景,但我找不到。我有以下结构:

  • 类/
    • dev/
      • class1.php
      • class2.php
    • class1.php
    • class2.php
    • class3.php

然后,代码调用:

$class1 = new class1();
$class2 = new class2();
$class3 = new class3();

"dev"文件夹包含应覆盖根类的类。"dev"文件夹中有一个扩展的class1,还有class2,但没有class3。目前,我正在尝试在autoload函数中加载这个扩展类,它可以工作,但不是我所期望的方式:D

它不是从根文件夹中加载class1,然后从"dev"文件夹中加载class1,而是仅在离开自动加载功能后才加载根类。简化代码,我有这样的东西:

function $autoloader($class) {
    if (!file_exists($class)) {
        echo 'failed - fallback or do something else';
        return false;
    }
    require_once($class);
    // Instantiates extended class from 'dev'
    if (file_exists($classFromDevFolder)) {
        require_once($classFromDevFolder);
        $newClass = new $classFromDevFolder();
        unset($newClass);
    }            
}

它可以工作,从"dev"文件夹加载类,并精细地扩展class1。但是,如前所述,它在根类之前实例化dev类。我假设是因为它在自动加载函数中加载dev类,即使在调用根类之后也是如此。

你们对如何实现这一目标有什么想法吗?我还想创建另一个函数来加载"dev"类,但我不知道该把它放在哪里,以便让这个函数在任何类上都能工作。


我添加了这个非常简化的代码示例来更好地说明和澄清我想要实现的目标。

根文件夹:

class Class1() {
    ...
    public function __construct() {
        $this->x = 1;
    }
    ...
}

开发文件夹:

class Class1_Dev extends Class1() {
    ...
    public function __construct() {
        $this->x = 2;
    }
    ...
}

自动加载器:

function autoloader($class) {
    if (!file_exists($class)) {
        echo 'failed - fallback or something else';
        return false;
    }
    require_once($class);
    ...
    $devClass = 'dev/' . $class;
    if (file_exists($devClass)) {
        require_once($devClass);
        $newClassName = $class . '_Dev';
        // Here is the issue: it instantiates Class1_Dev before Class1,
        // and ultimately, Class1 keeps 'x' as 1, not 2
        $newClass = new $newClassName();
    }
}

代码:

$class1 = new Class1();
var_dump($class1->x);

$class->x应该是2,而不是1。

如果我正确理解您的问题,您希望首先从dev文件夹加载类,或者如果它们不存在,则从根文件夹加载。您可以通过spl_autolaod_register:轻松实现这一点

// Please be aware that anonymous function are first available for spl_autoload_register as of PHP 5.3.0
spl_autoload_register(function ($class) {
    include 'classes/dev/' . $class . '.php';
    if (! class_exists($class)) {
        include 'classes/' . $class . '.php';
    }
}); 

然后你应该能够将你的课程称为

$class1 = new class1();
$class2 = new class2();
$class3 = new class3();

理想情况下,从dev文件夹加载class1和class2,从root文件夹加载class3。


更新:

正如评论中已经发布的那样,如果您加载两个具有相同名称的类,则第一个加载的类将是持久的类,如果您尝试加载另一个具有相同类名的类,这将失败。

我不太清楚你为什么要这么做?

但是,看看您的代码,当使用我之前发布的自动加载方法时,我可以想出一些实现上述目标的方法。

例如,使用实现共享逻辑的基类,然后将其扩展到不同文件夹中的类:

根文件夹

class BaseClass1 {
   protected $devMode;
   protected $x;
   public function __construct() {
        $this->devMode = false;
        $this->x = 0;
   }
   public function isDevClass() {
        return $this->devMode;
   }
   public function getX() {
        return $this->x;
    }
}

开发文件夹:

class Class1 extends BaseClass1 {
    public function __construct() {
        $this->devMode = true;
        $this->x = 2;
    }
    ...
}

根文件夹:

class Class1 extends BaseClass1 {
    public function __construct() {
        parent::__construct();
        $this->x = 1;
    }
    ...
}

现在,根据情况,如果我们在dev文件夹中有一个class1类,它的构造函数覆盖基类,我们可以确定该类是从哪里加载的:

$class1 = new class1();
// Check if devMode is set (false by default)
if ($class1->isDevClass()) {
    // Class1 was loaded from dev, do bla; 
} else {
   // Class1 was loaded from root, do bla; 
}
// or
echo $class1->getX(); // will return 1 or 2 depending on the folder the class was loaded from

但不确定这是否是你想要的。还有其他挑衅的方式可以做到这一点。您应该尽可能减少冗余。最终,您宁愿定义一个站点范围的常量,然后将其作为标识符传递给类的构造函数。然后,您可以根据构造类时设置的内容,在类内部进行细微的更改。

例如,完全删除多余的dev文件夹:

define("DEV", true);
...
class Class1 {
    protected $devMode;
    protected $x;
    public function __construct($mode = false) {
        $this->devMode = $mode;
        if ($this->devMode) {
            $this->x = 2;
        } else {
            $this->x = 1;
        }
    }
    public function doSomething() {
        if ($this->devMode) {
            // Do only devMode specific stuff
        }
    }
    ...
}
...
$class1 = new class1(DEV);

希望这能有所帮助。