Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 133 · От одного поля к безопасной обработке фотографий

EXIF: метаданные фотографий, ориентация и миниатюры

Все функции EXIF, конфигурация кодировок и связанные типы: читаем обычные и вложенные поля, извлекаем превью и понимаем поворот изображения.

Фотография и её карточка: какие задачи решает EXIF

Посетитель загрузил фотографию товара. В файле могут быть не только пиксели, но и карточка съёмки: модель камеры, дата, отметка поворота, комментарий, иногда координаты. Галерее нужно правильно показать снимок, редактору — извлечь несколько полей, а публичному сервису — не раскрыть лишние сведения. Расширение EXIF помогает прочитать такую карточку в основном из JPEG/TIFF.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

От файла к одному полю
  1. lesson.jpg содержит пиксели и теги
  2. exif_read_data читает заголовки
  3. получается PHP массив
  4. Model хранит строку Model A

В примерах require "exif-lab.php" разворачивает два приложенных учебных JPEG. lesson.jpg имеет размер 32×20, Make=Learning Camera, Model=Model A, Orientation=6 и встроенную JPEG-миниатюру 4×3. base.jpg содержит те же исходные пиксели без этих EXIF-тегов. Это специально созданные файлы без личных данных; подготовка и файлы входят в скачиваемый пример.

Вывести всего одно понятное полеСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$photo = exif_read_data(__DIR__ . '/lesson.jpg');
if ($photo === false) throw new RuntimeException('Не удалось прочитать');
echo $photo['Model'] ?? 'Камера не указана', "\n";
Ожидаемый результат
Model A

В lesson.jpg заранее записана модель камеры. Функция читает значение, а echo выводит его. Никакого обхода всех тегов для этой задачи не требуется.

Сначала полезно понять массив как набор ключей и значений, затем вложенные массивы и перебор. EXIF возвращает именно такую структуру, как другие интеграции. JSON здесь не нужен для чтения файла: он появляется позже, если выбранные значения требуется передать другому сервису.

Требования и подключение расширения

Для сборки PHP поддержку включают параметром --enable-exif; отдельная внешняя библиотека для самого модуля не требуется. Для многобайтовых комментариев нужна mbstring. На Windows документация предписывает включать php_mbstring.dll перед php_exif.dll. Подбирайте DLL к своей версии PHP, архитектуре и TS/NTS; настройка CLI может отличаться от веб-сервера.

Проверить наличие расширения и поддержки mbstringСамостоятельный пример
<?php
declare(strict_types=1);

var_dump(extension_loaded('exif'));
var_dump(function_exists('exif_read_data'));
echo EXIF_USE_MBSTRING === 1 ? "С mbstring\n" : "Без mbstring\n";
Ожидаемый результат
bool(true)
bool(true)
С mbstring

Такой вывод получен в учебном PHP 8.5.11, где mbstring загружен до EXIF. Перед чтением EXIF_USE_MBSTRING на сервере сначала убедитесь, что само расширение доступно.

Если функции нет, это вопрос подключения расширения, а не свойства фотографии. Если функция есть, но возвращает false, исследуйте путь, формат, доступ и требования секций. У расширения нет собственного класса Photo и нет методов изменения EXIF. Запись/удаление метаданных выполняют отдельными средствами с проверкой сохранённого файла.

Все настройки кодировки комментариев

Некоторые UserComment состоят из маркера кодировки и байтов текста. EXIF не угадывает произвольную кодировку: для Unicode и JIS используются настройки декодирования и последующего кодирования. Суффиксы motorola/intel означают порядок байтов в TIFF-части, а не марку компьютера посетителя сайта. Преобразование работает при доступной mbstring.

Параметр и стандартное значениеНазначение
exif.encode_unicode = 'ISO-8859-15'Кодировка результата для Unicode UserComment; пустое значение использует внутреннюю mbstring.
exif.decode_unicode_motorola = 'UCS-2BE'Исходная кодировка Unicode-комментария при big-endian byte order; не пустая.
exif.decode_unicode_intel = 'UCS-2LE'Исходная кодировка Unicode-комментария при little-endian byte order; не пустая.
exif.encode_jis = ''Кодировка результата JIS-комментария; пустое значение использует внутреннюю mbstring.
exif.decode_jis_motorola = 'JIS'Исходная кодировка JIS-комментария при big-endian; не пустая.
exif.decode_jis_intel = 'JIS'Исходная кодировка JIS-комментария при little-endian; не пустая.
Временно выбрать UTF-8 для Unicode-комментариевСамостоятельный пример
<?php
declare(strict_types=1);

