Я просто смотрел на функцию autoload () php. Кажется хорошей идеей, но я не уверен, как она обрабатывает несколько каталогов. Моя текущая разработка в основном имеет структуру каталогов библиотеки, которая группирует классы в подкаталоги по операции. Мне интересно, я должен объявить include () для каждой директории …, которую я действительно надеюсь, что мне не нужно это делать.
Можете ли вы посоветовать – спасибо
Возможно, вы захотите взглянуть на Конвенцию PEAR для имен классов, что действительно отлично подходит для автозагрузки.
В основном, он утверждает, что:
Иерархия классов PEAR также отражается в имени класса, каждый уровень иерархии разделяется одним подчеркиванием.
Это означает, что поиск файла для включения для имени HTML_Upload_Error
– это просто замена '_' на '/'; давая вам HTML/Upload/Error.php
Для получения дополнительных пояснений и нескольких примеров вы можете взглянуть на статьи:
BTW: эта конвенция используется многими большими Frameworks / libraries 😉
Например, Zend Framework использует это соглашение – и это действительно полезно!
Вот класс, который я написал некоторое время назад для аналогичной цели. В то время я все еще был на этапе обучения, поэтому могут быть глупые идеи; тем не менее он работал.
Основная идея заключается в том, что он сканирует исходный каталог один раз и создает классы сопоставления массива в свои исходные файлы. Класс зарегистрирован как автозагрузчик, а при вызове он включает требуемый файл. Если не найден, он пытается перестроить массив на лету.
/* 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; } }
К сожалению, вам нужно явно добавить каждый каталог. Это может быть сделано программно в скрипте, который рекурсивно проходит ваши каталоги, или вы можете указать список.
Вероятно, наиболее эффективным способом является указать список каталогов и подкаталогов для поиска и добавить их в ваш 'include_path', используя ini_set ().
Вы, кажется, смущены 🙂 Или, может быть, я смущен вашим вопросом.
Вам полностью нужно написать функцию, которая находит и загружает класс, PHP не заботится о том, где / сколько уровней в глубине.
И загляните в автозагрузку SPL тоже , он имеет ту же базовую функциональность, но вы можете написать несколько функций автозагрузки, а затем связать их. Может быть полезно, если вы хотите использовать некоторые внешние библиотеки, которые определяют свои собственные автозагрузчики, которые могут конфликтовать с вашими.
Я предполагаю, что вы говорите о возможности автозагрузки PHP SPL – где вы пишете свою собственную функцию, а затем регистрируете ее с помощью SPL.
Как вы это делаете, зависит от того, как вы создаете свои функции включения. Можно объявить несколько включенных функций, а затем зарегистрировать их с помощью PHP: сколько из вас зависит. Способность автозагрузки SPL просто позволяет вам создать свою собственную функцию, а затем сказать PHP, чтобы запускать эту функцию каждый раз, когда требуется класс.
Одним из преимуществ создания нескольких является возможность зарегистрировать их в порядке их использования, наиболее часто используемый каталог сначала наименее используемый последним. Кроме того, если каталог изменяется или удаляется, то вы просто изменяете и / или удаляете ответственную функцию.
Вы можете написать одну функцию, которая будет проходить через всю структуру папок (хотя я бы не рекомендовал ее для упрощения администрирования и развязки кода). Нет никакого «технически правильного» способа сделать это 🙂
Как уже упоминалось, автозагрузка SPL функционально представляет собой структуру, на которую вы должны внедрить практическую реализацию – соглашения об обходах каталогов и именования являются частью этих соображений.
Возьмем практический пример в виде Zend Loader: на его основе это singleton, который использует соглашение о соотношении пространств имен с каталогами, которые зарегистрированы в пути включения 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») в карту каталога, затем загрузить и проверить класс.