Read-only metadata extraction for JPEG, ISO BMFF, TIFF-based, and RIFF/AVI files in PHP.
ImageMeta is a PHP library for read-only metadata extraction from image and media containers. It parses metadata from JPEG, ISO BMFF (for example HEIC, AVIF, MOV, MP4), TIFF-based files, JPEG XL containers, and RIFF/AVI video files. The library exposes both low-level parsed documents and a typed structured aggregate for application-level usage. Parsing is defensive by design, with explicit bounds checks and strict validation.
| Key | Value |
|---|---|
| Package | magicsunday/imagemeta |
| PHP | >=8.4.0 <8.6.0 |
| Main API | MagicSunday\ImageMeta\MetadataReader |
| Output | Raw Model\Metadata + typed Value\StructuredMetadata |
ImageMeta reads metadata from supported containers and returns both raw parsed documents and a typed, structured aggregate (StructuredMetadata). The structured output normalizes camera, exposure, GPS, temporal, and lens data across EXIF, XMP, IPTC, QuickTime, and RIFF INFO sources via automatic fallback chains β applications get complete metadata without knowing which source holds it. Vendor maker notes (Apple, Samsung, DJI) and ICC color profiles are decoded where present.
PHP's built-in exif_read_data() is limited to JPEG/TIFF EXIF tags and returns untyped arrays. It cannot read ISO BMFF containers (HEIC, AVIF, MOV, MP4), has no XMP/IPTC/QuickTime support, and offers no structured output model. ImageMeta closes that gap: one API, five container families, six metadata sources, typed value objects with automatic cross-source fallback.
In scope:
- Local file parsing via signature-based container detection (JPEG, ISO BMFF, TIFF, JXL, RIFF/AVI).
- Extraction and merge of metadata sources: EXIF, XMP, IPTC, QuickTime, RIFF INFO, and ICC profiles.
- Typed structured output (
StructuredMetadata) with automatic EXIF β XMP β QuickTime β RIFF fallback chains. - Vendor maker-note decoding (Apple, Samsung, DJI).
- MPF (Multi-Picture Format) document parsing.
- Defensive streaming parser with explicit bounds checks and configurable limits.
Out of scope:
- Writing, editing, or re-serializing metadata.
- Pixel/media decoding, rendering, or transcoding.
- Network-based metadata resolution.
- Guaranteed support for every proprietary maker-note dialect.
| Container | File types | Metadata extracted |
|---|---|---|
| JPEG | .jpg, .jpeg |
EXIF, XMP, IPTC, ICC, MPF, FlashPix, JFIF, EXIF audio |
| ISO BMFF | .heic, .heif, .avif, .mp4, .mov, .m4v, .3gp |
EXIF, XMP, QuickTime metadata, ICC, item references |
| TIFF | .tiff, .tif, .dng, .nef, .arw, .cr2 |
EXIF, XMP (tag 700), IPTC (tag 33723), ICC (tag 34675) |
| JXL | .jxl |
EXIF (Exif box), XMP (xml box), HDR gain map (hrgm) |
| RIFF/AVI | .avi |
INFO chunks, XMP (_PMX), RIFF EXIF (LIST 'exif'), TIFF blobs (strd), AVI header |
Detection is signature-based (magic bytes), not extension-based.
| Source | Scope |
|---|---|
| EXIF | Versions 1.0 through 3.1 with full capability mapping per version |
| XMP | RDF/XML parsing via XMLReader (non-network, entity-safe) |
| IPTC | IIM datasets from JPEG APP13 and TIFF tag 33723 |
| QuickTime | Keys/data atoms, mvhd/tkhd/mdhd timestamps, track media metadata |
| RIFF INFO | Key-value pairs (INAM, IART, ICRD, ISFT, IDIT, etc.) |
| ICC | Profile header, tag table, and tag data parsing |
| Vendor | Coverage |
|---|---|
| Apple | HDR, LivePhoto, AutoFocus, AutoExposure, capture identity, scene analysis |
| Samsung | Maker note tag decoding |
| DJI | Maker note tags + mdat protobuf telemetry scanning (GPS, model for truncated recordings) |
| Feature | Details |
|---|---|
| MPF | CIPA DC-007-2025, 3rd Edition (all MP type codes including Gain Map) |
| FlashPix | OLE compound document / property set parsing |
| DNG | DNG 1.7.1 tag semantics |
| Structured output | ->structured() returns typed value objects: Camera, Exposure, GPS, Temporal, Lens, Sensor, Image, Device, Scene, Motion, Regions, MultiPicture |
- File-level support depends on whether the input actually contains parseable metadata blocks.
- For JXL, the current scope is metadata-box extraction (EXIF/XMP/gain map); no pixel/codestream decode.
- For RIFF/AVI, vendor-specific JUNK-chunk maker notes are not yet supported.
composer require magicsunday/imagemeta<?php
declare(strict_types=1);
use MagicSunday\ImageMeta\MetadataReader;
$metadata = MetadataReader::createDefault()->read('/path/to/photo.heic');
$structured = $metadata->structured();
// Typed structured access (automatic EXIF β XMP β QuickTime β RIFF fallback):
$cameraMake = $structured->hardware->camera->make;
$iso = $structured->settings->exposure->settings->iso;
$latitude = $structured->locationTime->gps->position->latitudeCoordinate?->signed;
$createDate = $structured->locationTime->temporal->original;
// Low-level raw access:
$exif = $metadata->exifDoc;
$xmp = $metadata->xmpDoc ?? $metadata->selectiveXmpDocument();
$quickTime = $metadata->quickTime;JPEG XL example:
<?php
declare(strict_types=1);
use MagicSunday\ImageMeta\MetadataReader;
$metadata = MetadataReader::createDefault()->read('/path/to/image.jxl');
// JXL currently exposes metadata carried in Exif/xml boxes.
$exif = $metadata->exifDoc;
$xmp = $metadata->xmpDoc;AVI example:
<?php
declare(strict_types=1);
use MagicSunday\ImageMeta\MetadataReader;
$metadata = MetadataReader::createDefault()->read('/path/to/video.avi');
// AVI exposes INFO chunks, RIFF-native EXIF fields, XMP, and embedded TIFF/EXIF blobs.
$info = $metadata->riffInfo;
$title = $info?->get('INAM');
$software = $info?->get('ISFT');
$aviHeader = $metadata->riffAviHeader;
$width = $aviHeader?->width;
$height = $aviHeader?->height;- Exceptions:
MagicSunday\ImageMeta\Core\ParseErrorfor malformed/unsupported content and validation failures.MagicSunday\ImageMeta\Core\BoundsErrorfor out-of-range offset/length access.
- Guarantees:
- Bounds and limits are enforced in stream and parser layers (for example
src/Core/*,src/Parse/ParserLimits.php). - XMP parsing uses
XMLReaderwithLIBXML_NONET(andLIBXML_NO_XXEwhen available), disabling network/entity resolution. - The library is read-only and does not modify input files.
- Bounds and limits are enforced in stream and parser layers (for example
- Not guaranteed:
- Full coverage of all proprietary maker-note formats.
Development uses a Docker buildbox. All commands are available via make:
make build # Build the Docker image (first time)
make install # Install composer dependencies
make test # Run full CI pipeline (mandatory gate before any commit)make test runs (in order): phplint β php-cs-fixer (dry-run) β rector (dry-run) β phpstan β phpunit β jscpd
Additional targets:
| Target | Description |
|---|---|
make lint |
PHP linter only |
make cgl-check |
Code style check (dry-run) |
make cgl |
Fix code style |
make rector-check |
Rector check (dry-run) |
make rector |
Apply rector rules |
make stan |
PHPStan analysis |
make unit |
PHPUnit tests only |
make coverage |
PHPUnit with HTML + Clover coverage report |
make cpd |
Copy-paste detection (jscpd) |
make bash |
Open a shell in the buildbox container |
make format FILE=<path> |
Format metadata output for a file (--debug, --digest flags) |
- PHP
>=8.4.0 <8.6.0 - Extensions:
ctype,date,fileinfo,hash,iconv,json,mbstring,pcre,xmlreader
composer install
composer ci:testSpecifications and reference material live in docs/.
Primary files used by parser development:
docs/EXIF-310.pdf-- EXIF 3.1 (CIPA DC-008-2026)docs/EXIF-300.pdf(plus historical revisionsdocs/EXIF-2*.pdf)docs/TIFF6.pdfdocs/ISO_IEC_14496-12_2015.pdfdocs/Quicktime-File-Format-2012.pdfdocs/XMP.pdfdocs/ICC.pdfdocs/MPF.pdf-- MPF 3rd Edition (CIPA DC-007-2025)docs/DNG_Spec_1_7_1_0.pdf
HTML mirrors (docs/*.html) are available for faster navigation/search during implementation.
See CONTRIBUTING.md for contributor workflow and minimal setup.
If contributions are prepared or modified by an LLM/agent, follow AGENTS.md (and tests/AGENTS.md for test-only scope).