强制特定类的自动加载

时间:2015-08-09 04:22:20

标签: php autoload spl-autoloader

This previous question显示了如何强制所有类的自动加载。

但是我需要自己强制自动加载一个类。我怎么能这样做?

它不能:

  • 参与更改课程的源代码
  • 依赖于该类源代码的任何部分(方法,变量,修饰符,例如它必须可以自由地从具体变为抽象而不影响它)。

此外,最好不要将类的名称编码为字符串。 (帮助IDE重构等)。

我到目前为止找到的最佳选择是使用spl_autoload_call()

spl_autoload_call("Jodes\\MyClass");

或非命名空间类:

spl_autoload_call("MyClass");

2 个答案:

答案 0 :(得分:1)

我最近有同样的需求。执行require_once不是一个选项,因为由于一些更复杂的规则,自动加载器确实需要定位类,所以无法准确知道该类文件的路径。

虽然函数spl_autoload_call($classname)的目的是为了做到这一点,但是它存在一个根本性的缺陷:它会抛出致命错误,以防你为同一个类名调用它两次甚至如果该类的某些子类已经加载。这是因为它无法在PHP中重新声明类:

<?php

spl_autoload_call('TheClass');
// no problem

spl_autoload_call('TheClass');
// PHP Fatal error:  Cannot declare class TheClass, because the name is already in use in ... on line ...

我对此问题的解决方案不依赖于class_exists($classname)的副作用,这种副作用并非为此目的而设计,而是更具可配置性,因此可以更好地控制触发自动加载器。

更好的是,它在多次调用或者已经在继承链中加载了某些内容时完全没有问题。它只是具有需要文件的副作用(如果你想),如果该类还没有!

<?php

class_exists('TheClass');
// no problem

class_exists('TheClass');
// already exists. No need to autoload it and no fatal errors!

通过这种方式,您可以通过自动加载器加载类,这是一种安全且幂等的方法。

而且,如果您不想在其中使用类名对字符串进行硬编码,那么从PHP 5.5开始,您可以使用::class伪常量,该常量在编译时解析为具有完全限定类名的字符串(包括名称空间):

<?php

class_exists(TheClass::class);

答案 1 :(得分:0)

我对此采取了不同的方法,可能不是最好的方法,但它对我来说是最好的方法之一。它确实涉及修改源代码,但实现起来非常简单。

我通常有一个名为Core

的主要类
<?php
require_once("{$_SERVER['DOCUMENT_ROOT']}\Matilda\class\Settings.class.php");
require_once("{$_SERVER['DOCUMENT_ROOT']}\Matilda\class\Auth.class.php");
require_once("{$_SERVER['DOCUMENT_ROOT']}\Matilda\class\Permissions.class.php");
require_once("{$_SERVER['DOCUMENT_ROOT']}\Matilda\class\Procedures.class.php");
require_once("{$_SERVER['DOCUMENT_ROOT']}\Matilda\class\Audit.class.php");

class Core {

    /**
     * @var External class
     */
    public $Settings,$Sql,$Auth,$Permissions,$Payroll,$Customers,$Plans,$Billing,$Engin,$AAPT,$Stock,$Prospects,$Freeside,$Audit;

    /**
     * @var Base Config
     */
    private $InitConfig = array(
        'Payroll'           => false,
        'Customers'     => false,
        'Plans'             => false,
        'Billing'           => false,
        'Engin'             => false,
        'AAPT'          => false,
        'Stock'             => false,
        'Prospects'         => false,
        'Freeside'      => false,
    );

    public function __construct($Config = array()) {    
        // Session instantiation
        if (!isset($_SESSION)) session_start();

        if (!is_array($Config)) die('Core instantiation parameter must be an array, even if its empty');
        $this->InitConfig = array_merge($this->InitConfig, $Config);

        // Base classes
        $this->Settings             = new Settings();
        $this->Sql                  = new MySQLi(/* Mysql info */)
        $this->Audit                = new Audit($this->Settings, $this->Sql);
        $this->Auth                 = new Auth($this->Settings, $this->Sql, $this->Audit);
        $this->Permissions          = new Permissions($this->Settings, $this->Sql, $this->Auth, $this->Audit);

        // Optional class handling
        foreach ($this->InitConfig as $Dependency => $State) {
            if ($State == true) {
                require_once($this->Settings->RootDir . "/class/{$Dependency}.class.php");
                $this->$Dependency = new $Dependency($this->Settings, $this->Sql, $this->Auth, $this->Permissions, $this->Audit);
            }
        }

    }

这几乎说明了我如何提供可选的加载配置。

它有5个默认类,因此无论如何都会加载:设置,验证,权限,程序和审计

然后我可以实例化可选类:$Core = new Core(array('Payroll' => true)),它将实例化Payroll.class.php并可通过$Core->Payroll

访问

这几乎是存储树级功能的好方法

 $Core->Members->FetchMemberByID($ID);
 $Core->Plans->CreateNewPlan($PlanName, $Monthly, $Attributes = array())
 etc etc.

有些人可能会因此而打我,但我个人认为这是一种相对不错的方式来实现你想要的,尽管不是你通常喜欢的方法(不是改变代码)。

同样方便的是允许为您正在制作的任何内容创建插件:)