$previous = ini_get('exif.encode_unicode');
if (ini_set('exif.encode_unicode', 'UTF-8') === false) throw new RuntimeException('Настройка недоступна');
try {
    echo ini_get('exif.encode_unicode'), "\n";
    // Теперь читают exif_read_data($photoPath) для соответствующего комментария.
} finally {
    ini_set('exif.encode_unicode', $previous);
}
Ожидаемый результат
UTF-8

Это проверка изменения настройки, а не демонстрация распознавания любой кодировки. Для реального текста нужно знать формат входного комментария. Предыдущее значение восстанавливается после текущей операции.

Все шесть настроек имеют режим INI_ALL. Исходные decode_* должны задавать непустую поддерживаемую mbstring кодировку или допустимый список; encode_* может быть пустым и использовать внутреннюю кодировку. Назначайте подходящие значения до чтения файла. Эти настройки не превращают произвольный бинарный MakerNote в UTF-8 и не исправляют ошибочно размеченный исходник. Для выбранных полей проверяйте итоговый текст отдельно, прежде чем отправлять JSON.

Как выглядит вложенный результат и какие поля выбирать

СекцияЧто там искать
FILEИмя и размер файла, тип, время файла и обнаруженные секции. FileDateTime не равно гарантированной дате съёмки.
COMPUTEDВычисленные Width/Height/html, сведения об ориентации byte order, разобранные комментарии и Copyright, размеры миниатюры при наличии.
IFD0Общие теги, например Make, Model, Orientation. Их набор зависит от файла.
EXIFПодробности съёмки: DateTimeOriginal, экспозиция, чувствительность и другие поддерживаемые поля.
THUMBNAILПараметры встроенного превью; с read_thumbnail=true также его байты.
COMMENTКомментарии JPEG, часто несколько строк.
GPS / INTEROP / MAKERNOTEСпециальные сведения, если поддержаны и присутствуют; координаты и закрытые теги требуют осторожного обращения.
ANY_TAGУсловие наличия теговой информации при выборе required_sections.
Перебрать небольшой набор разрешённых полейСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true);
if ($metadata === false) throw new RuntimeException('Ошибка чтения');
$camera = $metadata['IFD0'] ?? [];
foreach (['Make' => 'Производитель', 'Model' => 'Модель'] as $key => $label) {
    $value = $camera[$key] ?? 'Не указано';
    echo $label, ': ', is_string($value) ? $value : 'Неожиданный тип', "\n";
}
Ожидаемый результат
Производитель: Learning Camera
Модель: Model A

foreach проходит ваш разрешённый список, а не печатает весь внешний массив. На веб-странице value нужно дополнительно экранировать. Это тот же принцип выбора нужных данных, что при обработке ответа API.

Для рекурсивного диагностического обхода сначала освойте проверку is_array и возврат к соседним элементам. У EXIF могут быть двоичные строки и личные данные: универсальное «вывести все листья» не должно становиться публичным интерфейсом галереи. Для приложения обычно лучше небольшая явная схема полей, их типов и единиц.

Почему снимок лежит на боку: метка и пиксели

Фотокамера может сохранить пиксели в одной ориентации и записать отдельную инструкцию отображения Orientation. Программа просмотра может её учитывать, а простая обработка пикселей — нет. Поэтому изображение после уменьшения иногда оказывается повёрнутым. EXIF-функция лишь читает метку; пиксели она не переставляет.

OrientationПреобразование сохранённых пикселей для нормального отображения
1Без поворота и отражения.
2Горизонтальное отражение.
3Поворот на 180°.
4Вертикальное отражение.
5Горизонтальное отражение, затем поворот на 270° по часовой стрелке.
6Поворот на 90° по часовой стрелке.
7Горизонтальное отражение, затем поворот на 90° по часовой стрелке.
8Поворот на 270° по часовой стрелке.
Определить действие для учебного снимкаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true);
$orientation = $metadata['IFD0']['Orientation'] ?? 1;
echo 'Метка: ', $orientation, "\n";
echo $orientation === 6 ? "Нужен поворот по часовой стрелке\n" : "Нужно другое правило\n";
Ожидаемый результат
Метка: 6
Нужен поворот по часовой стрелке

