автозагрузка и несколько каталогов - PullRequest
12 голосов
/ 31 августа 2009

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

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

Ответы [ 6 ]

17 голосов
/ 31 августа 2009

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

В основном, это гласит, что:

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

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

Дополнительные пояснения и несколько примеров вы можете посмотреть в статьях:

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

10 голосов
/ 01 сентября 2009

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

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

/* 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;
    }

}
1 голос
/ 01 сентября 2009

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

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

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»). ') в карту каталогов, затем загрузите и подтвердите класс.

1 голос
/ 31 августа 2009

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

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

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

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

1 голос
/ 31 августа 2009

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

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

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

1 голос
/ 31 августа 2009

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

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

...