data types and standard library
Exif Metadata
EXIF metadata is information embedded in image files, especially photos from cameras and phones. It can include orientation, camera model, capture date, GPS coordinates, and other device details.
Treat EXIF as optional and untrusted. Some images have no metadata, some metadata is wrong, and some metadata is sensitive enough that it should be removed before public display.
Check EXIF support
The EXIF extension may not be available in every PHP environment.
<?php
declare(strict_types=1);
$available = function_exists('exif_read_data');
echo 'EXIF support: ' . ($available ? 'available' : 'missing') . PHP_EOL;
Code that depends on EXIF should either require the extension during deployment or handle the missing capability clearly.
Read metadata defensively
exif_read_data() can return false, and individual keys may be missing.
<?php
declare(strict_types=1);
function orientationFromExif(array $exif): int
{
$orientation = $exif['Orientation'] ?? 1;
if (!is_int($orientation) || $orientation < 1 || $orientation > 8) {
return 1;
}
return $orientation;
}
echo orientationFromExif(['Orientation' => 6]) . PHP_EOL;
echo orientationFromExif([]) . PHP_EOL;
// Prints:
// 6
// 1
Orientation is commonly used to rotate uploaded photos so they display correctly.
Parse capture dates carefully
EXIF dates often use a format like YYYY:MM:DD HH:MM:SS.
<?php
declare(strict_types=1);
function dateTakenFromExif(array $exif): ?DateTimeImmutable
{
if (!isset($exif['DateTimeOriginal']) || !is_string($exif['DateTimeOriginal'])) {
return null;
}
$date = DateTimeImmutable::createFromFormat('Y:m:d H:i:s', $exif['DateTimeOriginal']);
return $date ?: null;
}
$date = dateTakenFromExif(['DateTimeOriginal' => '2026:05:20 09:30:00']);
echo $date?->format('Y-m-d H:i') . PHP_EOL;
// Prints:
// 2026-05-20 09:30
If the timezone is not present, decide whether the value is only for display or whether it can safely drive business logic.
Think about GPS privacy
EXIF GPS fields can reveal where a photo was taken. Public applications often strip metadata from images before serving them.
<?php
declare(strict_types=1);
function containsGpsMetadata(array $exif): bool
{
foreach (array_keys($exif) as $key) {
if (str_starts_with((string) $key, 'GPS')) {
return true;
}
}
return false;
}
echo containsGpsMetadata(['GPSLatitude' => ['51/1', '30/1']]) ? 'gps found' : 'no gps';
echo PHP_EOL;
// Prints:
// gps found
Whether to keep, strip, or display this metadata is a product and privacy decision, not just a technical detail.
Do not validate images using EXIF alone
EXIF is metadata, not proof that an upload is safe. Validate MIME type, extension policy, file size, and dimensions before using image metadata.
<?php
declare(strict_types=1);
function canReadExifForMime(string $mimeType): bool
{
return in_array($mimeType, ['image/jpeg', 'image/tiff'], true);
}
echo canReadExifForMime('image/jpeg') ? 'maybe' : 'no';
echo PHP_EOL;
// Prints:
// maybe
JPEG photos are the common case. PNG and WebP workflows usually do not depend on EXIF in the same way.
What to remember
EXIF can improve photo handling, but it is optional metadata. Check extension support, validate uploads separately, default missing orientation sensibly, handle dates carefully, and make a deliberate privacy decision about GPS and device metadata.
Practice
Task: Summarise photo metadata
Write a small EXIF metadata summariser that works from an array.
Requirements
- Use
declare(strict_types=1);. - Accept an EXIF-like array.
- Return orientation, defaulting to
1when missing or invalid. - Return capture date as
Y-m-d H:iwhenDateTimeOriginalis valid. - Report whether GPS metadata is present.
- Print one normal metadata summary.
- Print one missing metadata summary.
- Include the expected output as comments in the same PHP code block.
The task should treat metadata as optional, not guaranteed.
Show solution
<?php
declare(strict_types=1);
function photoMetadataSummary(array $exif): array
{
$orientation = $exif['Orientation'] ?? 1;
if (!is_int($orientation) || $orientation < 1 || $orientation > 8) {
$orientation = 1;
}
$dateTaken = null;
if (isset($exif['DateTimeOriginal']) && is_string($exif['DateTimeOriginal'])) {
$date = DateTimeImmutable::createFromFormat('Y:m:d H:i:s', $exif['DateTimeOriginal']);
$dateTaken = $date ? $date->format('Y-m-d H:i') : null;
}
$hasGps = false;
foreach (array_keys($exif) as $key) {
if (str_starts_with((string) $key, 'GPS')) {
$hasGps = true;
break;
}
}
return [
'orientation' => $orientation,
'dateTaken' => $dateTaken,
'hasGps' => $hasGps,
];
}
$summary = photoMetadataSummary([
'Orientation' => 6,
'DateTimeOriginal' => '2026:05:20 09:30:00',
'GPSLatitude' => ['51/1', '30/1'],
]);
echo $summary['orientation'] . ' / ' . $summary['dateTaken'] . ' / ' . ($summary['hasGps'] ? 'gps' : 'no gps') . PHP_EOL;
$missing = photoMetadataSummary([]);
echo $missing['orientation'] . ' / ' . ($missing['dateTaken'] ?? 'no date') . ' / ' . ($missing['hasGps'] ? 'gps' : 'no gps') . PHP_EOL;
// Prints:
// 6 / 2026-05-20 09:30 / gps
// 1 / no date / no gps
The solution defaults safely when metadata is missing, parses the camera date format explicitly, and treats GPS metadata as a privacy signal rather than ordinary display text.