Значение 1 при отсутствии метки — выбранная политика приложения. Это предположение, а не доказательство, что пользователь снял фотографию ровно.

Связать прочитанную метку 6 с обработкой GDСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$path = __DIR__ . '/lesson.jpg';
$metadata = exif_read_data($path, null, true);
$orientation = $metadata['IFD0']['Orientation'] ?? 1;
if ($orientation !== 6) throw new RuntimeException('Этот пример посвящён только метке 6');
$image = imagecreatefromjpeg($path);
if ($image === false) throw new RuntimeException('Не декодирован JPEG');
$upright = imagerotate($image, -90, 0);
if ($upright === false) throw new RuntimeException('Не выполнен поворот');
echo imagesx($upright), '×', imagesy($upright), "\n";
if (!imagejpeg($upright, __DIR__ . '/upright.jpg', 90)) throw new RuntimeException('Не сохранено');
unset($upright, $image);
Ожидаемый результат
20×32

У imagerotate положительный угол направлен против часовой стрелки, поэтому для метки 6 используется -90. Это намеренно один полностью разобранный случай, не универсальная обработка восьми значений. Метки с отражением требуют imageflip в правильном порядке.

После физического преобразования надо согласовать Orientation итогового файла с нормальными пикселями, иначе просмотрщик может повернуть их повторно. GD при повторной записи JPEG обычно не переносит исходный блок EXIF; при другой библиотеке нельзя полагаться на это поведение. Перекодирование JPEG может уменьшать качество и менять метаданные/цветовой профиль. Для полного production-процесса отдельно определите сохранение профилей, обработку всех восьми значений, лимиты размеров и генерацию превью.

Все константы расширения и типы изображений

EXIF_USE_MBSTRING — int 1 при поддержке mbstring и 0 без неё. Это собственная константа EXIF. Семейство IMAGETYPE_* относится к связанному общему API изображений; значения используются для сравнения результата exif_imagetype и типа миниатюры.

КонстантаЗначение и замечание
IMAGETYPE_GIF1
IMAGETYPE_JPEG2
IMAGETYPE_PNG3
IMAGETYPE_SWF4
IMAGETYPE_PSD5
IMAGETYPE_BMP6
IMAGETYPE_TIFF_II7 — little-endian TIFF
IMAGETYPE_TIFF_MM8 — big-endian TIFF
IMAGETYPE_JPC9
IMAGETYPE_JP210
IMAGETYPE_JPX11
IMAGETYPE_JB212
IMAGETYPE_SWC13
IMAGETYPE_IFF14
IMAGETYPE_WBMP15
IMAGETYPE_XBM16
IMAGETYPE_ICO17
IMAGETYPE_WEBP18 — распознавание с PHP 7.1
IMAGETYPE_AVIF19 — с PHP 8.1
IMAGETYPE_HEIF20 — с PHP 8.5

Присутствие константы не гарантирует полноценного декодера или чтения EXIF именно этого формата. В EXIF API нет универсального преобразования HEIF в JPEG. Для MIME используйте image_type_to_mime_type после успешного определения, для размеров — getimagesize; для пикселей нужна соответствующая библиотека обработки.

Все функции EXIF: от определения формата до миграции

ЗадачаФункция
Определить формат по сигнатуреexif_imagetype
Прочитать поля и секции в массивexif_read_data
Назвать тег по числовому IDexif_tagname
Получить встроенное превьюexif_thumbnail
Разобраться со старым именемread_exif_data

У каждой функции ниже есть отдельные параметры, возвращаемые значения и несколько примеров. Старый алиас сохранён в справочнике, чтобы читатель мог перенести прежний код, но не предлагается как действующий API PHP 8.

Ошибки чтения, приватность и стоимость обработки

