Автолоад без __autoload

Функция __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');

13 комментариев »

  • Да, это круто, уже пользовался благодаря тебе!

    Только у меня вопрос, нафига называть файлы 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

  • new classname() заменяем на точно такое же конкретное getObject(’name’).

    Эээ собственно:

    Параметризованные фабричные методы.
    Это еще один вариант паттерна, который позволяет фабричному методу создавать разные виды продуктов.
    Фабричному методу передается параметр, который идентифицирует вид создаваемого объекта(в данном случае это у нас ‘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

Leave a comment