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 example
<?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 example
<?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 example
<?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 example
<?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 example
<?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 1 when missing or invalid.
  • Return capture date as Y-m-d H:i when DateTimeOriginal is 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 example
<?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.