EXIF может отсутствовать у совершенно нормальной картинки: редактор, мессенджер или экспорт его удалил. И наоборот, наличие Make/Model/GPS не подтверждает происхождение снимка: поля редактируются. Не используйте DateTimeOriginal как достоверное время события для финансовой или юридической логики. Copyright в файле — текстовое поле, а не автоматическая проверка прав.

Ошибки пути, отсутствие доступа, неподдерживаемый формат и повреждение заголовков отличают от отсутствия одного необязательного тега. Обработчик ошибок можно временно ограничить одним чтением, записать диагностический код во внутренний журнал и показать посетителю понятное сообщение. Постоянный оператор @ или полное отключение ошибок затрудняют поиск настоящей причины.

Большая фотография, множество тегов и встроенные двоичные данные расходуют ресурсы. Сначала ограничьте размер загрузки, проверьте допустимый формат, задайте лимиты памяти/пикселей для декодера. Получение Orientation обычно дешевле полного изменения изображения; повторное декодирование JPEG на каждом просмотре страницы разумнее заменить подготовкой производных файлов при загрузке. Не называйте этот выигрыш гарантированным временем без измерения на своих файлах.

Не публикуйте полный результат exif_read_data в JSON «на всякий случай»: там могут оказаться координаты, имя владельца, серийные номера и бинарные блоки. Выберите разрешённые поля и проверяйте их типы. Для удаления приватных метаданных нужна фактическая обработка и проверка нового файла, а не unset элементов в прочитанном массиве.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

exif_imagetypeРаспознать формат изображения по первым байтам.

PHP 4.3+ · exif

exif_imagetype(string $filename): int|false

Распознать формат изображения по первым байтам.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

Часть вызоваСмысл
filenameПуть к проверяемому файлу; не готовая двоичная строка изображения.
РезультатIMAGETYPE_* либо false.
ДиагностикаСлишком мало байт: E_NOTICE и false; ошибки открытия тоже требуют обработки.

Перед чтением EXIF нужно понять, относится ли файл к поддерживаемому формату. exif_imagetype смотрит на сигнатуру в начале содержимого, а не на суффикс имени. Возврат — числовая IMAGETYPE_* константа, которую сравнивают строго. Это ещё не полная проверка декодирования всего изображения.

В примерах require "exif-lab.php" разворачивает два приложенных учебных JPEG. lesson.jpg имеет размер 32×20, Make=Learning Camera, Model=Model A, Orientation=6 и встроенную JPEG-миниатюру 4×3. base.jpg содержит те же исходные пиксели без этих EXIF-тегов. Это специально созданные файлы без личных данных; подготовка и файлы входят в скачиваемый пример.

Определить JPEG по содержимомуСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$type = exif_imagetype(__DIR__ . '/lesson.jpg');
echo $type === IMAGETYPE_JPEG ? "JPEG\n" : "Другой формат\n";
Ожидаемый результат
JPEG

Смысл числа лучше выражать именем константы: IMAGETYPE_JPEG понятнее магического 2.

Показать, что расширение файла не определяет форматСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
copy(__DIR__ . '/lesson.jpg', __DIR__ . '/renamed.dat');
$type = exif_imagetype(__DIR__ . '/renamed.dat');
echo image_type_to_mime_type($type), "\n";
Ожидаемый результат
image/jpeg

Копия называется .dat, но внутри JPEG. В прикладном коде перед image_type_to_mime_type проверяют type!==false; здесь файл — известная учебная заготовка.

Отдельно учесть слишком короткий файлСамостоятельный пример
<?php
declare(strict_types=1);

file_put_contents(__DIR__ . '/empty.bin', '');
$diagnostics = [];
set_error_handler(static function (int $severity, string $message) use (&$diagnostics): bool {
    $diagnostics[] = $message;
    return true;
}, E_WARNING | E_NOTICE);
try { $type = exif_imagetype(__DIR__ . '/empty.bin'); }
finally { restore_error_handler(); }
var_dump($type);
echo $diagnostics !== [] ? "Есть диагностическое сообщение\n" : "Без диагностики\n";
Ожидаемый результат
bool(false)
Есть диагностическое сообщение

Здесь локально перехватывается ожидаемая диагностика ровно одного вызова. error_reporting(0) на всём сайте скрыл бы посторонние проблемы. Пустой файл не становится изображением.

