Функция __autoload(), как все знают, очень полезна. Её появление в PHP5 значительно упростило всем жизнь. А появление spl_autoload_register() упростило ещё больше.
Однако, не всё с ней здорово. При достаточно разветвлённой системе классов, приходится создавать весьма сложные алгоритмы поиска файлов, которые к тому же существенно замедляют работу. Однако, во многих случаях можно сделать автоподгрузку описаний классов и без __autoload().
Рассмотрим один из таких вариантов на примере. На сайте нужно реализовать возможность загрузки пользователями различных файлов (аватарок, фотографий, видеофайлов). И есть желание несколько абстрагировать механизм работы с этими файлами. То есть сегодня мы грузим всё в папочку на сайте, а завтра к нам набежал миллион посетителей и нам пришлось разнести все аватарки на двести серверов. Попробуем сделать это так:
/** * Абстрактный класс для работы с абстрактным типом файлов (fm/filesmanipulation.php) */ abstract class filesManipulation { /** * Сохранить файл * @param int $id внутренний идентификатор * @param string $tmpFile путь к временному файлу */ abstract public function save($id, $tmpFile); /** * Удалить файл * @param int $id */ abstract public function remove($id); /** * Получить адрес файла на сайте * @return string */ abstract public function getUrl($id); } /** * Реализация для хранения аватаров (fm/classes/avatars.php) */ class filesManipulationAvatars extends filesManipulation { public function save($id, $tmpFile) { $this->remove($id); $path = $_SERVER['DOCUMENT_ROOT'].'/images/avatars/'.$id.'.jpg'; return move_uploaded_file($tmpFile, $path); } public function remove($id) { return unlink($_SERVER['DOCUMENT_ROOT'].'/images/avatars/'.$id.'.jpg'); } public function getUrl($id) { return 'http://'.$_SERVER['HTTP_HOST'].'/images/avatars/'.$id.'.jpg'; } } /** * Реализация для хранения фотографий (fm/classes/photos.php) */ class fileManipulationPhotos extends filesManipulation { /* ... */ } /* ... */ |
Пример использования:
$fmAvatar = new filesManipulationAvatar(); $src = $fmAvatar->getUrl($userId); echo 'Вот ваш аватар: <img src="'.$src.'" />'; |
В этом случае нам нужно организовать автоподгрузку всех классов filesManipulation*
. Для этого нужно зарегистрировать новый автозагрузчик через spl_autoload_register
или привести структуру этой библиотеки в соответствии со структурой автолоада всей системы.
А можно несколько по иному. Добавим в абстрактный класс статический метод:
public static function getObject($name) { $classname = 'filemanipulation'.$name; if (!class_exists($classname, false)) { filename = dirname(__FILE__).'/classes/'.$name.'.php'; if (!file_exists($filename)) { throw new RuntimeException('Нет такого FM-класса: '.$name); } require($filename); } return new $classname(); } |
При этом нам нужно озаботится только тем, чтобы подгружалось описание одного абстрактного класса, а конкретные объекты можно получать через:
$fmAvatar = fileManipulation::getObject('avatar'); |
Да, это круто, уже пользовался благодаря тебе!
Только у меня вопрос, нафига называть файлы godb.php, когда класс зовется goDB?
В чем сакральный смысл этого действа? почему не называть файлы goDB.php?
adw0rd, 13.07.2009, 19:01
Сакральный смысл в том, что у меня все имена файлов всегда в нижнем регистре и я из-за этого не парюсь :)
vasa_c, 13.07.2009, 19:02
А нафига они всегда в нижнем регистре? В чем прикол то?
А минус в том, что для nix систем надо писать правило, потомучто регистро-зависимо там.
adw0rd, 13.07.2009, 19:04
Вот, а мне не надо писать правило :)
vasa_c, 13.07.2009, 19:06
Да вот как раз и надо! Я прос твой способ и говорю, что надо писать strtolower
А если не юзать разные регистры в именах классов и в названиях файла, то все проще намного! и нет strtolower всяких
adw0rd, 13.07.2009, 19:14
Просто меня лично эстетически вымораживают разные регистры в именах файлов.
+ Куча проблем с переносом туда-сюда.
Кроме того, PHP регистронезависимый. Можно к классу и как godb и gOdb обращаться.
vasa_c, 13.07.2009, 19:56
Эм… ИМХО стоит переименовать статью на «Пример использования фабричного метода в PHP». Или я таки что-то не понял и это не фабричный метод?
dallone, 14.07.2009, 10:47
Т.е. не «на», а «в»
dallone, 14.07.2009, 10:47
Не, фабричные методы всяких абстракций добавляют.
А мы здесь конкретное создание класса new classname() заменяем на точно такое же конкретное getObject(‘name’).
vasa_c, 14.07.2009, 11:45
Эээ собственно:
Параметризованные фабричные методы.
Это еще один вариант паттерна, который позволяет фабричному методу создавать разные виды продуктов.
Фабричному методу передается параметр, который идентифицирует вид создаваемого объекта(в данном случае это у нас ‘name’. Все объекты, получающиеся с помощью фабричного метода, разделяют общий интерфейс(у тебя все включают абстрактный класс filesManipulation).
Вы передаете методу лишний параметр, который и определяет, документ какого вида нужно создавать.
P.S. Я не ради того что бы показать «Я такой умный знаю паттерны», просто сейчас читаю по ним книжку вот и пытаюсь их найти везде где только можно(ну и мерещаться они мне повсюду как же без этого), что бы получше в голове уложились.
P.P.S. Черт как же неудобно копировать с книжки, давно пора реализовать нормальный CTRL+C CTRL+V.
dallone, 14.07.2009, 12:34
Возможно, с виду, это действительно оно. Но цели несколько разные.
vasa_c, 14.07.2009, 14:25
Хочу заметить, что такое можно делать только для группы классов с общим интерфейсом.
Ни в коем случае это не надо делать для всех классов.
Просто в этом случае можно прописать в phpdoc объект интерфейса и все будет вполне нормально.
Пример как совершенно неправильно это сделали — Kohana 2 — их гениальный автолоад на фабричном методе умудляется снести крышу любой IDE, в результате половина времени уходит на «посмотреть функции», потому как автодополнение прекращает работать гарантировано
Ivan1986, 12.06.2011, 1:56
Ivan1986, согласен.
vasa_c, 12.06.2011, 9:20