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

Дерево руководства
Глава 133 · От одного поля к безопасной обработке фотографий
EXIF: метаданные фотографий, ориентация и миниатюры
Все функции EXIF, конфигурация кодировок и связанные типы: читаем обычные и вложенные поля, извлекаем превью и понимаем поворот изображения.
Фотография и её карточка: какие задачи решает EXIF
Посетитель загрузил фотографию товара. В файле могут быть не только пиксели, но и карточка съёмки: модель камеры, дата, отметка поворота, комментарий, иногда координаты. Галерее нужно правильно показать снимок, редактору — извлечь несколько полей, а публичному сервису — не раскрыть лишние сведения. Расширение EXIF помогает прочитать такую карточку в основном из JPEG/TIFF.
EXIF — метаданные внутри файла: например, модель камеры, ориентация или дата съёмки. Они не являются самими пикселями. Функции расширения читают сведения и миниатюры; изменение массива результата не переписывает файл. Поля могут отсутствовать, быть некорректными или намеренно подделанными.
- lesson.jpg содержит пиксели и теги
- exif_read_data читает заголовки
- получается PHP массив
- 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 может отличаться от веб-сервера.
<?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; не пустая. |
<?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 при отсутствии метки — выбранная политика приложения. Это предположение, а не доказательство, что пользователь снял фотографию ровно.
<?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_GIF | 1 |
| IMAGETYPE_JPEG | 2 |
| IMAGETYPE_PNG | 3 |
| IMAGETYPE_SWF | 4 |
| IMAGETYPE_PSD | 5 |
| IMAGETYPE_BMP | 6 |
| IMAGETYPE_TIFF_II | 7 — little-endian TIFF |
| IMAGETYPE_TIFF_MM | 8 — big-endian TIFF |
| IMAGETYPE_JPC | 9 |
| IMAGETYPE_JP2 | 10 |
| IMAGETYPE_JPX | 11 |
| IMAGETYPE_JB2 | 12 |
| IMAGETYPE_SWC | 13 |
| IMAGETYPE_IFF | 14 |
| IMAGETYPE_WBMP | 15 |
| IMAGETYPE_XBM | 16 |
| IMAGETYPE_ICO | 17 |
| IMAGETYPE_WEBP | 18 — распознавание с PHP 7.1 |
| IMAGETYPE_AVIF | 19 — с PHP 8.1 |
| IMAGETYPE_HEIF | 20 — с PHP 8.5 |
Присутствие константы не гарантирует полноценного декодера или чтения EXIF именно этого формата. В EXIF API нет универсального преобразования HEIF в JPEG. Для MIME используйте image_type_to_mime_type после успешного определения, для размеров — getimagesize; для пикселей нужна соответствующая библиотека обработки.
Все функции EXIF: от определения формата до миграции
| Задача | Функция |
|---|---|
| Определить формат по сигнатуре | exif_imagetype |
| Прочитать поля и секции в массив | exif_read_data |
| Назвать тег по числовому ID | exif_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-тегов. Это специально созданные файлы без личных данных; подготовка и файлы входят в скачиваемый пример.
<?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_sections | null или список названий: достаточно хотя бы одной найденной секции. |
| as_arrays | true группирует теги по секциям. Несколько специальных секций всегда массивы. |
| read_thumbnail | true читает и двоичные данные встроенной миниатюры. |
| Результат | Ассоциативный массив или 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.
<?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
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 может перейти из памяти на диск.
<?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 повреждён. Генерацию выполняет библиотека обработки изображений с отдельными лимитами.
<?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
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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


