Improve export of enums

This commit improves the export of enumerations (see https://wiki.php.net/rfc/enumerations). This change is backwards-compatible with PHP 8.0. Previously, enums would be exported as objects (which they are internally), for instance:

```
SebastianBergmann\Exporter\ExampleEnum Object #%d (
    'name' => 'Value'
)"
```

This commit changes that behaviour and improves the export for both backed and non-backed enums:

Unit enum:
 SebastianBergmann\Exporter\ExampleEnum Enum #%d (Value)

Backed enum:
 SebastianBergmann\Exporter\ExampleBackedEnum Enum #%d (Value, 'value')
This commit is contained in:
Marijn van Wezel 2022-01-16 22:51:08 +01:00 committed by Sebastian Bergmann
parent 2d3ded59e9
commit ba5c2c83da
7 changed files with 152 additions and 0 deletions

View File

@ -11,5 +11,9 @@
<code>$hash</code>
<code>$key</code>
</InvalidScalarArgument>
<NoInterfaceProperties occurrences="6">
<code>$value->name</code>
<code>$value->value</code>
</NoInterfaceProperties>
</file>
</files>

View File

@ -48,6 +48,11 @@
"src/"
]
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"

View File

@ -15,6 +15,7 @@ use function get_class;
use function get_resource_type;
use function gettype;
use function implode;
use function interface_exists;
use function is_array;
use function is_float;
use function is_object;
@ -28,8 +29,10 @@ use function sprintf;
use function str_repeat;
use function str_replace;
use function var_export;
use BackedEnum;
use SebastianBergmann\RecursionContext\Context;
use SplObjectStorage;
use UnitEnum;
/**
* A nifty utility for visualizing PHP variables.
@ -115,6 +118,26 @@ final class Exporter
return $string;
}
// FIXME: Remove 'interface_exists' check once we drop support for PHP 8.0
if (interface_exists('\UnitEnum')) {
if ($value instanceof BackedEnum) {
return sprintf(
'%s Enum (%s, %s)',
get_class($value),
$value->name,
$this->export($value->value)
);
}
if ($value instanceof UnitEnum) {
return sprintf(
'%s Enum (%s)',
get_class($value),
$value->name
);
}
}
if (is_object($value)) {
return sprintf(
'%s Object (%s)',
@ -217,6 +240,28 @@ final class Exporter
);
}
// FIXME: Remove 'interface_exists' check once we drop support for PHP 8.0
if (interface_exists('\UnitEnum')) {
if ($value instanceof BackedEnum) {
return sprintf(
'%s Enum #%d (%s, %s)',
get_class($value),
spl_object_id($value),
$value->name,
$this->export($value->value, $indentation)
);
}
if ($value instanceof UnitEnum) {
return sprintf(
'%s Enum #%d (%s)',
get_class($value),
spl_object_id($value),
$value->name
);
}
}
if (is_string($value)) {
// Match for most non printable chars somewhat taking multibyte chars into account
if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {

18
tests/ExampleEnum.php Normal file
View File

@ -0,0 +1,18 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/exporter.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Exporter;
/*
* Helper to test export of enums.
*/
enum ExampleEnum
{
case Value;
}

View File

@ -0,0 +1,18 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/exporter.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Exporter;
/*
* Helper to test export of backed enums.
*/
enum ExampleIntegerBackedEnum: int
{
case Value = 0;
}

View File

@ -0,0 +1,18 @@
<?php declare(strict_types=1);
/*
* This file is part of sebastian/exporter.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\Exporter;
/*
* Helper to test export of backed enums.
*/
enum ExampleStringBackedEnum: string
{
case Value = 'value';
}

View File

@ -365,6 +365,50 @@ EOF;
mb_language($oldMbLanguage);
}
public function enumProvider()
{
return [
'export enum' => [ExampleEnum::Value, 'SebastianBergmann\Exporter\ExampleEnum Enum #%d (Value)'],
'export backed enum' => [ExampleStringBackedEnum::Value, 'SebastianBergmann\Exporter\ExampleStringBackedEnum Enum #%d (Value, \'value\')'],
'shortened export integer backed enum' => [ExampleIntegerBackedEnum::Value, 'SebastianBergmann\Exporter\ExampleIntegerBackedEnum Enum #%d (Value, 0)'],
];
}
/**
* @requires PHP 8.1
* @dataProvider enumProvider
*/
public function testEnumExport($value, $expected): void
{
// FIXME: Merge test into testExport once we drop support for PHP 8.0
$this->assertStringMatchesFormat(
$expected,
$this->trimNewline($this->exporter->export($value))
);
}
public function enumShortenedProvider()
{
return [
'shortened export enum' => [ExampleEnum::Value, 'SebastianBergmann\Exporter\ExampleEnum Enum (Value)'],
'shortened export string backed enum' => [ExampleStringBackedEnum::Value, 'SebastianBergmann\Exporter\ExampleStringBackedEnum Enum (Value, \'value\')'],
'shortened export integer backed enum' => [ExampleIntegerBackedEnum::Value, 'SebastianBergmann\Exporter\ExampleIntegerBackedEnum Enum (Value, 0)'],
];
}
/**
* @requires PHP 8.1
* @dataProvider enumShortenedProvider
*/
public function testEnumShortenedExport($value, $expected): void
{
// FIXME: Merge test into testShortenedExport once we drop support for PHP 8.0
$this->assertStringMatchesFormat(
$expected,
$this->trimNewline($this->exporter->shortenedExport($value))
);
}
public function provideNonBinaryMultibyteStrings(): array
{
return [