为PHP编写Bulletproof __autoload函数

时间:2010-07-17 20:45:03

标签: php autoload

我正在尝试将我的PHP __autoload函数定义为尽可能灵活且灵活。

以下是我的应用程序结构的细分:

/dev (root)
    /my_app
        /php
            /classes
                - Class1.php
                - Class2.php
                - Class3.php
        /scripts
            myscript.php (the file I have to include the classes in)

这是相当直接的。我的问题是:如何编写我的__autoload函数,以便我可以包含我想要的任何类,无论调用文件在目录结构中的嵌套程度如何。我知道它与__FILE__realpathdirname函数有关,但我不确定将它们组合起来以实现我之后的灵活性的适当方式

这是我做的快速测试:

<?php
echo realpath(dirname(__FILE__)) . "/php/classes/Class1.php";
?>

结果:

/home/mydirectory/dev.mysite.com/my_app/php/scripts/php/classes/Class1.php

如您所见,结果与类所在的位置不匹配。但是,如果我将myscript.php文件移动到/ my_app文件夹中,它将正确打印。

关于使这更灵活的建议?

4 个答案:

答案 0 :(得分:3)

我建议调查spl_autoload。只需将适当的目录添加到include_path

即可

这样的事情可能有助于你开始:

ini_set($your_class_dir_here .PATH_SEPERATOR. ini_get('include_path'));

您必须使用spl_autoload_register或小写所有文件名提供自己的自动加载器。

这是我自己的一个自动加载器,它使用php命名空间来克服一些目录问题。

<?php

namespace Red
{
    // since we don't have the Object yet as we load this file, this is the only place where this needs to be done.
    require_once 'Object.php';

    /**
     * Loader implements a rudimentary autoloader stack.
     */
    class Loader extends Object
    {
        /**
         * @var Loader 
         */
        static protected $instance = null;

        /**
         * @var string 
         */
        protected $basePath;

        /**
         * @return Loader
         */
        static public function instance()
        {
            if (self::$instance == null)
            {
                self::$instance = new self();
            }
            return self::$instance;
        }

        /**
         * Initialize the autoloader. Future expansions to the 
         * autoloader stack should be registered in here.
         */
        static public function Init()
        {
            spl_autoload_register(array(self::instance(), 'autoLoadInNamespace'));
        }

        /**
         * PHP calls this method when a class is used that has not been
         * defined yet. 
         * 
         * I'm returning a boolean for success which isn't required (php ignores it)
         * but makes life easier when the stack grows.
         * 
         * @param string $fullyQualifiedClassName
         * @return boolean 
         */
        public function autoLoadInNamespace($fullyQualifiedClassName)
        {
            $pathParts = preg_split('/\\\\/', $fullyQualifiedClassName, -1, PREG_SPLIT_NO_EMPTY);
            array_unshift($pathParts, $this->basePath);
            $pathToFile = implode(DIRECTORY_SEPARATOR, $pathParts) . '.php';

            if (file_exists($pathToFile))
            {
                require_once $pathToFile;
                return true;
            }
            return false;
        }

        /**
         * Constructor is protected because we don't want multiple instances
         * But we do want an instance to talk to later on.
         */
        protected function __construct()
        {
            $this->basePath = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..');
        }
    }
}

#EOF;

它是Loader命名空间中名为\Red的类的一部分,并从一个简单的引导文件初始化:

<?php
// This is where the magic is prepared. 
require_once implode(DIRECTORY_SEPARATOR, array(dirname(__FILE__), 'Red', 'Loader.php'));
// Initialize the autoloader, no more require_once for every class
// and everything is OOP from here on in.
Red\Loader::Init();

#EOF

答案 1 :(得分:1)

我不会选择自动文件位置检测。除非仔细考虑它可能是一个安全漏洞,否则您需要设计一个支持子目录的命名原理图,它会强制您为每个类使用一个文件(这可能是好事也可能是坏事,但我发现它不灵活)。我会使用全局或静态数组,你保持映射className =&gt; pathToClass。

答案 2 :(得分:1)

$_SERVER['DOCUMENT_ROOT']应包含Web服务器根目录的完整路径。从那里你应该能够继续通过文件夹结构到类目录的路径。如果您将应用程序名称存储在会话中,则可以在任何地方使用相同的代码。

//set in config file
if(!isset($_SESSION['APP_DIR'])) $_SESSION['APP_DIR'] = "my_app";

//autoload
//builds a string with document root/app_name/classes
//takes the app name and replaces anything not alphanumeric or _ with an _ and
//makes it lowercase in case of case sensitive. as long as you follow this naming
//scheme for app directories it should be fine for multiple apps.
$classPath = $_SERVER['DOCUMENT_ROOT'] . '/' .
           strtolower(preg_replace('/\W+/', '_', $_SESSION['APP_DIR'])) .
           '/classes/';

答案 3 :(得分:0)

基本上有两种方式。您可以指定类目录(/home/mydirectory/dev.mysite.com/my_app/php/classes/)的完整绝对路径,我不建议这样做,因为它涉及更改主机时更改绝对路径。或者您可以使用相对路径,这样更容易和便携:

require_once '../classes/'.$classname;

无需在此处获取realpath,PHP可以使用相对路径。 ;)

PS:我认为realpath(dirname(__FILE__))是重复的。 __FILE__已经是“真正的路径”,因此您无需致电realpath