Заметки об «индийском» коде
- Автор(ы): Чобиток Василий
Пример № 1 (PHP 5)
- 14.08.2010
Задача: известно, что путь к картинке хранится в переменной $image_path. Картинка может быть в одном из форматов, который поддерживается браузерами (JPEG, GIF и т. п.). Необходимо картинку передавать в браузер не прямой ссылкой, а при обращении к скрипту.
Естественно, что при получении картинки скриптом в начале PHP-скрипт должен сообщить в заголовке тип передаваемых данных, например: «Content-type: image/jpeg
».
Программист решает эту задачу следующим образом:
$extension = strtolower(strrchr(basename($image_path), '.'));
switch($extension) {
case '.jpg': header('Content-type: image/jpeg'); break;
case '.jpeg': header('Content-type: image/jpeg'); break;
case '.png': header('Content-type: image/png'); break;
case '.gif': header('Content-type: image/gif'); break;
}
В этом коде проблемы следующие:
- Привязка к расширению. Не всегда расширение соответствует реальному формату данных. Дай файлу в формате JPEG расширение «.gif» и этот код перестанет работать. Впрочем, это некритичная проблема, т.к. любой сайтовладелец, должен следить за адекватностью контента и данная проблема может никогда и не проявится.
- При появлении новых форматов изображений (SVG набирает силу) придётся менять код внутри структурного оператора switch и добавлять новые операторы.
Решение проблемы: в PHP есть функции, которые возвращают тип изображения по сигнатуре первых байтов его файла (exif_imagetype), а также Mime-тип изображения по его типу. Рефакторинг предыдущего решения даёт следующий результат:
$imgType = exif_imagetype($image_path);
header('Content-type: ' . image_type_to_mime_type($imgType));
Здесь картинки работают независимо от расширения, нет привязки к ограниченному списку форматов, размер кода сократился в три раза.
Пример № 2 (PHP 5)
Этот пример идет в продолжение предыдущего.
Задача: известен тип ($imgType) изображения, которое хранится по известному пути ($image_path). Необходимо загрузить изображение в память (переменная $img) для последующей программной обработки.
Самый простой способ сделать это следующим образом:
switch ($imgType) {
case IMAGETYPE_GIF:
$img = imagecreatefromgif($image_path); break;
case IMAGETYPE_JPEG:
$img = imagecreatefromjpeg($image_path); break;
case IMAGETYPE_PNG:
$img = imagecreatefrompng($image_path); break;
}
Мы простых путей не ищем и делаем то же самое так:
$imgCreateMethods = array (
IMAGETYPE_GIF => 'imagecreatefromgif',
IMAGETYPE_JPEG => 'imagecreatefromjpeg',
IMAGETYPE_PNG => 'imagecreatefrompng',
);
$img = $imgCreateMethods[$imgType]($image_path);
Хоть кода и меньше, но первый вариант на мой взгляд читается проще. Так зачем в этом случае городить огород?
Суть решения в том, что, как говорилось в предыдущем примере, заранее может быть неизвестен полный перечень форматов, что является изменчивой частью системы. Как говорят классики, изменчивая часть должна быть инкапсулирована (сокрыта) от основной системы. Поэтому перечень имён функций для создания изображения в памяти вынесен в отдельный массив $imgCreateMethods. Этот массив может быть объявлен во включаемом остальными файлами файле настройки (например, config.php).
Теперь добавление новых форматов или, наоборот, ограничение числа поддерживаемых (если это необходимо) выполняется путем редактирования файла конфигурации, а не файлов с основным функционалом. Файл конфигурации доступен, как правило, не только программисту, но и владельцам сайтов, которые используют этот функционал.