【问题标题】:spl_autoloader: Limit scopespl_autoloader:限制范围
【发布时间】:2014-05-05 10:18:16
【问题描述】:

如果我正确地理解了 SPL Autoloader 的行为,它会在“全局范围”内发挥作用,对吗?如果我们有,比如说,这个代码:

class Autoloader {

    protected static $instance = NULL;

    public static function get_instance() {
        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }

    public function init() {
        spl_autoload_register(array($this,'autoload'));
    }

    private function autoload() {
        // to the autoload magic
    }

}

$autoloader = new Autoloader();
$autoloader->init();

// or

add_action('muplugins_loaded', array(Autoloader::get_instance(),'init'));

...它适用于应用程序的其余部分,或者如果挂钩到 Wordpress 操作,从挂钩开始,对吗?

在我看来,这不是很方便,尤其是如果您在较大的框架(例如 Wordpress)的框架中工作。有没有办法,如何通过以下方式将 SPL Autoload 的范围限制在特定的上下文中:

  1. 以不同的方式定义 SPL 自动加载?
  2. 在函数中有一些特定的东西(例如 return false 上的名称没有太多某种模式的类)?
  3. 更聪明的东西?

我知道,我可以在autoload() 函数中添加一些条件语句来避免冲突和错误,但这似乎不是很有效。

谢谢!是的,很可能我只是忽略了一些东西。

编辑

通过命名空间模拟目录结构:在 Wordpress 中实际上是不可能的,是吗?如果您追求的是某种沟通和逻辑结构,例如,必须使用的插件主题。 (@RoyalBg)

找不到文件:如何确定什么时候找不到文件“可以”,什么时候不可以?通过函数的逻辑?在我看来,缩小自动加载器的范围将是更优雅的解决方案。 (@Jon,@Mark Ba​​ker)

但如果我很了解您的 cmets,SPL Autoloader 默认会真正“全局”应用。

【问题讨论】:

  • 对于3. 是的,有命名空间:-)
  • 2 也适用,如果一个应用程序中有多个自动加载器,那么 PHP 将遍历所有嵌套的自动加载器,直到自动加载魔法返回非错误
  • @RoyalBg:你能详细说明和/或举一个简单的例子吗?我也试图围绕命名空间进行思考,而 Wordpress 对我来说并不容易。 :)
  • 不知道你在这里追求什么。您可以根据需要注册任意数量的自动加载器,只要它们都表现良好,如果它们不想处理特定的类,它们就什么也不做。 PHP 会自动依次尝试每个自动加载器,直到其中一个加载类。
  • @PetrCibulka 我不知道你的实际问题是什么,举个例子,但如果你的类用命名空间很好地定义,它解决了一个目录结构,你永远不会加载错误类,您将在当前命名空间所在的目录树范围内工作。

标签: php wordpress autoload spl-autoload-register


【解决方案1】:

您可以注册多个自动加载器功能/方法。 SPL 将遍历它们,直到加载所需的类。

<?php
function loaderA($name) { // Do Stuff }
function loaderB($name) { // Do Other Stuff }
spl_autoload_register('functionA');
spl_autoload_register('functionB');
?>

SPL 会一个接一个地遍历这些注册函数。首先是functionA,然后是functionB,如果请求的类不是通过functionA加载的。

编辑:

final class LoaderException extends \Exception{}

class Loader {

    /**
     * Project-Root-Directory
     * @var string
     */
    protected static $AbsPath;
    /**
     * Directory-List
     * @var array
     */
    protected static $DirList;

    /**
     * DO NOT INSTANTIATE
     */
    private function __construct() {}

    /**
     * The actual autoloader.
     * @param string $name Class-Name
     * @return void
     * @throws LoaderException
     */
    public static function load($name) {
        if(!is_string($name))
            throw new \InvalidArgumentException('Argument is not a string.');
        $a = isset(static::$AbsPath) ? static::$AbsPath : '';
        $f = str_replace('\\', '/', $name).'.php';
        if(!is_array(static::$DirList)) {
            if(file_exists($a.$f))
                return include $a.$f;
            if(file_exists(getcwd().$f))
                return include getcwd().$f;
            throw new LoaderException('Unable to load "'.$name.'".');
        }
        foreach(static::$DirList as $d) {
            if(file_exists($a.$d.$f))
                return include $a.$d.$f;
        }
        throw new LoaderException('Unable to load "'.$name.'".');
    }

    /**
     * Registers the Loader.
     */
    public static function register() {
        spl_autoload_register(__CLASS__.'::load');
    }

    /**
     * Unregisters the Loader.
     */
    public static function unregister() {
        spl_autoload_unregister(__CLASS__.'::load');
    }

    /**
     * Adds one, or more, directories to the Directory-List.
     * @throws LoaderException
     */
    public static function addDir() {
        foreach(func_get_args() as $k => $v) {
            if(!is_string($v))
                throw new \InvalidArgumentException('Argument #'.($k+1).' is not a string.');
            if(!is_dir($v))
                throw new \InvalidArgumentException('Argument #'.($k+1).' is not a directory.');
            if(!is_array(static::$DirList) or !in_array($v, static::$DirList))
                static::$DirList[] = $v;
        }
    }

    /**
     * Removes one, or more, directories from the Directory-List.
     * @throws LoaderException
     */
    public static function removeDir() {
        foreach(func_get_args() as $k => $v) {
            if(!is_string($v))
                throw new \InvalidArgumentException('Argument #'.($k+1).' is not a string.');
            if(in_array($v, static::$DirList))
                unset(static::$DirList[array_search($v, static::$DirList)]);
        }
    }

    /**
     * Sets the Absolute-Path for the loader, this will speed up the loading process.
     * @param string $path Absolute-Path to the Project root directory
     * @throws LoaderException
     */
    public static function setAbsPath($path = null) {
        if(isset($path)) {
            if(!is_string($path))
                throw new \InvalidArgumentException('Argument is not a string.');
            if(!is_dir($path))
                throw new \InvalidArgumentException('Invalid path "'.$path.'".');
        }
        static::$AbsPath = $path;
    }

}

这是我经常使用的基本自动加载器,它将命名空间识别为子目录,并且可以提供搜索类的特定目录。

【讨论】:

  • 感谢@BrainInBlack,我使用了类似的方法,导致由于无法加载一些晦涩的 Wordpress 类而引发错误/异常。这就是为什么我问如果找不到文件/类,如何“限制”自动加载器的“范围”并保留错误报告。不过谢谢,我一定会检查你的自动加载器命名空间,这对我来说仍然是非常陌生的话题!
  • 好吧,据我所知,wordpress 有它自己的自动加载器,也就是说,你必须让自动加载器返回,而不是抛出异常。
  • 但是我必须通过自动加载器功能的逻辑来确定什么时候可以,如果找不到文件,什么时候找不到......不,Wordpress没有它的自动加载器,AFAIK:core.trac.wordpress.org/ticket/21300
  • 刚刚查看了最新版本的 wordpress,它们没有使用任何命名空间,也就是说,如果没有声明命名空间,您可以修改自动加载器以不触发。
  • 是的,命名空间听起来像是一条路。现在,我正在尝试找出与 Wordpress 逻辑兼容的正确方法。我将问题发布到 Wordpress Stackexhange,如果您有兴趣,请在此处查看:wordpress.stackexchange.com/questions/143400/…
猜你喜欢
  • 2011-08-11
  • 1970-01-01
  • 2021-11-07
  • 2021-10-22
  • 2011-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多