Подтвердить что ты не робот

Автозагрузка и несколько каталогов

Я только что посмотрел на функцию php autoload(). Кажется хорошей идеей, но я не уверен, как она обрабатывает несколько каталогов. Моя текущая разработка в основном имеет структуру каталогов библиотеки, которая группирует классы в подкаталоги по операции. Мне интересно, я должен объявить include() для каждого каталога..., который я действительно надеюсь, что мне не нужно делать.

Можете ли вы посоветовать - спасибо

4b9b3361

Ответ 1

Вы можете взглянуть на PEAR Convention для имен классов, что действительно отлично подходит для автозагрузки.

В основном, он утверждает, что:

Иерархия классов PEAR также отражаются в имени класса, каждый уровень иерархии, разделенный одно подчеркивание.

Это означает, что поиск файла для включения для имени кластера HTML_Upload_Error - это просто замена '_' на '/'; давая вам HTML/Upload/Error.php

Для получения дополнительных пояснений и нескольких примеров вы можете взглянуть на статьи:

BTW: это соглашение используется многими большими Frameworks/libraries;-)
Например, Zend Framework использует это соглашение - и это действительно полезно!

Ответ 2

Вот класс, который я написал некоторое время назад для аналогичной цели. В то время я все еще был на этапе обучения, поэтому могут быть глупые идеи; тем не менее он работал.

Основная идея заключается в том, что он сканирует исходный каталог один раз и создает классы сопоставления массива в свои исходные файлы. Класс зарегистрирован как автозагрузчик, а при вызове он включает требуемый файл. Если он не найден, он пытается перестроить массив на лету.

/* register ClassLoader as class loader */
spl_autoload_register(array(ClassLoader::getInstance(), 'loadClass'));


class ClassLoader {

    private static $SAVE_FILE = 'ClassLoader.save.php';

    /* singleton */
    private static $instance;

    /* stores a className -> filePath map */
    private $classList;
    /* tells whether working from saved file */
    private $refreshed;


    public static function getInstance() {
        if (!isset(self::$instance)) {
            self::$instance = new ClassLoader();
        }
        return self::$instance;
    }

    private function __construct() {
        $this->initClassList();
    }

    public function loadClass($className) {
        if ( !array_key_exists($className, $this->classList) && !$this->refreshed ) {
            $this->refreshClassList();
        }
        require_once($this->classList[$className]);
    }

    private function initClassList() {
        if (file_exists(INCLUDES_DIR . self::$SAVE_FILE)) {
            require_once(INCLUDES_DIR . self::$SAVE_FILE);
            $this->refreshed = FALSE;
        } else {
            $this->refreshClassList();
        } 
    }

    private function refreshClassList() {
        $this->classList = $this->scanDirectory(INCLUDES_DIR);
        $this->refreshed = TRUE;

        $this->saveClassList();
    }

    private function saveClassList() {
        $handle = fopen(INCLUDES_DIR . self::$SAVE_FILE, 'w');
        fwrite($handle, "<?php\r\n");

        foreach($this->classList as $class => $path) {
            $line = '$this->classList' . "['" . $class . "'] = '" . $path . "';\r\n";
            fwrite($handle, $line);
        }

        fwrite($handle, '?>');
        fclose($handle);
    }

    private function scanDirectory ($directory) {
        // strip closing '/'
        if (substr($directory, -1) == '/') {
            $directory = substr($directory, 0, -1);
        }

        if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) {
            return array();
        }

        $dirH = opendir($directory);
        $scanRes = array();

        while(($file = readdir($dirH)) !== FALSE) {

            // skip pointers
            if ( strcmp($file , '.') == 0 || strcmp($file , '..') == 0) {
                continue;
            }

            $path = $directory . '/' . $file;

            if (!is_readable($path)) {
                continue;
            }

            // recursion
            if (is_dir($path)) {
                $scanRes = array_merge($scanRes, $this->scanDirectory($path));

            } elseif (is_file($path)) {
                $className = explode('.', $file);
                if ( strcmp($className[1], 'class') == 0 && strcmp($className[2], 'php') == 0 ) {
                    $scanRes[$className[0]] = $path; 
                }
            }
        }

        return $scanRes;
    }

}

Ответ 3

К сожалению, вам нужно явно добавить каждый каталог. Это можно сделать программно в script, который рекурсивно перемещает ваши каталоги или вы можете указать список.

Вероятно, наиболее эффективным способом является указать список каталогов и подкаталогов для поиска и добавить их в ваш 'include_path', используя ini_set().

Ответ 4

Вы, кажется, смущены:) Или, может быть, я смущен вашим вопросом.

Вы можете написать функцию, которая находит и загружает класс, PHP не заботится о том, где/сколько уровней в глубине.

И загляните в автозагрузку SPL тоже, он имеет ту же базовую функциональность, но вы можете написать несколько функций автозагрузки, а затем связать их. Может быть полезно, если вы хотите использовать некоторые внешние библиотеки, которые определяют свои собственные автозагрузчики, которые могут конфликтовать с вашими.

Ответ 5

Я предполагаю, что вы говорите о способности автозагрузки PHP SPL - где вы пишете свою собственную функцию, а затем регистрируете ее с помощью SPL.

Как вы это делаете, зависит от того, как вы создаете свою функцию включения. Можно объявить несколько включенных функций, а затем зарегистрировать их с помощью PHP: сколько из вас зависит. Возможность автозагрузки SPL просто позволяет вам создать свою собственную функцию, а затем сообщите PHP, чтобы запускать эту функцию каждый раз, когда требуется класс.

Одним из преимуществ создания нескольких является возможность зарегистрировать их в порядке их использования, наиболее используемый каталог сначала наименее используемый последним. Кроме того, если каталог изменен или удален, вы просто измените и/или удалите ответственную функцию.

Вы можете написать одну функцию, которая будет проходить через всю структуру папок (хотя я бы не рекомендовал ее для упрощения администрирования и развязки кода). Для этого нет "технически правильного" способа:)

Ответ 6

Как уже упоминалось, автозагрузка SPL является функциональной структурой, на которую вы должны внедрить практическую реализацию - соглашения об обходах каталогов и именования являются частью этих соображений.

Возьмем практический пример в виде Zend Loader: на его основе это одноэлемент, который использует соглашение о соотношении пространств имен с каталогами, которые зарегистрированы в PHP, включая путь. Практический пример:

set_include_path(get_include_path(). PATH_SEPARATOR. 'App/'); //Concat the "App" directory onto the existing include paths
$loader = Zend_Loader::getInstance(); //because the autoloader is a singleton, we get a reference to it without assuming we need to first create it
$loader->registerNamespace('App_'); //Tell autoloader it can look in the app directory to find classes if it can't find them in the default Zend directory.

Очевидно, что конкретные проблемы реализации будут варьироваться от проекта к проекту, но может быть лучше, как упражнение в понимании, так и повторное использование кода, попробовать свои силы при программировании автозагрузчика, который может анализировать определенный формат класса (например, 'directory_classname ') в каталог, затем загрузите и подтвердите класс.