PHPackages                             codewiser/exiftool - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. codewiser/exiftool

ActiveLibrary

codewiser/exiftool
==================

Light-weight IPTC reader/writer

v1.0.2(1y ago)05[1 issues](https://github.com/C0deWiser/exiftool/issues)MITPHPPHP ^8.1

Since Oct 10Pushed 11mo ago2 watchersCompare

[ Source](https://github.com/C0deWiser/exiftool)[ Packagist](https://packagist.org/packages/codewiser/exiftool)[ RSS](/packages/codewiser-exiftool/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (4)Versions (5)Used By (0)

IPTC metadata
=============

[](#iptc-metadata)

```
Using Exiftool. Using IPTC specification.

```

This lightweight package based on [machine-readable specification](https://iptc.org/std/photometadata/specification/)of IPTC provided by [iptc.org](https://iptc.org/std/photometadata/documentation/techreference/).

As specification describes how exiftool exports/imports metadata, describes every attribute and every structure — this package uses json-version of specification as a framework.

This package provides an object-oriented programmatic interface to work with iptc attributes as objects.

This package provides [some helpers](laravel.md) for Laravel.

Known issues
------------

[](#known-issues)

IPTC specification describes `locationCreated` as a single element, but `Exiftool` counts it as a bag and requires an array of `Location` structures.

IPTC specification describes `ProductWGtin.identifiers` as multiple element (array), but `Exiftool` requires single element.

`Exiftool` doesnt import `GPSAltitudeRef` as `GPSAltitude` may be positive or negative — this is enough.

This library overrides IPTC specification, making `locationCreated` multiple and `ProductWGtin.identifiers` single. And ignores `GPSAltitudeRef`.

Configuration
-------------

[](#configuration)

Construct `Exiftool` with a path to `exiftool` binary and, optionally, with path to a specification file.

```
use Codewiser\Exiftool\Exiftool;

// With the latest specification
$exiftool = new Exiftool('/bin/exiftool');

// Or with another
$exiftool = new Exiftool('/bin/exiftool', '/path/to/specification.json');
```

You may find the latest specification on [iptc.org repository](https://iptc.org/std/photometadata/specification/).

Read metadata
-------------

[](#read-metadata)

```
use Codewiser\Exiftool\Iptc;

public function read(string $filename): Iptc
{
    return $this->exiftool()->read($filenme);
}
```

Embed metadata
--------------

[](#embed-metadata)

```
use Codewiser\Exiftool\Iptc;
use Symfony\Component\Process\Process;

public function write(string $filename, Iptc $data): Process
{
    return $this->exiftool()->write($filenme, $data);
}
```

Method returns `Process` object for you to inspect possible error output.

Clear metadata
--------------

[](#clear-metadata)

```
use Symfony\Component\Process\Process;

public function write(string $filename): Process
{
    return $this->exiftool()->clear($filenme);
}
```

Fake or empty metadata
----------------------

[](#fake-or-empty-metadata)

You may create an empty metadata collection and fill it with fake values:

```
// empty collection
$empty = $exiftool->newMetadata();

// requires `fakerphp/faker`
$faked = $exiftool->newMetadata()->fake();
```

Properties
----------

[](#properties)

Whole collection and every attribute are `JsonSerializable`: this method exports metadata to JSON format.

```
$json = $data->jsonSerialize();
```

The whole collection and every single attribute may be filled with JSON data:

```
$data->fromJson(['captionWriter' => 'me']);
```

### Plain

[](#plain)

`Plain` attribute is `Stringable` and has `toString` method, that is used to get attribute scalar value.

```
(string) $data->captionWriter;
// the same as
$data->captionWriter?->toString();

$data->captionWriter = 'me';
```

### DateTime

[](#datetime)

`DateTime` attribute is `Stringable` and has `toDateTime` method, that is used to get `DateTimeInterface` value.

```
(string) $data->dateCreated;
// the same as
$data->dateCreated?->toDateTime()->format('c');

$data->dateCreated = time();
$data->dateCreated = new \DateTime();
```

### AltLang

[](#altlang)

`AltLang` attribute keeps an array of strings, each for different locale. Default current locale is `en`. You may change it:

```
use Codewiser\Exiftool\Attributes\AltLangAttribute;

AltLangAttribute::useLocale('de');
```

When importing data, `AltLang` stores default value to current locale. When exporting, `AltLang` current locale value will be embedded as default.

```
If exiftool export is:

[
    "AltTextAccessibility" => "Value 1",
    "AltTextAccessibility-es" => "Value 2",
    "AltTextAccessibility-de" => "Value 3",
]

and current locale is `en` — the result will be:

[
    "en" => "Value 1",
    "es" => "Value 2",
    "de" => "Value 3",
]

```

`AltLang` attribute is `Stringable` and has `toString` method, that is used to get attribute value in current locale.

```
(string) $data->description;
// the same as
$data->description?->toString();

$data->description = 'about';
```

It has `toArray` method te get all localized values.

```
$data->description?->toArray();
```

It implements `ArrayAccess`, so you can access values using locale as key.

```
isset($data->description['cn']);

$data->description['en'] = 'about';
```

When your backend-end responds with metadata to a front-end, the `AltLang`attribute responds as an array. Sometimes you may want to collapse `AltLang`values to a string with current locale (or best matched) value. To do that,
instruct attribute to collapse values before respond.

```
use Codewiser\Exiftool\Attributes\AltLangAttribute;

AltLangAttribute::collapse();

return json_encode($data->jsonSerialized());
```

### Structure

[](#structure)

`Structure` is a collection of attributes. All structures are well documented with all their attributes. Every attribute may be any of types.

```
$data->creatorContactInfo?->address?->toString();

$data->locationCreated?->name['en']
```

To add structure, fill it with a JSON:

```
$data->locationCreated = ['city' => 'London', 'country' => 'UK'];
```

Or use factory to create an empty object:

```
$data->locationCreated = $exiftool->newStructure()->location();
$data->locationCreated->city = 'London';
```

### Multiple

[](#multiple)

`Multiple` attribute is array of other attributes of any type. It is `Iterator`, `ArrayAccess` and `Countable`, so you may handle it as a true array.

```
foreach ($data->keywords as $keyword) {
    //
}

count($data->keywords);

(string) $data->keywords[0];

$data->keywords = ['value 1', 'value 2'];

// etc.
```

Of course, `Multiple` may consist of structures:

```
$data->locationsShown = [
    ['city' => 'London', 'country' => 'UK'],
    ['city' => 'Paris', 'country' => 'France'],
];
```

Print Conv
----------

[](#print-conv)

Read

Without enabling `$exiftool->printConv()` all values is human-readable. For example, `GPSLatitude` may have value `45 deg 20' 11.00"`.

If you call `$exiftool->printConv()` before importing/exporting IPTC metadata, you should use *dirty* values. For example, `GPSLatitude` may have value `45.3363888888889`.

This is very important in context of `enum` attributes — that must use values from a limited list. Exiftool will reject value if it is not from a list.

Read more below.

Enum values
-----------

[](#enum-values)

Some attributes, such as `dataMining`, `modelReleaseStatus`, `rbShape` and some others, require their values from a limited list.

For example, `Exiftool` internally keeps `dataMining` values as `DMI-UNSPECIFIED`, `DMI-ALLOWED` etc., but exports it as `Unspecified - no prohibition defined`, `Allowed` etc. Conversely, you should import this attribute with values `Unspecified - no prohibition defined`, `Allowed` etc.

However, with enabled `$exiftool->printConv()` we will export/import it with keys (`DMI-UNSPECIFIED`, `DMI-ALLOWED` etc.) instead of values.

You may inspect the attribute specification for its enum values:

```
use Codewiser\Exiftool\Exiftool;

$exiftool = new Exiftool($bin);

$values = $exiftool->specification()->topLevel()
    ->getAttributeByJsonName('dataMining')->enum();
```

For example, this is enum values for `modelReleaseStatus` attribute:

```
[
    'MR-NON' => 'None',
    'MR-NAP' => 'Not Applicable',
    'MR-UMR' => 'Unlimited Model Releases',
    'MR-LMR' => 'Limited or Incomplete Model Releases',
]
```

If you call `enum()` on attribute without enabling `printConv`, you will gat values of such an array. If you call `enum()` on attribute with enabled `printConv`, you will gat keys of such an array.

Controlled Vocabularies
-----------------------

[](#controlled-vocabularies)

Some attributes, such as `sceneCodes`, `subjectCodes` and some others, should use values from external controlled vocabularies, called [NewsCodes](https://iptc.org/standards/newscodes/).

[Read more](laravel.md#controlled-vocabularies)

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance47

Moderate activity, may be stable

Popularity4

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~35 days

Total

4

Last Release

481d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ce23eaf7ae48d79d2b07efc83ff7cecb2664f428bd531b167b90169423f1453d?d=identicon)[Cellard](/maintainers/Cellard)

---

Top Contributors

[![Cellard](https://avatars.githubusercontent.com/u/1220316?v=4)](https://github.com/Cellard "Cellard (21 commits)")

---

Tags

exiftooliptclaravellaravelIPTCexiftool

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/codewiser-exiftool/health.svg)

```
[![Health](https://phpackages.com/badges/codewiser-exiftool/health.svg)](https://phpackages.com/packages/codewiser-exiftool)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.7k509.9M17.0k](/packages/laravel-framework)[laravel/horizon

Dashboard and code-driven configuration for Laravel queues.

4.1k84.2M225](/packages/laravel-horizon)[orchestra/testbench

Laravel Testing Helper for Packages Development

2.2k39.1M32.1k](/packages/orchestra-testbench)[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k36.7M259](/packages/laravel-dusk)[laravel/envoy

Elegant SSH tasks for PHP.

1.6k5.5M18](/packages/laravel-envoy)[spatie/laravel-health

Monitor the health of a Laravel application

86910.0M83](/packages/spatie-laravel-health)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
