Problem/Motivation
Enumerations were added to PHP in 8.1.
Whilst they can't be added to Drupal 9 core which still still supports down to PHP 7.3, custom/contrib code being developed on branches that support PHP >= 8.1 or Drupal >=10.0 may wish to take advantage of this language feature.
It is not currently mentioned in the Drupal Coding Standards, but it would be good to add consistency through standardization, for example to enforce a common casing for the names of enums and the values within them.
The proposed option is to use UpperCamelCase (PascalCase), the alternative is to adopt UPPER_SNAKE_CASE.
They are illustrated below with code from core issue #3249599: Add support for PHP 8.1 Backed Enums in Select, Checkboxes, Radio elements and distributions_recipes project, both of which intend to introduce enums to core.
UPPER_SNAKE_CASE
enum FormTestIntEnum: int {
case NONE = 0;
case FEW = 3;
case MANY = 12;
case ALMOST_ALL = 99999;
}
.
'int_enum' => FormTestIntEnum::ALMOST_ALL->name,
.
UpperCamelCase (PascalCase)
enum FormTestIntEnum: int {
case None = 0;
case Few = 3;
case Many = 12;
case AlmostAll = 99999;
}
.
'int_enum' => FormTestIntEnum::AlmostAll->name,
.
References
https://www.php.net/manual/en/language.enumerations.backed.php
https://github.com/php-fig/per-coding-style/blob/master/spec.md#9-enumer...
https://symfony.com/doc/current/contributing/code/standards.html
Benefits
If we adopted this change, the Drupal Project would benefit by having a consistent standard for a useful language feature, consistent with our existing standards around classes, and allowing to easily distinguish enum case values (UpperCamelCase) from constants (which continue to use UPPER_SNAKE_CASE).
Three supporters required
- https://www.drupal.org/u/catch 2024-04-10
- https://www.drupal.org/u/borisson_ 2024-04-10
- https://www.drupal.org/u/Kingdutch (2024-04-10)
- https://www.drupal.org/u/kimpepper (2024-04-11)
Proposed resolution
The proposed resolution is to adopt UpperCamelCase for enums, both the name of the enum class and the case values.
Although the opinions between UpperCamelCase (or "PascalCase") and UPPER_SNAKE_CASE are divided, both Symfony and PER (the follow-up of PSR-12) have chosen for UpperCamelCase.
Additionally Dale42 outlined good reasons why Enums should be distinguished from constants in #17 (quoted below)
Enumerations are not constants, they are classes
The idea behind making constants upper case, as I understand it, is making them easily distinguishable from variables. A enumeration::case is not a constant, it is it's own unique thing.
For example, a backed enumeration has this feature: Enum::Case->value
Also:
Enumerations can contain constants
Borrowing from the example on the PHP documentation page, https://www.php.net/manual/en/language.enumerations.constants.php, but changing the constant to upper case:
enum Size {
case Small;
case Medium;
case Large;public const HUGE = Size::Large;
}
Size::HUGE is easily identified as a constant, vs Size::Large as a case.If the cases are also upper case, the comparison would be Size::HUGE vs Size::LARGE.
Mixed case is more readable
A reference: https://en.wikipedia.org/wiki/All_caps
These standards are about making the code more readable, Pascal case does that.
Following Symfony and PER ensures we’re consistent with the rest of the PHP ecosystem and we don’t end up with mixed cases for enums of different origins (Drupal vs Symfony). Additionally from Coder/PHPCS’s perspective it may allow us to re-use existing coding standards rules for enforcing UpperCamelCase of enums rather than having to write and maintain our own rules.
1. https://www.drupal.org/docs/develop/standards/php/php-coding-standards
Current text
None
Proposed text
To follow "Naming Conventions > Classes"
Enums follow the same conventions as classes with the addition that for their cases the enums must use UpperCamelCase.
enum Exists { case ErrorIfExists; case ErrorIfNotExists; case ReturnEarlyIfExists; case ReturnEarlyIfNotExists; // Do stuff. }
Remaining tasks
Create this issue in the Coding Standards queue, using the defined templateAdd supportersCreate a Change RecordReview by the Coding Standards CommitteeCoding Standards Committee takes action as requiredDiscussed by the Core Committer Committee, if it impacts Drupal CoreFinal review by Coding Standards CommitteeDocumentation updates- Edit all pages
- Publish change record
- Remove 'Needs documentation edits' tag
If applicable, create follow-up issues for PHPCS rules/sniffs changes
Comments
Comment #2
eli-tThe versions of PSR-12 and PER Coding Style published on https://www.php-fig.org do not currently mention enums.
However the master branch of the PER Coding Style in github at the time of writing this comment currently says
Comment #3
drunken monkeyI would prefer UPPER_CASE for constants, to be consistent with constants everywhere else
For the same reason I’d call it “UpperCamel naming” instead of “PascalCase capitalization”.
But otherwise I’d just vote for including this in our standards as-is.
Comment #4
gabesullice+1 to UpperCamel casing for enum names.
Comment #5
mondrakeMy 0.02: +1 for #2, PascalCase for both enums and constants. STOP_SHOUTING_CONST_IDENTIFIERS...
Comment #6
fathershawnAnother +1 for UpperCamelCase for Enum cases. I see the similarity to Constants, but these are also very much like how we have used associative arrays as ad hoc enums for value maps.
Comment #7
fenstratAnother +1 for UpperCamelCase for Enum cases. PHP docs also use it: https://www.php.net/manual/en/language.enumerations.basics.php
Comment #8
larowlanI don't have a preference either way, as long as we have a rule and its consistent
Now we're starting to see patches for core, I think we should look to address this. Bumping priority as a result.
Adding related issue that adds enums for core.
Comment #9
mstrelan commentedAlso not fussed either way but I found https://stitcher.io/blog/php-enum-style-guide which has some thoughts. TL;DR the author is in favour of UPPER_CASE enums.
Comment #10
acbramley commentedI'm in agreement with the article in #9 - upper case feels and looks more natural to me.
Comment #11
fenstrat@mstrelan just pointed out that Symfony also use UpperCamelCase https://symfony.com/doc/current/reference/forms/types/enum.html
Comment #12
mstrelan commentedSymfony seems to be using UpperCamel, see https://symfony.com/doc/current/reference/forms/types/enum.html for example.
Comment #13
larowlanDistributions and recipes are using upper case https://git.drupalcode.org/project/distributions_recipes/-/raw/patch/rec...
Comment #14
alex.skrypnykFrom the point of using enums, the uppercase looks more consistent with constants. PascalCase looks more consistent with class names.
I'm for the uppercase.
Comment #15
catchThis could use the examples and a comparison in the issue summary.
Comment #16
eli-tAdded examples to issue summary.
Comment #17
dale42I believe Enumeration cases should be Pascal case for the following reasons:
Enumerations are not constants, they are classes
The idea behind making constants upper case, as I understand it, is making them easily distinguishable from variables. A enumeration::case is not a constant, it is it's own unique thing.
For example, a backed enumeration has this feature:
Enum::Case->valueAlso:
Enumerations can contain constants
Borrowing from the example on the PHP documentation page, https://www.php.net/manual/en/language.enumerations.constants.php, but changing the constant to upper case:
Size::HUGEis easily identified as a constant, vsSize::Largeas a case.If the cases are also upper case, the comparison would be
Size::HUGEvsSize::LARGE.Mixed case is more readable
A reference: https://en.wikipedia.org/wiki/All_caps
These standards are about making the code more readable, Pascal case does that.
Comment #18
kim.pepperLinking to a few enum issues. We might want to decide on the coding style first?
Comment #19
kingdutchI used to be for UPPER_CASE because in a lot of places we're going to replace constants with enums. However, I think #17 lays out an excellent case for why enums are not constants and why, from a differentiation and readability point of view, PascalCase and following Symfony (see #11 & #12) and the PER coding style (see #2) is the right answer.
As an added bonus that means PHPCS won't shout at you if you move between Symfony or Drupal because you forget which uses which when contributing. It also prevents us from having things like the following in our codebases:
I hope no one wants the above in their codebase ^^'
+1 for PascalCase (or UpperCamelCase as some call it)
Comment #20
kim.pepper+1 for PascalCase too
Comment #21
kingdutchUpdated the issue summary with the proposed resolution which aligns Drupal with the rest of the PHP ecosystem (PascalCase).
Comment #22
acbramley commentedI think it's pretty clear Pascal is the way to go based on the new IS/comments in 17, as per slack I'm going to RTBC this as we're starting to get lots of Enum issues for core.
Comment #23
acbramley commentedJumped the gun, we need the proposed docs changes I believe
Comment #24
joachim commentedFixed links in summary.
Comment #25
kingdutchHere's a first attempt at a proposed documentation change.
I've used similar language as the PER standard which says 'The term "class" refers to all classes, interfaces, traits, and enums.'. I think treating those all the same is a good idea because even the PHP manual says "Enumerations are a restricting layer on top of classes and class constants, intended to provide a way to define a closed set of possible values for a type.".
Comment #26
acbramley commentedI think the proposed docs read well, nice work
Comment #27
kristiaanvandeneyndeJust a heads up: We already have CacheTagOperation in core which is camelCase, not PascalCase, though.
It's only related to tests so can we still change that?
Comment #28
catchYeah we can change that in tests, I think it's only in 10.3.x/11.x too? If so it technically won't be an API change as long as we do it before beta/rc.
Comment #29
kristiaanvandeneyndeYeah, we only added that to 10.3.x/11.x as part of the many improvements to StandardPerformanceTest and the like.
Comment #30
quietone commentedWorking to use the updated issue summary
Comment #31
quietone commentedComment #32
quietone commentedI can't think of another occurrence of PascalCase in our coding standards. So, we should use UpperCamelCase.
Where on the page should the new section be added? What section is this suggesting it be before or after?
Comment #33
quietone commentedComment #34
kingdutchI think since Enum's are very class-like, this makes the most sense directly after Classes: https://www.drupal.org/docs/develop/standards/php/php-coding-standards#s...
Comment #35
catchAdded myself as a supporter.
Comment #36
borisson_Added as a supporter
Comment #37
kingdutchAdded myself as a supporter
Comment #38
kingdutchRemoved the returnEarly function from the documentation proposal since it distracted from what the docs are focusing on which is the naming convention of the cases.
Comment #39
kim.pepperEven though we have 3 already, add myself as a supporter too.
Comment #40
quietone commentedThe issue summary tasks need to be updated.
The CR needs some work to. The title is well, wrong. We are not adopting the PER Coding Style 2.0 for enums. That should change to just state that we have a standard for enum. I also think that if someone skims the CR the last sentence will give them the wrong idea. I am also not convinced we need to inform readers when Enumerations were added to PHP or that our standard shares something in common with other standards. The latter is a concern in that this may set a pattern that future changes are to documented against other standards.
Can it be as simple as this?
Can someone check if Coder has anything for enumerations?
Comment #41
kim.pepperI couldn't find anything for enums in Drupal Coder or existing rules about enum case in https://github.com/slevomat/coding-standard or https://github.com/squizlabs/PHP_CodeSniffer
Comment #42
dwwAgreed with @quietone that we should use "UpperCamelCase" not "PascalCase" in our standards docs. Also took the liberty of removing two "should"'s in favor of a single "must" in the proposed standards text:
Also, edited the CR per the concerns in #40:
https://www.drupal.org/node/3439920/revisions/view/13524321/13525482
Updating remaining tasks. The CS committee was in broad agreement. Also, since there are already a few enums in core, and we really need to standardize before 10.3.0 ships, we want to "fast-track" this change. Tagging for needing announcement (although maybe we're going to jump over that formality in this case). 😂
Comment #43
dww#3440087: Update CacheTagOperation enum to use UpperCamelCase is there with an MR if we want to move quickly on that before the standard is formally adopted.
Comment #44
kristiaanvandeneyndeI was with larowlan in #8, meaning I didn't care either way as long as it was clear and consistent. After reading up on #17 I can't but agree that PascalCase seems like the only sensible way forward.
Not seeing much value in adding even more names to the IS so I'll leave that as is.
Comment #45
catchCommitted #3440087: Update CacheTagOperation enum to use UpperCamelCase so that (I think?) core is now compliant with the proposed standard in advance.
Comment #46
alex.skrypnykA tiny nit on the wording: we use
UpperCamelas a name on the classes page rather thanUpperCamelCase. Can we use the same in the proposed standard description for this issue?Proposed currently
Enums follow the same conventions as classes with the addition that for their cases the enums must use UpperCamelCase.After update
Enums follow the same conventions as classes with the addition that for their cases the enums must use UpperCamel.Comment #47
dwwI think including "Case" is helpful for understanding. The CR title seems more weird as "Use UpperCamel for enum and enum case values".
How about friendly amendment, we standardize on "UpperCamelCase" and update the one other reference to it as part of fixing this?
Comment #48
dwwLooking more closely https://www.drupal.org/node/608152#naming uses:
Ugh to #3. That should either be "UpperCamel", or we do:
I don't want to slow this issue down, we can clean all this up later. But I believe we can tidy as we go like this. It's only the wording of the docs, not the underlying standard they describe.
Cheers,
-Derek
Comment #49
dwwMeanwhile, trimming a lot of noise out of the summary here.
Comment #50
alex.skrypnyk+1 for #48 updating names! thank you Derek
Comment #51
quietone commentedAdded the point from #48 to a comment on the issue for the next meeting.
Comment #52
longwaveI support the current proposal. I also opened #3443118: Enum cases may need to change to UpperCamelCase to inform the distributions and recipes initiative of this likely-upcoming standard.
Comment #53
quietone commentedComment #54
xjmI see cases for both casing of the cases 😉 which means that following existing external standards is the right call. IMO the only reason we might not do so immediately is if it's inconsistent with other core standards (I don't see a conflict) or disruptive to core.
Here are the places
enumis used in core already (outside of the Doctrine fork):I checked and all of them use
UpperCamel(following #3443118: Enum cases may need to change to UpperCamelCase which fixed the configenum).So +1 from me also.
Comment #55
quietone commentedUpdate steps per #3456119: Coding Standards Meeting Tuesday 2024-06-18 2100 UTC
Comment #56
quietone commentedI updated the doc page, step 8.1. It would be best if someone checked that before the CR is published.
Comment #57
acbramley commented@quietone I checked the documentation and made a minor change to add indentation to the enum example. Otherwise looks good to me!
Comment #58
quietone commented@acbramley, thanks for checking!
I changed to the file Exists.php to include some errors, wrong case and extra spaces, and ran commit-code-check. None of the problems were identified.
So, this does need an issue in coder.
Comment #59
quietone commentedIn the recent meeting mstrlen mentioned the following sniffs
With those enabled the following passes. There are two spaces after 'case' in the first line.
And this fails. A space is needed before the comment line.
To need an extra line in one case and not the other is unexpected. So, I think there is more work needed on the style of enums.
I am thinking we should define the properties for SlevomatCodingStandard.Classes.EnumCaseSpacing. They are
minLinesCountBeforeWithComment: minimum number of lines before enum case with a documentation comment or attribute
maxLinesCountBeforeWithComment: maximum number of lines before enum case with a documentation comment or attribute
minLinesCountBeforeWithoutComment: minimum number of lines before enum case without a documentation comment or attribute
maxLinesCountBeforeWithoutComment: maximum number of lines before enum case without a documentation comment or attribute
The example does not include a ": type-string" either. The other sniff deals with that.
Comment #60
quietone commentedEnforcing what we have agreed to can be done with
SlevomatCodingStandard.Classes.BackedEnumTypeSpacing. And a core issue is needed to enable it.Another issue is needed to decide about comments for each case and the use of
SlevomatCodingStandard.Classes.EnumCaseSpacing.Comment #61
quietone commentedCreated #3490054: Comment style for Enum and for core, #3490056: Enable SlevomatCodingStandard.Classes.BackedEnumTypeSpacing.
And a reminder that this was announced in https://www.drupal.org/about/core/blog/coding-standards-proposals-for-fi....
So, I think this is now complete.
Thanks everyone!
Comment #62
dwwThanks for cleaning this up and moving it along!
I just RTBC'ed #3490056: Enable SlevomatCodingStandard.Classes.BackedEnumTypeSpacing. I know we're still discussing #3490054: Comment style for Enum. However, neither of the proposed sniffs seems to check for the main thing we debated here: using UpperCamelCase for the case names. I'm not sure the best way to enforce that. Seems we need another followup, somewhere, but I don't know where. 😅
Comment #63
kristiaanvandeneyndeWas the "needs work" intentional or a mistake? Is it because you think we need another follow-up?
Comment #64
dwwIndeed, intentional since we need to decide where to follow up to enforce this part of the standard.
Comment #65
kristiaanvandeneyndeOkay so would it make sense to mark this one as fixed again as it was about deciding a style? And then open a follow-up about enforcing it? Or is there a chance we need to enforce it in e.g. gitlab_templates and we can't open up a follow-up yet before we know where to open it?
Comment #66
dwwI'm following the protocols that we use for the coding standards committee (of which I am a member). Even though this is indeed a decided policy, we're not supposed to mark this 'fixed' until all the follow-ups exist.
I don't have time for a deep-dive on this, so for now, doing the quick/easy thing of opening an issue in coder:
#3492126: Enforce UpperCamelCase case values in PHP enum definitions
We can decide there if that's really the best way to enforce this, and if not, move the issue to the core queue and rescope it to enable some existing sniff in the wild or whatever.
Cheers,
-Derek
Comment #67
kristiaanvandeneyndeAll right, makes total sense. Thanks!
Comment #69
klausiThe enum case standard was implemented in Coder in #3492126: Enforce UpperCamelCase case values in PHP enum definitions.
The enum name itself Coder checking is planned in #3497433: Forbid upper case acronyms in class, enum, interface, trait names. This will also cover traits and will throw errors on all upper case acronyms (2 bad names in core, class SSH and class FTP)
Comment #70
klausifixing issue link
Comment #71
xjmAmending attribution.