Мне интересно, как лучше всего решить проблему с необходимостью «включать» так много файлов в мои сценарии PHP, чтобы гарантировать, что все классы, которые мне нужно использовать, доступны для моего сценария.
В настоящее время я использую include_once только для включения классов, к которым я обращаюсь напрямую. Каждый из них будет include_once классов, к которым они имеют доступ.
Я изучал возможность использования функции __autoload, но, похоже, она не работает, если вы планируете организовать файлы классов в виде дерева каталогов. Если бы вы это сделали, похоже, что вы в конечном итоге пройдете по дереву каталогов, пока не найдете класс, который искали. Кроме того, я не уверен, как это влияет на классы с одинаковыми именами в разных пространствах имен.
Есть ли более простой способ справиться с этим?
Или PHP просто не подходит для приложений типа «предприимчивость» с множеством различных объектов, расположенных в отдельных файлах, которые могут находиться во многих разных каталогах.






__autoload хорошо работает, если у вас есть согласованное соглашение об именах для ваших классов, которое сообщает функции, где они находятся внутри дерева каталогов. MVC особенно хорошо подходит для такого рода вещей, потому что вы можете легко разделить классы на модели, представления и контроллеры.
В качестве альтернативы можно сохранить ассоциативный массив имен для расположения файлов для вашего класса и позволить __autoload запрашивать этот массив.
__autoload будет работать, но только в PHP 5.
Вы можете определить несколько функций автозагрузки с spl_autoload_register:
spl_autoload_register('load_controllers');
spl_autoload_register('load_models');
function load_models($class){
if ( !file_exists("models/$class.php") )
return false;
include "models/$class.php";
return true;
}
function load_controllers($class){
if ( !file_exists("controllers/$class.php") )
return false;
include "controllers/$class.php";
return true;
}
Что касается предложений, то я неравнодушен к предложениям Кевина, но они не должны быть абсолютными. Я вижу несколько разных вариантов использования с __autoload.
classes/User.php или classes/User.class.php.classes/Model/User.php. Ваша функция __autoload должна знать, что нужно перевести подчеркивание в разделитель каталогов, чтобы найти файл.classes и кешируйте, какие файлы где находятся. Итак, если вы попытаетесь загрузить класс User, не имеет значения, в котором он находится: classes/User.php, classes/Models/User.php или classes/Utility/User.php. Как только он найдет User.php где-нибудь в каталоге classes, он будет знать, какой файл включить, когда класс User должен быть загружен автоматически.@ Кевин:
I was just trying to point out that spl_autoload_register is a better alternative to __autoload since you can define multiple loaders, and they won't conflict with each other. Handy if you have to include libraries that define an __autoload function as well.
Уверены ли вы? документация говорит иначе:
If your code has an existing __autoload function then this function must be explicitly registered on the __autoload stack. This is because spl_autoload_register() will effectively replace the engine cache for the __autoload function by either spl_autoload() or spl_autoload_call().
=> вы также должны явно зарегистрировать __autoload любой библиотеки. Но в остальном вы, конечно, правы, эта функция - лучшая альтернатива.
В моих приложениях у меня обычно есть файл setup.php, который включает все основные классы (т.е. фреймворк и сопутствующие библиотеки). Мои пользовательские классы загружаются с помощью автозагрузчика, которому помогает карта компоновки каталогов.
Каждый раз, когда добавляется новый класс, я запускаю сценарий построения командной строки, который сканирует все дерево каталогов в поисках классов моделей, а затем создает ассоциативный массив с именами классов в качестве ключей и путями в качестве значений. Затем функция __autoload ищет имя класса в этом массиве и получает путь включения. Вот код:
autobuild.php
define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");
function buildAutoloaderMap() {
$dirs = array('lib', 'view', 'model');
$cache = array();
$n = 0;
foreach ($dirs as $dir) {
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
$fn = $entry->getFilename();
if (!preg_match('/\.class\.php$/', $fn))
continue;
$c = str_replace('.class.php', '', $fn);
if (!class_exists($c)) {
$cache[$c] = ($pn = $entry->getPathname());
++$n;
}
}
}
ksort($cache);
file_put_contents(MAP, serialize($cache));
return $n;
}
autoload.php
define('MAP', 'var/cache/autoload.map');
function __autoload($className) {
static $map;
$map or ($map = unserialize(file_get_contents(MAP)));
$fn = array_key_exists($className, $map) ? $map[$className] : null;
if ($fn and file_exists($fn)) {
include $fn;
unset($map[$className]);
}
}
Обратите внимание, что соглашение об именах файлов должно быть [имя_класса] .class.php. После изменения каталогов классы будут просматриваться в autobuild.php. Вы также можете запустить autobuilder из функции автозагрузки, когда класс не найден, но это может привести вашу программу в бесконечный цикл.
Сериализованные массивы чертовски быстры.
@JasonMichael: PHP 4 мертв. Преодолей это.
Вы также можете программно определить расположение файла класса, используя соглашения о структурированном именовании, которые сопоставляются с физическими каталогами. Вот как это делает Zend в Zend Framework. Поэтому, когда вы вызываете Zend_Loader::loadClass("Zend_Db_Table");, он разбивает имя класса на массив каталогов, разделяя символы подчеркивания, а затем класс Zend_Loader загружает требуемый файл.
Как и все модули Zend, я ожидал, что вы можете использовать только загрузчик сам по себе со своими собственными классами, но я использовал его только как часть сайта, использующего Zend MVC.
Но были проблемы с производительностью под нагрузкой при использовании любого вида динамической загрузки классов, например, см. это сообщение в блоге, сравнивающий Zend_Loader с жесткой загрузкой файлов классов.
Помимо снижения производительности из-за необходимости поиска пути включения PHP, это предотвращает кеширование кода операции. Из комментария к этому сообщению:
When using ANY Dynamic class loader APC can’t cache those files fully as its not sure which files will load on any single request. By hard loading the files APC can cache them in full.