Last active
June 17, 2020 05:57
-
-
Save simPod/b21cdd57c4873a2dadc4013c747a2fc9 to your computer and use it in GitHub Desktop.
WIP: JMS Serializer EnumHandler for myclabs/php-enum
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace NS; | |
use NS\Enum; | |
use NS\Exception\FileDoesntContainClassDefinition; | |
use NS\Exception\NotSupported; | |
use NS\PathnameToClass; | |
use NS\Kernel; | |
use Generator; | |
use JMS\Serializer\Context; | |
use JMS\Serializer\GraphNavigatorInterface; | |
use JMS\Serializer\Handler\SubscribingHandlerInterface; | |
use JMS\Serializer\Metadata\PropertyMetadata; | |
use JMS\Serializer\Visitor\DeserializationVisitorInterface; | |
use JMS\Serializer\Visitor\SerializationVisitorInterface; | |
use SplFileInfo; | |
use Symfony\Component\Finder\Finder; | |
use Webmozart\Assert\Assert; | |
use function is_bool; | |
use function is_float; | |
use function is_int; | |
use function is_string; | |
use function is_subclass_of; | |
use function rtrim; | |
final class EnumHandler implements SubscribingHandlerInterface | |
{ | |
private const PATH_PROPERTY_SEPARATOR = '::'; | |
/** @return Generator<array<string, int|string>> */ | |
public static function getSubscribingMethods() : Generator | |
{ | |
$files = (new Finder()) | |
->files() | |
->in(Kernel::getSrcDir()) | |
->name('*.php'); | |
$pathnameToClass = new PathnameToClass(Kernel::getSrcDir()); | |
$formats = ['json', 'xml', 'yml']; | |
/** @var SplFileInfo $file */ | |
foreach ($files as $file) { | |
try { | |
$class = $pathnameToClass->getClass($file); | |
} catch (FileDoesntContainClassDefinition $exception) { | |
continue; | |
} | |
if (! is_subclass_of($class, Enum::class)) { | |
continue; | |
} | |
foreach ($formats as $format) { | |
yield [ | |
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION, | |
'format' => $format, | |
'type' => $class, | |
'method' => 'serializeEnum', | |
]; | |
yield [ | |
'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, | |
'format' => $format, | |
'type' => $class, | |
'method' => 'deserializeEnum', | |
]; | |
} | |
} | |
} | |
/** | |
* @param mixed[] $typeMetadata | |
* | |
* @return mixed | |
* | |
* @psalm-template T | |
* @psalm-param Enum<T> $enum | |
*/ | |
public function serializeEnum(SerializationVisitorInterface $visitor, Enum $enum, array $typeMetadata, Context $context) | |
{ | |
$value = $enum->getValue(); | |
if (is_int($value)) { | |
return $visitor->visitInteger($value, $typeMetadata); | |
} | |
if (is_string($value)) { | |
return $visitor->visitString($value, $typeMetadata); | |
} | |
if (is_bool($value)) { | |
return $visitor->visitBoolean($value, $typeMetadata); | |
} | |
if (is_float($value)) { | |
return $visitor->visitDouble($value, $typeMetadata); | |
} | |
throw NotSupported::new(); | |
} | |
/** | |
* @param mixed $data | |
* @param mixed[] $type | |
* | |
* @psalm-template T | |
* @psalm-return Enum<T> | |
*/ | |
public function deserializeEnum( | |
DeserializationVisitorInterface $visitor, | |
$data, | |
array $type, | |
Context $context | |
) : Enum { | |
$enumClass = $this->getEnumClass($type); | |
return $enumClass::get($data); | |
} | |
/** | |
* @param mixed[] $type | |
* | |
* @psalm-template T | |
* @psalm-return class-string<Enum<T>> | |
*/ | |
private function getEnumClass(array $type) : string | |
{ | |
/** @psalm-var class-string<Enum<T>> $enumClass */ | |
$enumClass = $type['name']; | |
Assert::isAOf($enumClass, Enum::class); | |
return $enumClass; | |
} | |
private function getPropertyPath(Context $context) : string | |
{ | |
$path = ''; | |
$lastPropertyMetadata = null; | |
foreach ($context->getMetadataStack() as $element) { | |
if (! ($element instanceof PropertyMetadata)) { | |
continue; | |
} | |
$name = $element->name; | |
$path = '$' . $name . self::PATH_PROPERTY_SEPARATOR . $path; | |
$lastPropertyMetadata = $element; | |
} | |
if ($lastPropertyMetadata !== null) { | |
$path = $lastPropertyMetadata->class . self::PATH_PROPERTY_SEPARATOR . $path; | |
} | |
$path = rtrim($path, self::PATH_PROPERTY_SEPARATOR); | |
return $path; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I didn't want the type searching logic so for my needs I simplified it to this: