Сен 29 2008
Несколько функций __autoload в одном коде на PHP
С полгода назад я писал про нюансы автоматической загрузки классов с использованием __autoload(), и там указывал на проблему использования данного способа, заключающуюся в том, что функция с магическим именем “__autoload” может быть объявлена только одна. Поэтому, если Вы пишете свой фреймворк и использовали в нем функцию с этим именем, то другой фреймворк, использующий аналогичную функцию уже не сможет работать в паре с Вашим.
Я писал про то, что единственным решением проблемы автозагрузки классов из двух несвязанных фреймворков может быть лишь создание гибрида из двух __autoload-функций. При этом я сам, выносил функцию автозагрузки за пределы фреймворка, отдавая загрузку классов на откуп программиста, непосредственно использующего мой фреймворк.
Так было полгода назад. В последствии я ближе познакомился со Standard PHP Library (SPL) и осознал, что эта проблема давно решена при помощи функции spl_autoload_register, которая активизирует стэк из функций автозагрузки классов.
Разумеется, Вы должны использовать уникальное имя для своей функции автозагрузки, поэтому от имени “__autoload” лучше отказаться сразу. Можете использовать уникальный префикс типа “myframework_autoload” или завернуть эту функцию в отдельный класс автозагрузки. А еще лучше (в рассчете на будущее) использовать нэймспэйсы в PHP, хотя это сразу ограничит минимальную версию PHP до 5.3.0.
Когда Вы первый раз вызываете функцию spl_autoload_register(), она активизирует стэк функций автозагрузки классов и с этого момента функция с именем “__autoload” уже не вызывается автоматически, если только Вы не зарегистрируете и ее. Т.е. с момента вызова spl_autoload_register функция с именем “__autoload” перестает быть магической. И даже разрегистрирование всех функций автозагрузки при помощи spl_autoload_unregister не возвращает ее магическое предназначение.
Зарегистрированные функции вызываются в том же порядке, в каком они были зарегистрированы. Цепочка вызовов прекращается сразу как только очередная функция автозагрузки нашла и загрузила нужный класс. Исходя из этого, логичнее регистрировать функции автозагрузки в порядке частоты их предполагаемого использования (частые в начало).
Вот такой дизайн может иметь класс автозагрузки в Вашем собственном фреймворке (для примера используется нэймспэйс “MyFramework”):
<?php
namespace MyFramework;
class Autoload {
public static function register() {
if (!spl_autoload_register(__NAMESPACE__.'::Autoload::load')) {
throw new Exception('Could not register '.__NAMESPACE__.''s class autoload function');
}
}
public static function unregister() {
if (!spl_autoload_unregister(__NAMESPACE__.'::Autoload::load')) {
throw new Exception('Could not unregister '.__NAMESPACE__.''s class autoload function');
}
}
public static function load($fullClassName) {
$namespaces = explode('::', $fullClassName);
$className = array_pop($namespaces);
if (count($namespaces) != 1 || $namespaces[0] != __NAMESPACE__)
return;
$basePath = dirname(__FILE__).'/';
@include_once($basePath . self::getFileByClass($className));
}
private static function getFileByClass($class) {
return str_replace('_', '/', $class).'.php';
}
}
?>
Теперь все, что нужно для того, чтобы использовать классы из этого фреймворка, не заботясь о их подключении через (require_once или include_once) — это вызвать статический метод register() класса Autoload в нэймспэйсе MyFramework:
require_once 'path/to/myframework/Autoload.php'; MyFramework::Autoload::register();
Можете еще больше упростить предложенный пример, создав в каталоге с Вашим фреймворком файл init.php вида:
<?php require_once dirname(__FILE__).'/classes/Autoload.php'; MyFramework::Autoload::register(); ?>
И тогда для использования фреймворка достаточно заинклудить этот init.php и использовать его классы напрямую.
В приведенном примере в функции загрузки проверяется, что нэймспэйс одноуровневый и совпадает с нэймспэйсом фреймворка (т.е. текущим). Вы можете усложнить эту функцию, добавив обработку многоуровневых нэймспэйсов, разнеся их по разным каталогам.
Напомню, что функция автозагрузки должна отрабатывать максимально “тихо”, не вызывая в случае неуспеха каких-либо исключений, приводящих к завершению скрипта, т.к. в стэке SPL может находиться несколько функций автозагрузки.
Применяя предложенный паттерн Вы сможете избежать конфликта с другим фреймворками, так же использующими автозагрузку классов через SPL.

Июль 29th, 2009 at 07:19
Скажу что на данный момент у хостеров нет php 5.3
Только на отдельных серверах.
Я до знакомства с вашим постом использовал (фреймворк один) сначала __autoload в нем, а в случае неуспеха - вызывал следующую функцию _autoload_stapp - для попытки подгрузки класса из пользовательского приложения.
У меня такой вопрос есть.
Если файлы зазендованы, то какая настройка отвечает за то, чтобы можно было пользоваться обычными именами функций в некоторых файлах - чтобы php их переименовывал сам? Например из шаблонов смарти. (у меня периодически возникает такая проблема на некоторых хостерах)