Go to file
Sebastian Bergmann 58c91cd888
Document that exporting a value can throw ObjectNotSupportedException
2026-08-01 09:13:57 +02:00
.github Update actions/checkout action to v7.0.1 2026-07-25 21:50:59 +00:00
.phive Update tools 2026-05-13 13:31:29 +02:00
build/scripts Create release when tag is pushed 2024-03-10 13:24:03 +01:00
src Document that exporting a value can throw ObjectNotSupportedException 2026-08-01 09:13:57 +02:00
tests Accept any object exporter, not just a chain, in Exporter's constructor 2026-08-01 09:11:45 +02:00
tools Update dependency tomasvotruba/type-coverage to ^2.3.0 2026-07-30 04:57:56 +00:00
.gitattributes Ignore renovate.json for export 2026-05-16 06:58:38 +02:00
.gitignore Remove .gitignore entry for Psalm cache 2024-06-26 09:03:08 +02:00
.php-cs-fixer.dist.php Update PHP-CS-Fixer configuration 2024-11-17 13:44:33 +01:00
ChangeLog.md Accept any object exporter, not just a chain, in Exporter's constructor 2026-08-01 09:11:45 +02:00
LICENSE Bump copyright year 2025-12-31 14:59:57 +01:00
README.md Accept any object exporter, not just a chain, in Exporter's constructor 2026-08-01 09:11:45 +02:00
SECURITY.md Update security policy 2026-05-01 08:33:37 +02:00
build.xml Do not use Phive to manage Composer 2026-05-13 13:31:16 +02:00
composer.json Introduce ObjectExporter interface and ObjectExporterChain for customizing how objects are exported 2026-07-13 13:48:57 +02:00
infection.json5.dist Do not remove function and method calls 2024-04-04 08:07:15 +02:00
phpstan.neon * Install PHPStan using Composer in order to be able to use phpstan/phpstan-strict-rules and tomasvotruba/type-coverage 2025-03-17 17:17:21 +01:00
phpunit.xml Update PHPUnit configuration 2025-01-18 09:13:29 +01:00
renovate.json Also automerge minor updates 2026-05-27 07:51:02 +02:00

README.md

Latest Stable Version CI Status codecov

sebastian/exporter

This component provides the functionality to export PHP variables for visualization.

Installation

You can add this library as a local, per-project dependency to your project using Composer:

composer require sebastian/exporter

If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency:

composer require --dev sebastian/exporter

Usage

Exporting:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;

$exporter = new Exporter;

/*
Exception Object #4 (
    'message' => '',
    'string' => '',
    'code' => 0,
    'file' => '/home/sebastianbergmann/test.php',
    'line' => 34,
    'previous' => null,
)
*/

print $exporter->export(new Exception);

Data Types

Exporting simple types:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;

$exporter = new Exporter;

// 46
print $exporter->export(46);

// 4.0
print $exporter->export(4.0);

// 'hello, world!'
print $exporter->export('hello, world!');

// false
print $exporter->export(false);

// NAN
print $exporter->export(acos(8));

// -INF
print $exporter->export(log(0));

// null
print $exporter->export(null);

// resource(13) of type (stream)
print $exporter->export(fopen('php://stderr', 'w'));

// Binary String: 0x000102030405
print $exporter->export(chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5));

Exporting complex types:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;

$exporter = new Exporter;

/*
Array &0 [
    0 => Array &1 [
        0 => 1,
        1 => 2,
        2 => 3,
    ],
    1 => Array &2 [
        0 => '',
        1 => 0,
        2 => false,
    ],
]
*/

print $exporter->export([[1, 2, 3], ['', 0, false]]);

/*
Array &0 [
    'self' => Array &1 [
        'self' => Array &1,
    ],
]
*/

$array         = [];
$array['self'] = &$array;

print $exporter->export($array);

/*
stdClass Object #4 (
    'self' => stdClass Object #4,
)
*/

$obj       = new stdClass;
$obj->self = $obj;

print $exporter->export($obj);

Compact exports:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;

$exporter = new Exporter;

// []
print $exporter->shortenedExport([]);

// [...]
print $exporter->shortenedExport([1, 2, 3, 4, 5]);

// stdClass Object ()
print $exporter->shortenedExport(new stdClass);

// Exception Object (...)
print $exporter->shortenedExport(new Exception);

// 'this\nis\na\nsuper\nlong\nstr...nspace'
print $exporter->shortenedExport(
<<<LONG_STRING
this
is
a
super
long
string
that
wraps
a
lot
and
eats
up
a
lot
of
space
LONG_STRING
);

Customizing How Objects Are Exported

By default, an object is exported property by property. Implement the ObjectExporter interface to control how objects of a type are represented:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\ExportContext;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\Exporter\ObjectExporter;

final class MoneyExporter implements ObjectExporter
{
    public function handles(object $object): bool
    {
        return $object instanceof Money;
    }

    public function export(object $object, Exporter $exporter, int $indentation, ExportContext $context): string
    {
        assert($object instanceof Money);

        return sprintf('Money (%d %s)', $object->amount(), $object->currency());
    }
}

An object exporter is registered by passing it to Exporter's constructor. It is consulted before the default representation of an object is used:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;

$exporter = new Exporter(objectExporter: new MoneyExporter);

// Money (1999 EUR)
print $exporter->export(new Money(1999, 'EUR'));

/*
stdClass Object #6 (
    'net' => Money (1999 EUR),
    'gross' => Money (2379 EUR),
)
*/

$price        = new stdClass;
$price->net   = new Money(1999, 'EUR');
$price->gross = new Money(2379, 'EUR');

print $exporter->export($price);

ObjectExporterChain composes multiple object exporters into a single one. They are consulted in the order in which they are composed into the chain and the first one whose handles() method returns true is asked for the representation:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\Exporter\ObjectExporterChain;

$exporter = new Exporter(
    objectExporter: new ObjectExporterChain(
        [
            new BasketExporter,
            new MoneyExporter,
        ],
    ),
);

Enums are objects and are therefore passed to the object exporters as well. When no object exporter handles an object, the default representation is used for it.

Repeated Occurrences

When the same object occurs more than once, the default representation is only used for the first occurrence; subsequent occurrences are replaced with a reference to the object:

/*
Array &0 [
    0 => Money Object #8 (
        'amount' => 1999,
        'currency' => 'EUR',
    ),
    1 => Money Object #8,
]
*/

$money = new Money(1999, 'EUR');

print (new Exporter)->export([$money, $money]);

An object exporter is responsible for the entire representation of the object it handles. Every occurrence of such an object is therefore exported by it:

/*
Array &0 [
    0 => Money (1999 EUR),
    1 => Money (1999 EUR),
]
*/

print $exporter->export([$money, $money]);

The exception is an object that is (indirectly) nested in itself: it cannot be exported this way without recursing infinitely and is replaced with a reference to the object.

Exporting Nested Values

An object exporter may use the Exporter it is given to export values that are nested in the object it handles. The ExportContext it is given must be passed on when it does. Otherwise, the export of these nested values starts over with an empty context and, for instance, assigns references to arrays that are already in use elsewhere in the same export:

<?php declare(strict_types=1);
use SebastianBergmann\Exporter\ExportContext;
use SebastianBergmann\Exporter\Exporter;
use SebastianBergmann\Exporter\ObjectExporter;

final class BasketExporter implements ObjectExporter
{
    public function handles(object $object): bool
    {
        return $object instanceof Basket;
    }

    public function export(object $object, Exporter $exporter, int $indentation, ExportContext $context): string
    {
        assert($object instanceof Basket);

        return 'Basket ' . $exporter->export($object->items(), $indentation, $context);
    }
}
/*
Array &0 [
    'basket' => Basket Array &1 [
        0 => Money (1999 EUR),
    ],
]
*/

print $exporter->export(['basket' => new Basket([new Money(1999, 'EUR')])]);

Object exporters are not consulted by Exporter::shortenedExport() and Exporter::shortenedRecursiveExport(), which deliberately elide the contents of objects.