Функция умеет распознавать больше форматов, чем exif_read_data умеет читать как EXIF. Например, распознанный PNG не означает, что этот API извлечёт его современные eXIf-чанки. WebP добавлен в 7.1, AVIF в 8.1, HEIF в 8.5. Не выводите числовой тип сразу как MIME и не доверяйте присланному браузером Content-Type. Для приёма загрузки нужны также проверка upload error, размера, допустимых форматов и безопасное декодирование в рамках лимитов.

По сравнению с чтением метаданных или полноценным декодированием проверка сигнатуры требует меньше работы. Документация сравнивает её с getimagesize; фактическое время зависит от файловой системы и источника. Сигнатура может сохраниться и у повреждённого файла. Не передавайте URL из пользовательского ввода без политики источников: чтение по разрешённым stream wrappers может инициировать доступ к другому ресурсу.

Соседние инструменты: exif_read_data; exif_thumbnail.

exif_read_dataПрочитать секции метаданных JPEG/TIFF в массив.

PHP 4.2+ · exif

exif_read_data(resource|string $file, ?string $required_sections = null, bool $as_arrays = false, bool $read_thumbnail = false): array|false

Прочитать секции метаданных JPEG/TIFF в массив.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

Часть вызоваСмысл
fileПуть либо seekable stream resource с PHP 7.2.
required_sectionsnull или список названий: достаточно хотя бы одной найденной секции.
as_arraystrue группирует теги по секциям. Несколько специальных секций всегда массивы.
read_thumbnailtrue читает и двоичные данные встроенной миниатюры.
РезультатАссоциативный массив или false; возможна дополнительная диагностика о частичных данных.

Функция возвращает обычный ассоциативный PHP массив. Сначала прочитаем одно поле. В режиме по умолчанию многие теги расположены на верхнем уровне; включённый as_arrays=true раскладывает их по секциям. Путь к значению нужно выбирать в соответствии с этим режимом.

В примерах require "exif-lab.php" разворачивает два приложенных учебных JPEG. lesson.jpg имеет размер 32×20, Make=Learning Camera, Model=Model A, Orientation=6 и встроенную JPEG-миниатюру 4×3. base.jpg содержит те же исходные пиксели без этих EXIF-тегов. Это специально созданные файлы без личных данных; подготовка и файлы входят в скачиваемый пример.

Взять модель камеры без обхода всего массиваСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg');
if ($metadata === false) throw new RuntimeException('Не прочитаны метаданные');
echo $metadata['Model'] ?? 'Модель не указана', "\n";
Ожидаемый результат
Model A

?? даёт запасную подпись, когда ключа нет. Проверка false отделяет неудачу чтения от успешно прочитанного файла без конкретного поля.

Пойти по двум уровням массиваСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true);
if ($metadata === false) throw new RuntimeException('Ошибка чтения');
echo $metadata['IFD0']['Model'], "\n";
echo $metadata['COMPUTED']['Width'], '×', $metadata['COMPUTED']['Height'], "\n";
echo $metadata['EXIF']['DateTimeOriginal'], "\n";
Ожидаемый результат
Model A
32×20
2026:10:13 12:00:00

Третий аргумент true создаёт секции-массивы. У нашей заготовки поля известны; для фотографий посетителей каждое необязательное поле проверяют. COMPUTED.Width/Height отражают вычисленные сведения о файле.

required_sections — условие присутствия хотя бы одной запрошенной секции, а не команда вернуть только перечисленные поля. null не вводит такого требования. Названия задаются через запятую. Для обязательной бизнес-схемы затем самостоятельно проверьте каждое нужное поле: наличие секции IFD0 не гарантирует Model или Orientation.

Не спутать требование секции с фильтром результатаСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', 'IFD0,GPS', true);
var_dump($metadata !== false);
var_dump(isset($metadata['EXIF']));
var_dump(exif_read_data(__DIR__ . '/lesson.jpg', 'GPS', true));
Ожидаемый результат
bool(true)
bool(true)
bool(false)

IFD0 найдена, поэтому список IFD0,GPS принят даже без GPS. EXIF тоже вернулась: список не отфильтровал её. Отдельное требование отсутствующей GPS дало false.

