Skip to content

[DependencyInjection] Allow using BackedEnum in Target attribute #46363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

ruudk
Copy link
Contributor

@ruudk ruudk commented May 16, 2022

Q A
Branch? 6.1 for features / 4.4, 5.4 or 6.0 for bug fixes
Bug fix? no
New feature? yes
Deprecations? no
License MIT
Doc PR symfony/symfony-docs#...

This adds support for using backed enums on the Target attribute.

In our code base, we want to do something like this:

enum GraphQLSchema: string {
  case PUBLIC = 'public';
  case INTERNAL = 'internal';
  // other schemas
}

public function __construct(
  #[Target(GraphQLSchema::PUBLIC)]
  private Schema $schema
) {}

Ideally, more attributes would support using enums but that's another discussion / PR.

@stof
Copy link
Member

stof commented May 16, 2022

to me, this looks like a use case for using constants rather than a BackedEnum. Using PHP enums makes sense when you typehint them, providing type safety. Here, you allow any enum case just to read its backing value, which voids all benefits of the enum.

@ruudk
Copy link
Contributor Author

ruudk commented May 16, 2022

@stof Thanks for your reply. In this case, using the enum only for this might not make sense. But in our system, we use the enum to typehint other attributes etc. For example:

#[ObjectType(
    schema: GraphQLSchema::PUBLIC,
    name: 'Movie',
)]
final class MovieType

The attribute's signature would look like this:

public function __construct(
        public readonly GraphlQLSchema $schema,
        public readonly string $name,
    ) {
    }

Now we get full type safety wherever we use the GraphQLSchema.

Since PHP 8.1 doesn't allow you to do this:

public function __construct(
  #[Target(GraphQLSchema::PUBLIC->value)]
  private Schema $schema
) {}

or this:

public function __construct(
  #[Target((string) GraphQLSchema::PUBLIC)]
  private Schema $schema
) {}

the only way to use the enum is to either get support in Target attribute, or to just have a separate constant instead.

I wish PHP would just automatically cast an BackedEnum (string) to a string when the function requires a string.

@korkoshko
Copy link

the only way to use the enum is to either get support in Target attribute, or to just have a separate constant instead.

Hmm, php since version 8.1 allows nested attributes, so you can do something with casting to a string:

#[SomeAttribute(new AsString(Test::A))]
class SomeClass {}

@ruudk
Copy link
Contributor Author

ruudk commented May 16, 2022

@korkoshko But that still won't work for the Target attribute as that only accepts string.

@korkoshko
Copy link

korkoshko commented May 17, 2022

@ruudk if you don't use a strict types declaration, php will auto cast the object to a string if it implements Stringable.

To fully support strict types, it might be better to add a union type string|Stringable to Target.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented May 17, 2022

This is really touching a deficiency of the language and enums in this case IMHO. They are really invading the codebase in a way that makes everything more complex. Like you said: "more attributes would support using enums but that's another discussion".

Fixing one "random" attribute doesn't make sense to me. I'd rather look for a more generic approach to them. eg string|Stringable is a bit more generic, but that'd require enums to have a __toString. I might be fine with forcing them all to have that method if they want such support. Also, the engine should provide a default __toString implementation for BackedEnum to me.

this looks like a use case for using constants rather than a BackedEnum

This might also be The generic solution I'm looking for. Enums being the new toy in school, we might be trying to use them when they don't fit - or where consts would fit better.

BTW: shouldn't BackedEnum::from(int|string) be turned into ::from(int|string|Stringable|static)?

/cc @Crell

@derrabus
Copy link
Member

but that'd require enums to have a __toString

… which is not allowed btw:

https://3v4l.org/m1cKu

Fatal error: Enum may not include __toString in /in/m1cKu on line 3

@derrabus
Copy link
Member

If we take this upstream to PHP core, I think we should try to make something like this possible:

#[MyAttribute(MyEnum::FOO->value)]

Right now, it is not: https://3v4l.org/NI78X

@ruudk
Copy link
Contributor Author

ruudk commented May 17, 2022

#[MyAttribute(MyEnum::FOO->value)]

This would be perfect.

Implementing Stringable + __toString on a string backed enum would be great too.

@Crell
Copy link
Contributor

Crell commented May 17, 2022

__toString was deliberately left off of enums because we weren't sure what the right behavior would be and wanted to see it in the wild for a while first. So running into issues like this is good, because it helps to see what the use cases are. Also, we aren't sure yet if it would cause issues with ADTs if/when those get implemented.

#[MyAttribute(MyEnum::FOO->value)]

This is currently blocked by -> not being allowed as part of a "constant expression." There's an open core issue about it:

php/php-src#8344

It sounds like no no one is against fixing that; someone just needs to do the work and file an RFC. That would be the best solution here, IMO.

@fabpot fabpot modified the milestones: 6.1, 6.2 May 20, 2022
@nicolas-grekas
Copy link
Member

nicolas-grekas commented May 20, 2022

So running into issues like this is good

Yes and no :)
Yes from the pov of the engine, as it forces feedback from userland and can help make the best decision.
No from the pov of eg Symfony, because the community wants to use enums no matter their limitations and we are all kinda confused about what is a work around a "by design" limitation vs what is legit design.

Take eg this PR. I'm not keen to accept it, and I'm not keen either with letting the community in a state where they cannot use enums before some hypothetical improvement in the future.

I'm not sure this process followed by enums is beneficial for the community, for this reason...
Eg what should we do here now?

@derrabus
Copy link
Member

@nicolas-grekas I think, we encountered a similar situation with attributes where the implementation that shipped with PHP 8.0 did not include nesting. We've discussed various workarounds back then and decided to wait for PHP to implement that missing feature properly.

If #[MyAttribute(MyEnum::FOO->value)] is shipped with PHP 8.2, I'd vote for rejecting this PR.

@Crell
Copy link
Contributor

Crell commented May 22, 2022

Best course of action here, I think, is to help get the core issue resolved for 8.2 and reject this PR, as fixing the core issue will make this irrelevant.

@fabpot
Copy link
Member

fabpot commented May 23, 2022

Closing the while we wait for 8.2 to improve.

@fabpot fabpot closed this May 23, 2022
@derrabus
Copy link
Member

@nicolas-grekas
Copy link
Member

This might be the improvement we're looking for: php/php-src#8825

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants