Написание функции Bulletproof __autoload для PHP - PullRequest
3 голосов
/ 18 июля 2010

Я пытаюсь определить мою функцию 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__, realpath и dirname, но я не уверен в том, как правильно комбинировать их для достижения желаемой гибкости.

Вот быстрый тест, который я сделал:

<?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 ]

3 голосов
/ 18 июля 2010

Я бы предложил посмотреть в 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 голос
/ 18 июля 2010

$_SERVER['DOCUMENT_ROOT'] должен содержать полный путь к корневому каталогу вашего веб-сервера. Оттуда вы сможете продолжить путь через структуру папок к каталогу классов. Если вы сохранили имя приложения в сеансе, один и тот же код можно было бы использовать практически везде.

//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/';
1 голос
/ 18 июля 2010

Я не выбрал бы автоматическое определение местоположения файла. Если хорошо не продумано, что это может быть уязвимостью безопасности, вам нужно разработать схему именования, которая поддерживает подкаталоги, и она заставляет вас использовать один файл на класс (что может быть как хорошо, так и плохо, но я считаю это негибким). Я бы использовал глобальный или статический массив, в котором вы храните отображение className => pathToClass.

0 голосов
/ 18 июля 2010

Есть два основных способа.Либо вы указываете полный абсолютный путь к каталогу вашего класса (/home/mydirectory/dev.mysite.com/my_app/php/classes/), который я бы не рекомендовал, поскольку он предполагает изменение абсолютного пути при смене хостов.Или вы можете использовать относительный путь, который является более простым и переносимым:

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

Нет необходимости получать realpath здесь, PHP подходит для относительных путей.;)

PS: realpath(dirname(__FILE__)) дубликат, я думаю.__FILE__ - это уже «реальный путь», и поэтому вам не нужно звонить realpath.

...