Прочитать seekable-поток и сохранить его позициюСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$stream = fopen(__DIR__ . '/lesson.jpg', 'rb');
if ($stream === false) throw new RuntimeException('Не открыт файл');
try {
    fseek($stream, 10);
    $metadata = exif_read_data($stream, null, true);
    echo $metadata['IFD0']['Make'], "\n";
    echo ftell($stream), "\n";
} finally { fclose($stream); }
Ожидаемый результат
Learning Camera
10

Начиная с PHP 7.2 можно передать ресурс потока. Он должен поддерживать перемещение позиции. Чтение заголовков возвращает исходную позицию; закрывает поток его владелец.

Прочитать полученные байты через php://tempСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$bytes = file_get_contents(__DIR__ . '/lesson.jpg');
if ($bytes === false) throw new RuntimeException('Нет байтов');
$stream = fopen('php://temp', 'w+b');
if ($stream === false) throw new RuntimeException('Нет временного потока');
try {
    if (fwrite($stream, $bytes) !== strlen($bytes)) throw new RuntimeException('Неполная запись');
    rewind($stream);
    $metadata = exif_read_data($stream, null, true);
    echo $metadata['IFD0']['Orientation'], "\n";
} finally { fclose($stream); }
Ожидаемый результат
6

Функция не принимает сырые JPEG-байты вместо имени файла. Seekable php://temp связывает полученную от хранилища строку с API чтения. В production ограничивают размер загружаемых байтов; temp может перейти из памяти на диск.

Прочитать комментарий и две части CopyrightСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true);
$computed = $metadata['COMPUTED'];
echo $computed['UserComment'], "\n";
echo $computed['UserCommentEncoding'], "\n";
echo $computed['Copyright.Photographer'], ' / ', $computed['Copyright.Editor'], "\n";
Ожидаемый результат
Learning sample
ASCII
Author / Editor

Сырые теги могут содержать маркер кодировки и NUL-разделитель. COMPUTED предоставляет разобранные поля. Они не обязаны присутствовать в каждой фотографии и не доказывают авторство.

Попросить также байты встроенного превьюСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true, true);
$bytes = $metadata['THUMBNAIL']['THUMBNAIL'] ?? null;
echo is_string($bytes) && strlen($bytes) > 0 ? "Миниатюра прочитана\n" : "Нет миниатюры\n";
echo $metadata['COMPUTED']['Thumbnail.Width'], '×', $metadata['COMPUTED']['Thumbnail.Height'], "\n";
Ожидаемый результат
Миниатюра прочитана
4×3

Четвёртый true добавил двоичные данные. Их нельзя бездумно отправлять через json_encode как UTF-8 текст. Если нужна только миниатюра, exif_thumbnail выразит цель прямо.

COMPUTED, THUMBNAIL и COMMENT остаются массивами даже при as_arrays=false, чтобы одинаковые имена не смешались с остальными тегами. FILE описывает файл; IFD0 содержит общие теги; EXIF — дополнительные параметры съёмки; ANY_TAG означает наличие теговых сведений. Реализация также читает GPS, INTEROP, MAKERNOTE и другие поддерживаемые секции. У производителей бывают разные схемы и отсутствующие поля; с 7.2 расширено распознавание, в частности, Samsung, DJI, Panasonic, Sony, Pentax, Minolta, Sigma/Foveon, AGFA, Kyocera, Ricoh и Epson.

В PHP 8.0 required_sections допускает null. Чтение может вернуть часть понятных данных и одновременно сообщить E_WARNING/E_NOTICE о повреждённых или неподдержанных тегах. Приложение решает, допустим ли частичный результат; отсутствие исключения не гарантирует достоверность всего файла. Пустой путь и NUL-байты в пути в современной реализации вызывают ValueError. Пути собирайте оператором точки, а не арифметическим +. HTML выводите с экранированием; не печатайте весь массив с GPS, серийными номерами и Copyright посетителю.

Дата DateTimeOriginal может не содержать часового пояса; не назначайте ей UTC только по внешнему виду строки. Дробные теги вроде ExposureTime бывают строками вида 1/125, а не готовым float. Для численной обработки отдельно проверяют формат, знаменатель и единицы. Изменение $metadata["IFD0"]["Orientation"] в PHP не поворачивает изображение и не переписывает EXIF исходного JPEG.

