Skip to content
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

Using Enum object as array key? #150

Open
TonyGravagno opened this issue Jul 29, 2021 · 1 comment
Open

Using Enum object as array key? #150

TonyGravagno opened this issue Jul 29, 2021 · 1 comment

Comments

@TonyGravagno
Copy link

TonyGravagno commented Jul 29, 2021

Consider:

final class State extends Enum
{
   public const Activated = 'Activated'; ...
}

...
$obj->state[State::Activated] = true; 

Is this a mis-use of this mechanism? Should that be [State::Activated->value()] or even [State::Activated.''] ? With more housekeeping, the reason for using an Enum is diminished. Rather, I'm thinking that in languages that support enumerations this would present similar problems, so constants are preferred. In this case that means simply removing the extends Enum from any class that serves as a container for pure constants, where enums aren't really required.

Someone might ask why I'm using Enums rather than constants. It's for this technique to help eliminate bugs:

function get(State $settingID)
{
    switch ($settingID) {
       case State::Activated:
         return $this->state[State::Activated];
     ...
}

A development-time error is evident when calling get() without a valid spec. This is better than a production-time error (even switch/default) that might occur with an invalid constant value or even a string passed in. Unfortunately using Enum as above means the requests to get need to be:

$activated = $foo->get( State::Activated() ); // instance of Enum class

I don't mind that. The problem is that by "simply removing the extends Enum" as noted above, this also means all instantiation with parentheses State::Activated() need to be modified in the application as well. So much for "simple".

Yes, the get method can also do this:

function get($settingID)
{
    if($settingID instanceof State)
       $settingID = $settingID->value();
    else
       // string value, contant? literal? not as reliable

    switch ($settingID) {

We need to make decisions about how to handle all tools at our disposal. I'm not looking for some magic bullet, just guidance about we can and should use this Enum class. I suspect using Enum as an array key will break in PHP v8, so it's probably not a good idea to use it like this anyway. But the get( State::Activated() ) syntax is valid in any PHP version, so I'm looking for best short-term and long-term options.

The reason I'm asking about this is that it seems a shame to have an either/or choice, that it would be cool if we could use an Enum as an array key without the Enum actually being the key, but by default using the string value as the key in that specific context. So, I'm just documenting thoughts on this, for my immediate needs and in case someone else has similar thoughts.

Sincere thanks for this package. I've just started to use it and it's been very helpful ... when I don't abuse it 😉 .

@drealecs
Copy link
Contributor

drealecs commented Jul 30, 2021

Hey @TonyGravagno, let me mention that native enums will arrive in PHP 8.1, something you might have already knew (https://wiki.php.net/rfc/enumerations).
May I point that the recommended pattern for using this library is with private constants:

final class State extends Enum
{
   private const Activated = 'Activated';
...
}

And because of this, the usage will be:

$obj->state[State::Activated()->getValue()] = true;

When moving to native enums it will change to:

enum State: string
{
    case Activated = 'Activated';
...
}

with the usage:

$obj->state[State::Activated->value] = true;

Also this RFC was discussed: https://wiki.php.net/rfc/object_keys_in_arrays but so far it doesn't seem to be a trivial decision and might have to wait for PHP 9.0 and not PHP 8.2

Regarding the usage, what I usually recommend is to use the enum as an object mostly everywhere adding it as a type in parameters/properties and returns.
As soon as you need it, just create an instance, either statically with State::Activated() or dynamically with State::from($receivedValue) where $receivedValue comes from a request, a database or whatever.
Having the constants defined private will put a pressure not to get to the backed value faster than needed.
Taking the value from the enum should be only when you need to work with it as an string/int, that's storing in the database, sending it in a request, yeah and also using it as an array key as currently that's not something we can do in PHP.
Also your example with get should look like this in my view if I'm not missing something:

function get(State $settingID)
{
    return $this->state[$settingID->getValue()];
...
}

Yes, I also find places where enums are abused. If the enum doesn't represent a type with a limited number of clear cases, it should not be an enum.

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

No branches or pull requests

2 participants