PhpEnums
PhpEnums copied to clipboard
🚧 V2: Rewrite for PHP 8.1 Enum
First step was made in https://github.com/Elao/PhpEnums/pull/165 to rewrite the lib with the new native enum types & adding some integrations based on it and our specificities (readables, flags, …).
📚 See current V2 documentation here
🚧 More work to come. Help welcome to integrate what could be useful, from 1.x features or whole new ones.
Will probable create a 2.0.0-alpha1
tag in the next weeks.
V2 Features
Core features
From V1, core features extending the PHP 8.1 native enum capabilities
- [x] Readable enums ➜ Done in #165.
- [x] Flag enums ➜ Done in #165.
It requires a new
FlagBag
object to manipulate flags, since PHP native enums are restricted to their cases and cannot dynamically declare combinations.
New features
Whole new features or integration, leveraging new enum capabilities
- [ ] Overblog GraphQL type? Might be supported by them directly.
- ???
(Pending) official integrations
From V1, likely to be dropped in the future, or already having an official integration
- [x] Serializer integration Core PR: https://github.com/symfony/symfony/issues/40241
- [ ] Doctrine DBAL Types & types generator:
~Doctrine is likely to provide DBAL types for enums in the future, but the current type system lacks a way to easily register it.~
➜ Doctrine guys found a workaround in https://github.com/doctrine/orm/pull/9304.
We might consider dropping our implementation, but collection and flag bag support is still valid. Also our current implementation supports specifying a default on null.
- [x] Basic \BackedEnum support ➜ Done in #165. Documentation
- [ ] Collection of enums (csv & json)
- [x] FlagBag DBAL type for persisting flag enums combination ➜ #186
- [ ] Doctrine ODM Types & types generator:
- [x] Basic \BackedEnum support: ➜ #176
- [x] Collection of enums: ➜ #176
- [ ] FlagBag ODM type for persisting flag enums combination
- [x] HTTP Kernel Controller Argument Resolver
Nothing in Symfony core yet?
- Core PR => https://github.com/symfony/symfony/pull/44831
- V2 PR: https://github.com/Elao/PhpEnums/pull/169
- [x] API Platform no specificity for readable or flagged enum? Anything else to consider? Core PR: https://github.com/api-platform/core/issues/4349
- [x] Faker
could be contributed to FakerPHP directly for native enum support
➜ rejected in https://github.com/FakerPHP/Faker/pull/421.
We might only want to port
randomEnum
from V1 to get a random enum case from given enum FQCN. Porting the namespace aliases feature might not be relevant, since it was targeting Nelmio Alice's fixtures generator DSL, but Foundry is getting more popularity now. ➜ #177 - [ ] Validator constraint. Rejected for now in https://github.com/symfony/symfony/pull/43047. Could provide the alternative here and re-discuss in Symfony's core later if proven useful.
Extra integrations
From V1, might still be relevant/adapted
- [x] VarDumper casters
Core PR: https://github.com/symfony/symfony/issues/40238)
- #167
- #168
- [x] Symfony Form Type
Core PR: https://github.com/symfony/symfony/pull/43095
- [x] Extend core EnumType for readable enums ➜ Done in #165. Documentation
- [x] FlagBag Form type to combine flag enums ➜ Done in #183
- [ ] Javascript generator
- [ ] Twig extension ? Any needs ?
- [ ] translation extractor & config for readables
Extra tasks
- [ ] Write an
UPGRADE-2.x.md
guide, with most basic hints for upgrading your application code from 1.x to 2.x
https://github.com/symfony/symfony/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+enum+label%3A%22Help+wanted%22
Likely to be dropped in the future, if any rewrite of this library is done based on the new enum type:
- Serializer integration => https://github.com/symfony/symfony/issues/40241
- DBAL Types & DBAL types generator
- HTTP Kernel Controller Argument Resolver
- API Platform: no specificity for readable or flagged enum? Anything else to consider?
- Faker: could be contributed to FakerPHP directly for native enum support
- Validator constraint. Likely to be supported in Symfony directly. => https://github.com/symfony/symfony/pull/43047
Might still benefits:
- VarDumper caster for the readable & flagged enums (Symfony will support basic enums => https://github.com/symfony/symfony/issues/40238)
- Symfony Form Type ? For readable & flagged enum specificities? => https://github.com/symfony/symfony/pull/43095
- Javascript generator
- Twig extension ? At least for readables?
- translation extractor & config for readables
Extras:
- Readable enums could be using PHP 8 attributes to provide their labels: https://github.com/Elao/PhpEnums/projects/1#card-66229974: https://3v4l.org/Gag4r#v8.1rc3 :
<?php
#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
class EnumLabel
{
public function __construct(public string $label)
{
}
}
enum Suit: string
{
#[\EnumLabel('suit.hearts')]
case Hearts = 'H';
#[\EnumLabel('suit.diamonds')]
case Diamonds = 'D';
#[\EnumLabel('suit.diamonds')]
case Clubs = 'C';
#[\EnumLabel('suit.spades')]
case Spades = 'S';
}
var_dump(Suit::Hearts);
var_dump($reflector = new \ReflectionEnum(Suit::class));
var_dump($reflector->getCase('Hearts')->getAttributes(\EnumLabel::class)[0]->newInstance());
var_dump((new \ReflectionClassConstant(Suit::class, 'Hearts'))->getAttributes(\EnumLabel::class)[0]->newInstance());
# output:
enum(Suit::Hearts)
object(ReflectionEnum)#2 (1) {
["name"]=>
string(4) "Suit"
}
object(EnumLabel)#3 (1) {
["label"]=>
string(11) "suit.hearts"
}
object(EnumLabel)#3 (1) {
["label"]=>
string(11) "suit.hearts"
}
Tried my hands with porting the ApiPlatform bridge over. Maybe it helps?
<?php
/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) Elao
*
* @author Elao <[email protected]>
*/
namespace App\ApiPlatform\Core\JsonSchema\Type;
use ApiPlatform\Core\JsonSchema\Schema;
use ApiPlatform\Core\JsonSchema\TypeFactoryInterface;
use Elao\Enum\ReadableEnumInterface;
use Symfony\Component\PropertyInfo\Type;
final class ElaoEnumType implements TypeFactoryInterface
{
/** @var TypeFactoryInterface */
private $decorated;
public function __construct(TypeFactoryInterface $decorated)
{
$this->decorated = $decorated;
}
/**
* {@inheritdoc}
*/
public function getType(Type $type, string $format = 'json', ?bool $readableLink = null, ?array $serializerContext = null, Schema $schema = null): array
{
if (!is_a($enum = $type->getClassName(), ReadableEnumInterface::class, true)) {
return $this->decorated->getType($type, $format, $readableLink, $serializerContext, $schema);
}
$ref = new \ReflectionEnum($type->getClassName());
$docType = '';
switch ($ref->getBackingType()) {
case 'string': $docType = 'string'; break;
case 'int': $docType = 'integer'; break;
case null:
default:
$docType = 'string';
break;
}
$schema = [];
$cases = $enum::cases();
if ($type->isNullable() && !$type->isCollection()) {
$cases[] = null;
}
$readableCases = [];
foreach ($cases as $case) {
//$readableCases[$case->value] = $case->getReadable();
$readableCases[$case->value] = $case->value . '=' . $case->name;
}
$enumSchema = [
'type' => $docType,
'enum' => $readableCases,
'example' => $cases[0]->value,
];
if ($type->isCollection()) {
$schema['type'] = 'array';
$schema['items'] = $enumSchema;
} else {
$schema = $enumSchema;
}
if ($type->isNullable()) {
$schema['nullable'] = true;
}
return $schema;
}
}
Hi @jensstalder . Thank you very much for looking at this.
Could you please explain me a bit more what would be the purpose of such a bridge now with native PHP enum?
I see the $readableCases
used as $enumSchema.enum
in your sample, but is that correct?
An API would rely on the backed value (as in the previous version of this library), not the readable one. But is there some places where the readable label could be relevant?
You might have more clues than me on what could be relevant to provide in such a bridge regarding the features that are specific to this lib v2 extending native enums capabilities. So, your help is appreciated :)
@ogizanagi Good question. The only benefit I see in relation to JsonSchema is that it shows up in the open API docs. My original goal was to extend the graphql implementation so that the types also get interpreted as enum. I have not found a solution for that yet. But thinking about it, it's true that API platform core should instead simply handle native enums better for both cases (JsonSchema and GraphQL), and it might not be necessary to do this within this package. But this might be a bit to opinionated from the view of the platform? As far as I can tell though, API platform currently only considers the backing type as if it were a simple string or int field? And Graphql endpoint hides the enum typed field completely (but that could be another issue).
For Doctrine there is one issue affecting this lib
- ENUMs cannot be used as IDs (I made a PR https://github.com/doctrine/orm/pull/9629 which hopefully will be merged soon)
Please add it on the list
EDIT: merged
@ogizanagi the https://github.com/doctrine/orm/pull/9629 has been merged in doctrine/orm 2.12.x , so you can remove the known issue from the list