Соседние инструменты: exif_imagetype; exif_thumbnail; exif_tagname; read_exif_data.

exif_tagnameНайти техническое имя EXIF-тега по числовому ID.

PHP 4.2+ · exif

exif_tagname(int $index): string|false

Найти техническое имя EXIF-тега по числовому ID.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

Часть вызоваСмысл
indexЧисловой идентификатор тега.
РезультатИмя известного тега либо false.

В формате EXIF теги имеют числовые идентификаторы. exif_tagname помогает превратить известный номер в понятное техническое имя для диагностического отчёта. Она не читает файл и не возвращает значение тега конкретной фотографии.

Получить название тега ориентацииСамостоятельный пример
<?php
declare(strict_types=1);

echo exif_tagname(0x0112), "\n";
echo exif_tagname(0x010F), "\n";
Ожидаемый результат
Orientation
Make

0x означает шестнадцатеричную запись числа. 0x0112 — тот же int, что 274. Функция знает название, но не знает ориентацию какого-либо вашего файла.

Подписать известный и неизвестный номераСамостоятельный пример
<?php
declare(strict_types=1);

foreach ([0x0112, 0xEEEE] as $tagId) {
    $name = exif_tagname($tagId);
    echo sprintf('0x%04X', $tagId), ': ', $name === false ? 'Неизвестный тег' : $name, "\n";
}
Ожидаемый результат
0x0112: Orientation
0xEEEE: Неизвестный тег

sprintf лишь форматирует номер. Проверка ===false сохраняет различие между отсутствующим именем и строковым значением. Каталог PHP не обязан знать каждый закрытый MakerNote производителя.

Нужен ID, а не строка названия и не индекс элемента в полученном массиве. exif_read_data уже возвращает многие ключи текстом, поэтому повторно «переводить» Model через exif_tagname не нужно. Сопоставление имени не проверяет значение, единицы, достоверность и контекст производителя.

Соседние инструменты: exif_read_data; exif_imagetype.

exif_thumbnailИзвлечь встроенную JPEG/TIFF-миниатюру без создания новой.

PHP 4.2+ · exif

exif_thumbnail(resource|string $file, int &$width = null, int &$height = null, int &$image_type = null): string|false

Извлечь встроенную JPEG/TIFF-миниатюру без создания новой.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

Часть вызоваСмысл
fileПуть или seekable ресурс.
width / heightВыходные переменные по ссылке, размеры миниатюры.
image_typeВыходная переменная IMAGETYPE_*, не MIME-строка.
РезультатДвоичные байты либо false. Размеры сами по себе не подтверждают успех.

Внутри фотографии иногда уже лежит небольшое превью. exif_thumbnail извлекает его байты, поэтому не требуется уменьшать весь исходник ради этой конкретной миниатюры. Функция не создаёт превью из пикселей, если его нет в файле.

В примерах require "exif-lab.php" разворачивает два приложенных учебных JPEG. lesson.jpg имеет размер 32×20, Make=Learning Camera, Model=Model A, Orientation=6 и встроенную JPEG-миниатюру 4×3. base.jpg содержит те же исходные пиксели без этих EXIF-тегов. Это специально созданные файлы без личных данных; подготовка и файлы входят в скачиваемый пример.

Прочитать готовую миниатюру и её размерыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$thumbnail = exif_thumbnail(__DIR__ . '/lesson.jpg', $width, $height, $type);
if ($thumbnail === false) throw new RuntimeException('Нет встроенного превью');
echo $width, '×', $height, "\n";
echo image_type_to_mime_type($type), "\n";
echo strlen($thumbnail) > 0 ? "Есть двоичные байты\n" : "Пусто\n";
Ожидаемый результат
4×3
image/jpeg
Есть двоичные байты

width, height и type передаются по ссылке: функция записывает результаты в ваши переменные. Отдельный return содержит байты JPEG, а не имя файла.

