Skip to content

Commit

Permalink
Add a VarDumper caster
Browse files Browse the repository at this point in the history
  • Loading branch information
ogizanagi committed Jul 6, 2017
1 parent ca906f5 commit 1e06d8d
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 2 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Table of Contents
* [Simple enums](#simple-enums)
* [Flagged enums](#flagged-enums-1)
* [Symfony Validator component](#symfony-validator-component)
* [Symfony VarDumper component](#symfony-vardumper-component)
* [Faker](#faker)
* [Usage with Alice](#usage-with-alice)
* [API](#api)
Expand Down Expand Up @@ -91,6 +92,7 @@ Providing our own package inspired from the best ones, on which we'll apply our
- Symfony Form component integration with form types.
- Symfony Serializer component integration with a normalizer class.
- Symfony Validator component integration with an enum constraint.
- Symfony VarDumper component integration with a dedicated caster.
- Doctrine DBAL integration with abstract classes in order to persist your enumeration in database.

# Installation
Expand Down Expand Up @@ -650,6 +652,25 @@ Where `allowedValues` is a static method of `MyApp\Enum\Gender`, returning allow

Any other [Choice option](http://symfony.com/doc/current/reference/constraints/Choice.html#available-options) (as `multiple`, `min`, ...) is available with the `Enum` constraint.

## Symfony VarDumper component
<a href="https://symfony.com"><img src="https://img.shields.io/badge/Symfony-%202.8%2F3.1%2B-green.svg?style=flat-square" title="Available for Symfony 2.8 and 3.1+" alt="Available for Symfony 2.8 and 3.1+" align="right"></a>

By requiring this package and if `symfony/var-dumper` is installed, an `EnumCaster` is registered automatically to enhance enum instances dump output.

For instance, here's what it'll look like when dumping a flagged enum instance:

```php
<?php
use Elao\Enum\Tests\Fixtures\Enum\Permissions;
dump(Permissions::get(Permissions::ALL));
```

|HTML output|CLI output|
|-----------|----------|
|![var-dumper-integration-cli](res/img/dump-html.png)|![var-dumper-integration-cli](res/img/dump-cli.png)|

## Faker

The PhpEnums library provides an `Elao\Enum\Bridge\Faker\Provider\EnumProvider` to generate fixtures.
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"autoload": {
"psr-4": {
"Elao\\Enum\\": "src/"
}
},
"files": ["src/Bridge/Symfony/VarDumper/Resources/register_caster.php"]
},
"autoload-dev": {
"psr-4": { "Elao\\Enum\\Tests\\": "tests/" }
Expand Down Expand Up @@ -50,7 +51,8 @@
"symfony/twig-bundle": "^2.8|^3.1|^4.0",
"symfony/validator": "^2.8|^3.1|^4.0",
"symfony/templating": "^2.8|^3.1|^4.0",
"symfony/yaml": "^2.8|^3.1|^4.0"
"symfony/yaml": "^2.8|^3.1|^4.0",
"symfony/var-dumper": "^2.8.9|^3.1|^4.0"
},
"extra": {
"branch-alias": {
Expand Down
Binary file added res/img/dump-cli.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/img/dump-html.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions src/Bridge/Symfony/VarDumper/Caster/EnumCaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) 2016 Elao
*
* @author Elao <contact@elao.com>
*/

namespace Elao\Enum\Bridge\Symfony\VarDumper\Caster;

use Elao\Enum\EnumInterface;
use Elao\Enum\FlaggedEnum;
use Elao\Enum\ReadableEnum;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Caster\ConstStub;

final class EnumCaster
{
public static function castEnum(EnumInterface $enum, $array)
{
$a = [];
$value = $enum->getValue();
$r = new \ReflectionClass($enum);
$constants = $r->getConstants();

if (PHP_VERSION_ID >= 70100) {
$constants = array_filter($constants, function (string $k) use ($r) {
return $r->getReflectionConstant($k)->isPublic();
}, ARRAY_FILTER_USE_KEY);
}

$rConstants = array_flip($constants);

// Append constant(s) name(s)
$a[Caster::PREFIX_VIRTUAL . ''] = new ConstStub(implode(' | ', array_map(function ($v) use ($rConstants) {
return $rConstants[$v];
}, $enum instanceof FlaggedEnum && $enum->getFlags() ? $enum->getFlags() : (array) $value)), $value);

// Append readable value
if ($enum instanceof ReadableEnum) {
$a[Caster::PREFIX_VIRTUAL . 'readable'] = $enum->getReadable();
}

// Append the instance value
$a[Caster::PREFIX_PROTECTED . 'value'] = $value;

// Append single bit flags list
if ($enum instanceof FlaggedEnum) {
$a[Caster::PREFIX_PROTECTED . 'flags'] = array_map(function (int $flag) use ($rConstants) {
return new ConstStub($flag, $rConstants[$flag]);
}, $enum->getFlags());
}

// Append any other potential properties
return $a + $array;
}
}
17 changes: 17 additions & 0 deletions src/Bridge/Symfony/VarDumper/Resources/register_caster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) 2016 Elao
*
* @author Elao <contact@elao.com>
*/

use Elao\Enum\Bridge\Symfony\VarDumper\Caster\EnumCaster;
use Elao\Enum\EnumInterface;
use Symfony\Component\VarDumper\Cloner\VarCloner;

if (class_exists(VarCloner::class) && !isset(VarCloner::$defaultCasters[EnumInterface::class])) {
VarCloner::$defaultCasters += [EnumInterface::class => [EnumCaster::class, 'castEnum']];
}
167 changes: 167 additions & 0 deletions tests/Unit/Bridge/Symfony/VarDumper/Caster/EnumCasterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php

/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) 2016 Elao
*
* @author Elao <contact@elao.com>
*/

namespace Elao\Enum\Tests\Unit\Bridge\Symfony\VarDumper\Caster;

use Elao\Enum\EnumInterface;
use Elao\Enum\Tests\Fixtures\Enum\Gender;
use Elao\Enum\Tests\Fixtures\Enum\Permissions;
use Elao\Enum\Tests\Fixtures\Enum\SimpleEnum;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\AbstractDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;

class EnumCasterTest extends \PHPUnit_Framework_TestCase
{
use VarDumperTestTrait;

public static function setUpBeforeClass()
{
putenv('DUMP_LIGHT_ARRAY=1');
}

public static function tearDownAfterClass()
{
putenv('DUMP_LIGHT_ARRAY');
}

public function testCasterIsRegistered()
{
$this->assertArrayHasKey(
EnumInterface::class,
VarCloner::$defaultCasters,
'the caster is registered through composer autoload files'
);
}

public function testCast()
{
$expectedDump = <<<'EODUMP'
Elao\Enum\Tests\Fixtures\Enum\SimpleEnum {
⚑ : FIRST
#value: 1
}
EODUMP;

$this->assertDumpEquals($expectedDump, SimpleEnum::get(SimpleEnum::FIRST));
}

public function testCastReadable()
{
$expectedDump = <<<'EODUMP'
Elao\Enum\Tests\Fixtures\Enum\Gender {
⚑ : MALE
readable: "Male"
#value: "male"
}
EODUMP;

$this->assertDumpEquals($expectedDump, Gender::get(Gender::MALE));
}

public function testCastFlaggedEnum()
{
$expectedDump = <<<'EODUMP'
Elao\Enum\Tests\Fixtures\Enum\Permissions {
⚑ : EXECUTE | WRITE
readable: "Execute; Write"
#value: 3
#flags:%S [
%S 1
%S 2
]
}
EODUMP;

$this->assertDumpMatchesFormat($expectedDump, Permissions::get(Permissions::EXECUTE | Permissions::WRITE));

$expectedDump = <<<'EODUMP'
Elao\Enum\Tests\Fixtures\Enum\Permissions {
⚑ : NONE
readable: "None"
#value: 0
#flags:%S []
}
EODUMP;

$this->assertDumpMatchesFormat($expectedDump, Permissions::get(Permissions::NONE));

$expectedDump = <<<'EODUMP'
Elao\Enum\Tests\Fixtures\Enum\Permissions {
⚑ : EXECUTE | WRITE | READ
readable: "All permissions"
#value: 7
#flags:%A [
%S 1
%S 2
%S 4
]
}
EODUMP;

$this->assertDumpMatchesFormat($expectedDump, Permissions::get(Permissions::ALL));
}

public function testCastAsHtml()
{
$enum = Permissions::get(Permissions::ALL);

$dump = $this->dumpAsHtml($enum);

$expectedDump = <<<'EODUMP'
<header></header><boundary><abbr title="Elao\Enum\Tests\Fixtures\Enum\Permissions" class=sf-dump-note>Permissions</abbr> {<samp>
<span class=sf-dump-meta>&#9873; </span>: <span class=sf-dump-const title="7">EXECUTE | WRITE | READ</span>
<span class=sf-dump-meta>readable</span>: "<span class=sf-dump-str title="15 characters">All permissions</span>"
#<span class=sf-dump-protected title="Protected property">value</span>: <span class=sf-dump-num>7</span>
#<span class=sf-dump-protected title="Protected property">flags</span>:%S [<samp>
%S<span class=sf-dump-const title="%SEXECUTE%S">1</span>
%S<span class=sf-dump-const title="%SWRITE%S">2</span>
%S<span class=sf-dump-const title="%SREAD%S">4</span>
</samp>]
</samp>}
</boundary>

EODUMP;

$this->assertStringMatchesFormat($expectedDump, $dump);
}

private function dumpAsHtml($value): string
{
$cloner = new VarCloner();
$cloner->setMaxItems(-1);

$configure = function (HtmlDumper $dumper) {
$dumper->setDumpHeader('<header></header>');
$dumper->setDumpBoundaries('<boundary>', '</boundary>');
};

// To remove once symfony/var-dumper < 3.2 support is dropped to simplify things
if (!method_exists(HtmlDumper::class, 'setDisplayOptions')) {
$h = fopen('php://memory', 'r+b');
$dumper = new HtmlDumper($h);
$configure($dumper);
$dumper->dump($cloner->cloneVar($value)->withRefHandles(false), $h);
$dump = stream_get_contents($h, -1, 0);
fclose($h);

return $dump;
}

// Once symfony/var-dumper < 3.1 support is dropped, the light array flag can be used
// and StringMatches assertions removed in favor of Identical/Equals assertions
$flags = defined(AbstractDumper::class . '::DUMP_LIGHT_ARRAY') ? AbstractDumper::DUMP_LIGHT_ARRAY : null;
$dumper = new HtmlDumper(null, null, $flags);
$configure($dumper);

return $dumper->dump($cloner->cloneVar($value)->withRefHandles(false), true);
}
}

0 comments on commit 1e06d8d

Please sign in to comment.