kraenzle-ritter / exif-extract
Unified PHP interface for extracting technical metadata (EXIF, IPTC, XMP, …) from media files using ExifTool.
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
exif-extract is a PHP library for extracting technical metadata
(EXIF, IPTC, XMP, …) from media files. It wraps the
ExifTool CLI behind a small engine
abstraction so applications get strongly-typed results without
re-implementing the proc_open / chunked-batch / encoding-recovery
plumbing on every call site.
This is the sibling package of
kraenzle-ritter/puidentify
(format identification via PRONOM PUIDs) — both packages share the
same engine-manager pattern.
Installation
composer require kraenzle-ritter/exif-extract
Requirements
- PHP 8.1 or higher
- ExifTool installed and available in
PATH(theexiftoolcommand).
Quick Start
use ExifExtract\ExifIdentifier; use ExifExtract\Engine\ExifToolEngine; $identifier = new ExifIdentifier([new ExifToolEngine()]); $result = $identifier->extract('/path/to/photo.jpg'); if ($result !== null) { echo $result->engineVersion; // "13.50" echo $result->metadata['EXIF:Make'] ?? '(no make)'; echo $result->metadataCount(); }
Batch Mode
For ingesting many files, use extractBatch() — the engine batches
the underlying exiftool invocations (default chunk size: 500 files)
and falls back to per-file mode for any chunk that errors out.
$paths = [ '/path/to/a.jpg', '/path/to/b.tif', '/path/to/c.jp2', ]; $results = $identifier->extractBatch($paths); foreach ($results as $path => $result) { if ($result === null) { // File was missing, oversized, or no metadata extracted. continue; } // … process $result->metadata }
Custom Binary Path
$engine = new ExifToolEngine('/opt/homebrew/bin/exiftool');
Tuning the Engine
$engine = new ExifToolEngine('exiftool', [ 'per_file_timeout' => 60, // seconds, default 30 'batch_timeout_floor' => 30, // seconds 'batch_timeout_ceiling' => 300, // seconds 'chunk_size' => 250, // files per exiftool invocation 'max_file_size' => 100 * 1024 * 1024, // 100 MB skip ]);
Engine Interface
ExifExtract\Engine\EngineInterface allows alternative implementations
(e.g. a pure-PHP getimagesize-based fallback or an ImageMagick wrapper)
to be plugged into ExifIdentifier without changes to consumers.
interface EngineInterface { public function extract(string $filePath): ?ExifResult; /** @param string[] $filePaths */ public function extractBatch(array $filePaths): array; public function isAvailable(): bool; public function getName(): string; public function getVersion(): ?string; }
Result Object
ExifExtract\Result\ExifResult is a small immutable value object:
| Property | Type | Description |
|---|---|---|
sourceFile |
string |
Absolute path of the source file |
metadata |
array<string, mixed> |
Group-prefixed flat array as returned by exiftool -j -G |
engine |
string |
Engine name (e.g. "ExifTool") |
engineVersion |
?string |
Engine version (e.g. "13.50") |
metadata keys carry the ExifTool group prefix: EXIF:Make,
File:FileSize, XMP-dc:Title. The SourceFile key from ExifTool is
preserved inside metadata so consumers can correlate.
Exceptions
All exceptions extend ExifExtract\Exception\ExifExtractException.
| Exception | Thrown when |
|---|---|
FileNotFoundException |
The input file does not exist or is unreadable |
FileTooLargeException |
The file exceeds the configured max_file_size |
TimeoutException |
The ExifTool invocation exceeded its timeout |
EngineException |
The binary is missing, returned no output, or returned unparseable JSON |
Testing
composer install vendor/bin/phpunit
Engine-level tests are skipped automatically if exiftool is not in
the PATH. The ExifIdentifier tests use stub engines and run
without any external binary.
License
MIT — see LICENSE.