Сохранить извлечённое превью отдельным файломСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$bytes = exif_thumbnail(__DIR__ . '/lesson.jpg', $width, $height, $type);
if ($bytes === false || $type !== IMAGETYPE_JPEG) throw new RuntimeException('Нет ожидаемого JPEG-превью');
$saved = file_put_contents(__DIR__ . '/preview.jpg', $bytes);
if ($saved !== strlen($bytes)) throw new RuntimeException('Неполная запись');
$size = getimagesize(__DIR__ . '/preview.jpg');
echo $size[0], '×', $size[1], "\n";
Ожидаемый результат
4×3

Имя preview.jpg выбрано приложением в учебной директории. Реальный сервис сохраняет файлы в контролируемом хранилище и не перезаписывает произвольный путь посетителя.

Предусмотреть отсутствие готового превьюСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$bytes = exif_thumbnail(__DIR__ . '/base.jpg', $width, $height, $type);
echo $bytes === false ? "Нужна генерация миниатюры\n" : "Превью найдено\n";
Ожидаемый результат
Нужна генерация миниатюры

Отсутствие миниатюры нормально и не означает, что основной JPEG повреждён. Генерацию выполняет библиотека обработки изображений с отдельными лимитами.

Отдать подготовленные байты через HTTPПример для указанного окружения
<?php
declare(strict_types=1);

// Контекст: web handler, $photoPath выбран приложением после проверки доступа.
$bytes = exif_thumbnail($photoPath, $width, $height, $type);
if ($bytes === false) {
    http_response_code(404);
    exit;
}
header('Content-Type: ' . image_type_to_mime_type($type));
header('X-Content-Type-Options: nosniff');
header('Content-Length: ' . strlen($bytes));
echo $bytes;

Это контекстный HTTP-обработчик, проверенный синтаксически. До header нельзя выводить текст или BOM. Требования доступа/хранилища, лимиты и политика кеширования задаются приложением; bytes не пропускают через htmlspecialchars.

Потоковый вход с PHP 7.2 должен быть seekable; его позиция после вызова сохраняется, а закрытие остаётся обязанностью владельца. width/height могут заполниться даже при false, если размеры определены, но собрать изображение не удалось; главным признаком успеха остаётся return!==false. Тип миниатюры может отличаться от типа основного файла: документированы JPEG и TIFF. Встроенное превью может не соответствовать требуемому качеству, ориентации или более поздней редакции изображения, поэтому не обещайте ему свойства свежей генерации.

Соседние инструменты: exif_read_data; exif_imagetype.

read_exif_dataИсторический алиас: перенос вызовов на exif_read_data.

PHP 4.0.1–7.x; removed PHP 8.0 · exif

read_exif_data(...)  // deprecated 7.2; removed 8.0

Исторический алиас: перенос вызовов на exif_read_data.

EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.

Часть вызоваСмысл
СтатусУдалена в PHP 8.0. Не запускать старое имя в новом коде.
Заменаexif_read_data с её документированными аргументами и результатом.

read_exif_data — прежнее имя exif_read_data. Алиас deprecated с PHP 7.2 и удалён в PHP 8.0. Это карточка для чтения и переноса старого проекта; новый код вызывает только современное имя.

Обнаружить удалённое имя на современном PHPСамостоятельный пример
<?php
declare(strict_types=1);

var_dump(function_exists('read_exif_data'));
var_dump(function_exists('exif_read_data'));
Ожидаемый результат
bool(false)
bool(true)

Ожидаемый вывод относится к PHP 8 с включённым exif. Это не повод добавлять старое имя обратно в глобальное пространство приложения.

Перенести старый вызов и явно выбрать секции-массивыСамостоятельный пример
<?php
declare(strict_types=1);

require __DIR__ . '/exif-lab.php';
$metadata = exif_read_data(__DIR__ . '/lesson.jpg', null, true);
if ($metadata === false) throw new RuntimeException('Не прочитано');
echo $metadata['IFD0']['Model'] ?? 'Нет модели', "\n";
Ожидаемый результат
Model A

В старом проекте меняют имя функции, затем проверяют режим массива и путь к ключам. Если раньше не было as_arrays=true, Model находилась на верхнем уровне; механически менять доступ нельзя.

Старые примеры иногда передают 0 как required_sections. В новом типизированном коде для отсутствия требования используйте null. Замена имени не решает отсутствие расширения, повреждение файла или изменения диагностик новой версии; обработка false остаётся необходимой.

Соседние инструменты: exif_read_data.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.