Problem/Motivation

There is currently no standard for ordering use statements.

Benefits

Having a defined order not only makes the code more readable, it also reduces the potential for clashes when two patches/MRs are changing the import/use lines.

Three supporters required

  1. https://www.drupal.org/u/jonathan1055 (8 Nov 2023)
  2. https://www.drupal.org/u/kimpepper (8 Apr 2024)
  3. https://www.drupal.org/u/smustgrave (April 7th, 2024)
  4. https://www.drupal.org/u/dww (April 7th, 2024)

Proposed changes

Provide all proposed changes to the Drupal Coding standards. Give a link to each section that will be changed, and show the current text and proposed text as in the following layout:

1. "use"-ing classes

Current text

If there are multiple use declarations in a file, we do not currently have a standard about what order they should be in. However, consider code readability and do something sensible, especially if there are many use declarations.

Proposed text
  • use statements must be located after the initial @file phpDoc block (if any) and after the namespace declaration (if any).
  • use statements must be sorted alphabetically, in a case-sensitive order which places all uppercase letters before the lowercase letters. Note also that backslash \ precedes underscore _ . Here is an example:
    use Drupal\Core\ControllerBase;
    use Drupal\Door\DoorBase;
    use Drupal\car\CarInterface;
    use Drupal\door\DoorInterface;
    use Drupal\door_handle\HandleInterface;
    

2. Configuring PHPStorm

(Not the standards, but a related doc change to be compassionate towards IDE users)

Add something about how to get case sensitive use sorting along the lines of comment #48.

@todo Decide if we want case-sensitive or case-insensitive alphabetical sorting.

Add something about configuring PHPStorm to use PHP_CodeSniffer with Drupal’s coding standards. Describe how PHP_CodeSniffer errors appear in the IDE.

Remaining tasks

  1. Create this issue in the Coding Standards queue, using the defined template
  2. Add supporters
  3. Create a Change Record
  4. Review by the Coding Standards Committee
  5. Coding Standards Committee takes action as required
  6. Tagged with 'Needs documentation edits' if Core is not affected
  7. Discussed by the Core Committer Committee, if it impacts Drupal Core
  8. Documentation updates
    1. Edit all pages
    2. Publish change record
    3. Remove 'Needs documentation edits' tag
  9. Coder issue #3470716: Configure use-statement sniff to be case sensitive has already fixed case-sensitive sorting.

For a fuller explanation of these steps see the Coding Standards project page

Comments

aspilicious’s picture

Title: Order the use statement in a sane way » Order the use statements in a sane way
Anonymous’s picture

Issue tags: +Coding standards

It would be good to determine what comes first, whether purely alphabetically or Symfony first, Drupal second (or vice versa), or something else.

Crell’s picture

My knee-jerk suggestion is "external" classes first, alphabetically, and Drupal-namespaced classes last, alphabetically, with a whitespace break between them. Or possibly between each "set" of namespaces. (So if we pull in some Zend code, for instance, it would be all Symfony classes, break, all Zend classes, break, all Drupal classes.)

That's just a knee-jerk suggestion, though, so feel free to disagree. :-)

jhodgdon’s picture

There is always resistance to adding coding standards that everyone needs to follow... Is this one really necessary? How often do we even have more than just a couple of "use" statements (I'm assuming that if we have only 2-3 of them, the order isn't really important)?

Crell’s picture

My totally unscientific memory of classes in the kernel work to date suggests that 5-10 use statements per file is not at all unusual. At that point I think having some minimal level of organization is useful, although I totally agree that it probably does not justify a strict rule. Perhaps if we just had clustering, eg, list all Symfony classes first, linebreak, then Drupal classes? That is effectively what I've been doing anyway in any of the classes I've touched.

(There zero runtime impact here so it's a purely human usability / aesthetic question.)

aspilicious’s picture

Most newer patches don't use a space but order them.
So can we just say we try to order them with ot without a space in the future? :)

jhodgdon’s picture

Do we really need a standard for this? I don't think we have a standard for the order of properties in a render API array and stuff like that.. the assumption is that people should do it in a logical and readable manner.

aspilicious’s picture

I can live with no standard...

Crell’s picture

I am OK with postponing this thread until the issue does come up in another thread. Then we can reopen this one. :-)

ZenDoodles’s picture

Status: Active » Postponed
sun’s picture

Title: Order the use statements in a sane way » [policy] Coding standards for "use" statements
Component: other » documentation
Status: Postponed » Active
Issue tags: +PSR-0

Unpostponing since I filed #1791928: [policy] Coding standards for "use" statements yesterday...

I've copied the issue summary / concrete proposal from over there.

sun’s picture

Issue summary: View changes

Imported summary from http://drupal.org/node/1791928

lars toomre’s picture

As I asked in the duplicate thread, what is the suggested sane ordering method? Alphabetical?

yesct’s picture

I think we are seeing longer lists of use statements and people are starting to add new use's in alphabetical order.

jhodgdon’s picture

Do we need a standard though? The more standards we have, the more we either have (a) a lot of Core code that violates them or (b) a lot of patches being held up on standards. Neither of these outcomes is optimal, so we need to think hard about whether a standard is really necessary before we adopt one.

In this case, my personal view is that a standard saying that all use statements need to be alphabetical is not necessary, and that adding another standard that most of Core does not follow is not a good idea.

I guess I'm sounding like a broken record. I have asked this same question three times now about whether having a standard at all is necessary. So far, no one has really answered that question.

Crell’s picture

I don't think a strict standard is necessary. At best a "recommended but not enforced". We have enough nitpicks to block patches on without adding another one that doesn't do anything. :-)

yesct’s picture

@jhodgdon I think no one is answering because we agree? :)

claudiu.cristea’s picture

The most used seems to be:

Drupal\Core\*
Drupal\*\*  <= modules
Drupal\Component\*
Symfony\* 

Each wildcard in alphabetical order.

jhodgdon’s picture

Status: Active » Closed (won't fix)

I am going to just close this as "won't fix". There doesn't seem to be any momentum towards making this a standard that we want to enforce. If individual files have completely random ordering, we can clean those up in the general interest of code readability (which is a guideline we have in Drupal and in fact in any code), without having a standard that says we must.

If you disagree strongly and can recruit more people who also do, please reopen this issue, but I just haven't seen much interest here.

jhodgdon’s picture

Issue summary: View changes

Updated issue summary.

catch’s picture

Issue summary: View changes
Status: Closed (won't fix) » Active
jhodgdon’s picture

I added a note to https://www.drupal.org/node/1353118/revisions/view/8970343/9251956 about this. That seems to be the only place in our coding standards docs pages that talks about "use" statements.

If that's OK, maybe change this issue back ot "won't fix", since we didn't make a standard?

mpdonadio’s picture

I think something more concrete would prevent back-and-forths w/ patch reviews, and precise language is better in specs (esp w/ non-English speakers). What may be sensible to one person may not be sensible to someone else. I vote to use language like "alphabetical unless there is a very compelling reason for a different grouping".

jhodgdon’s picture

Project: Drupal core » Coding Standards
Version: 8.0.x-dev »
Component: documentation » Coding Standards

Sure, in an ideal world, but please read the entire thread here. There wasn't general agreement that we needed a standard, especially as Core would be in violation of it immediately.

I guess what we should do is leave this open and move it to the coding standards issue queue. Meanwhile, I think the standards page is correct when it says we do not have a standard.

dawehner’s picture

These days I think given that so many people use phpstorm their order is kind of implicit deterministic, because it resorts them automatically, I think.

gogowitsch’s picture

@dawehner You are 100 % right: When I develop code for Drupal, I trust PhpStorm to know most of the Drupal coding standards. I press the shortcut for automatic code formatting all the time.

PhpStorm will re-order use statements to be alphabetically. This implicitly keeps the vendors grouped. The order of vendors is just alphabetically.

It is not just aesthetics: alphabetical is good to slightly reduce the probability of Git conflicts, too.

Personally, I don't think we need a standard, but if the community implements one, I opt for keeping it compatible with tools such as PhpStorm.

cburschka’s picture

The lack of this standard is a frequent problem when editing files with unsorted use blocks. The IDE will often automatically sort the statements, which then has to be manually reverted to avoid polluting the patch with unrelated changes. Requiring use statements to be alphabetized would make collaboration much easier.

A phpcs/phpcbf rule in the coder module could easily fix core once such a standard is adopted.

cburschka’s picture

jonathan1055’s picture

@cburschka I have forked your github branch and tested the new sniff. Nice, it all seems to work as expected. I tested it on Rules where there were 86 changes over 67 files, using the automatic fixer. Given the comments in #23, #24, #25 I think there is an appetite for sorting the use statements, purely alphabetical, with no blank lines, just as your new sniff does. Are you going to raise an issue in Coder to propose this? Then we can create a PR from your repo, and add the test cases that I am sure that Klausi will want.

darvanen’s picture

We have standards for everything else from how spaces should precede parameter descriptions to the requirement for a period on every comment. I see no reason *not* to set a standard on this issue so that there is something to point to to end fruitless discussions about unnecessary changes in patches/MRs vs the effort required to undo those unnecessary changes.

joachim’s picture

I don't understand what this means in the proposal:

> (FullyQualifiedClassName == framework/vendor namespace)

darvanen’s picture

Yeah... neither do I.

Suggest a change to:

use statements SHOULD be in alphabetical order.

This seems to be the general consensus here.

jonathan1055’s picture

As there is some consensus here that a rule would be useful I have created #3310013: Add sniff to check and fix the order of Use statements

Thanks to @cburschka for the initial work. Would you like to contribute on the coder issue?

klausi’s picture

Priority: Normal » Major

Alphabetical sorting has now been implemented in Coder and will be released soon. Escalating priority here to finalize the written coding standard.

darvanen’s picture

If there's a cs rule now does that mean it's upgraded to MUST?

use statements MUST be in alphabetical order

klausi’s picture

Yes, there is now a Coder rule that enforces it as a MUST, since Coder can only throw an error or not.

darvanen’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update

Since that's passing now I've updated the IS.

darvanen’s picture

Status: Active » Needs review
jonathan1055’s picture

Status: Needs review » Reviewed & tested by the community

Can this now be marked as Fixed? Coder has the sniff, Coder 8.3.19 was release 9 June, and docs page is updated.

jweowu’s picture

A case-insensitive sort is unstable in principle (even if that is unlikely to ever be a factor in lists of Use statements in practice), and so it's an inferior option. This really should be case-sensitive, as that is the sane way to sort lines of case-sensitive code!

The case-insensitive approach appears to have been chosen because "that's what PHP Storm does", which isn't a great reason to pick a worse option (but I understand that it was an easy way to obtain a mostly-good result, so I get it).

However... iff there's a way to tell PHP Storm to do this sensibly via a config file (ala .editorconfig), I'd be very much in favour of doing that and switching the standard to be case-sensitive. Then Drupal can ship that config file, PHP Storm will still/automatically match the revised standard, and the standard will better align with normal expectations for sorting in code (along with, I suspect, the sorting routines of most if not all other IDEs and text editors).

catch’s picture

Status: Reviewed & tested by the community » Needs work

This should be a case sensitive sort so that it's deterministic.

The coding standards change was never officially approved via the coding standards process, which is understandable since that was dormant for about four years, but now that it's up and running again we should try to do it properly.

Marking needs work for:

1. Needs an updated issue summary following the template.
2. We should probably revert and then re-add the documentation changes.
3. If the coder rule is doing case insensitive sort, then we might need an issue to make it case sensititive
4. We should have a core issue to apply the new rule.

andypost’s picture

Would be great to mention in standard usage of PHP functions

jonathan1055’s picture

Before we go any further, responding to #38, do we need to find out if there is a way to tell PHPStorm to order with a case-sensitive sort? If there is not, then is that a breaker? Do we still push ahead with requiring a case-sensitive sort, even if popular IDEs will re-order and mess it up?

Replying to #39.3

if the coder rule is doing case insensitive sort, then we might need an issue to make it case sensititive

In #3310013: Add sniff to check and fix the order of Use statements the sniff implemented is the external SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses which currently only does a case-insensitive order. So we could raise a SlevomatCodingStandard issue to add a config option to allow case-sensitive or insensitive sorting.

catch’s picture

Status: Needs work » Needs review

Actually I kind of wonder whether we're ever going to notice the difference in practice between case sensitive and case insensitive sort, I can't think of a single case with core or contrib (or vendor) code where this would actually come up.

Given that, would it actually matter? Moving back to needs review.

jweowu’s picture

My thought in #38 was that as the "case insensitive" decision appears to have been based on PHPStorm's default (but fairly bizarre) behaviour, we should change it if there's a way to tell PHPStorm via some config file in the Drupal repository to use case-sensitive sorting.

Without such a config file to automate the change for PHPStorm users they would presumably need to make a config change manually, in which case users will still encounter inconsistencies in this area -- just not the same users as before (I imagine it would be an improvement for pretty much everything other than PHPStorm).

Overall, case-sensitive is clearly correct, so in principle we should be using that, but I agree that the case-insensitive sorting of 'use' statements is highly unlikely to cause instability in practice, so it might just come down to "which alternative annoys the fewest users?"

(And on that note, I'm only guessing that PHPStorm is pretty much on its own in defaulting to case-insensitive sorting behaviour. I'm honestly just surprised that even one editor is doing that, and so I'm assuming it's probably only one, but I don't know that for sure.)

quietone’s picture

Priority: Major » Normal

When I sort lines in PHPStorm they are sorted case sensitive. If this is a setting somewhere I do not recall setting it nor have I found it.

This sniff is enabled for commerce_migrate and I could not use PHPStorm to sort the lines to pass phpcs.

Am I the only one using PHPStorm that has this behavior?

quietone’s picture

Title: [policy] Coding standards for "use" statements » Coding standards for "use" statements
Issue summary: View changes
Issue tags: -Coding standards, -kernel-followup

The Coding Standards Committee has developed a custom issue template to assist in managing issues efficiently. We have already found that it's use is helping. For issues created before the template was in use I am adding the new template to the Issue Summary and asking everyone here to help convert to it..

Thank you for your help!

quietone’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

Should be needs work for the issue summary update.

jweowu’s picture

When I sort lines in PHPStorm they are sorted case sensitive. If this is a setting somewhere I do not recall setting it nor have I found it. This sniff is enabled for commerce_migrate and I could not use PHPStorm to sort the lines to pass phpcs. Am I the only one using PHPStorm that has this behavior?

Looking back at #3310013: Add sniff to check and fix the order of Use statements I think it was https://www.drupal.org/project/coder/issues/3310013#comment-15101090 which had informed my comments here. I presume that will explain what you're seeing.

jonathan1055’s picture

Issue summary: View changes

Excellent. So that answers my first question in #41. PHPStorm will sort using proper case-sensitive when you use "Code > Optimize Imports" as per #3310013-30: Add sniff to check and fix the order of Use statements

So we can proceed by requiring the Drupal standard to also use a case-sensitive sort.

We will need to check if the existing sniff has any config option for case sensitive/insensitive, and if not then raise a ticket on SlevomatCodingStandard. But that should not hold up progress on getting the standard agreed.

Updated the issue summary with first draft, please add your support and suggest improvements to the text.

kim.pepper’s picture

Issue summary: View changes

+1 from me.

smustgrave’s picture

Issue summary: View changes

+1 for me.

acbramley’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update

Create a CR here https://www.drupal.org/node/3439320 (not sure what the versions should be)

The IS has been updated since #46

This should be ready for review.

kim.pepper’s picture

SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses defaults to case-insensitive but looks like we can configure that to be case-sensitive.

https://github.com/slevomat/coding-standard/blob/master/SlevomatCodingSt...

dww’s picture

Issue summary: View changes

Case sensitive sorting sounds good to me, too. Adding myself as another supporter. Other minor edits to the summary since we don't SHOUT in our standards anymore.

I don't use an IDE (emacs FTW), so I can't comment on how to get PHPStorm to do this properly. That does seem like a valid concern, and I think it'd be important to sort that out as part of this issue. That's the only reason I'm not moving this to RTBC, since otherwise, it's ready.

I was confused when this was brought up in Slack since I remember having to get the ordering right to make phpcs happy, but now I see it's that Coder already implemented this, and we're retroactively approving the policy now. 😂

kim.pepper’s picture

We do have a rule in coder_sniffer but it's not case-sensitive https://git.drupalcode.org/project/coder/-/blob/8.3.x/coder_sniffer/Drup...

acbramley’s picture

When I sort lines in PHPStorm they are sorted case sensitive

So that answers my first question in #41. PHPStorm will sort using proper case-sensitive when you use "Code > Optimize Imports"

For me, Optimize Imports actually sorts insensitively. Using the example in the IS I get

use Drupal\car\CarInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\door\DoorInterface;

After running Optimize Imports. This is very common to have on as a Save action so this would be very disruptive if we enforced case sensitivity.

If I turn off the auto optimize setting, and just have the "Sort 'use' statements" option set to Alphabetically in Editor > Code Style > PHP > Code Conversion it actually does nothing... so not sure how @quietone is getting the opposite results.

dww’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community
Related issues: +#3310013: Add sniff to check and fix the order of Use statements

Apologies, I missed #48 which includes the PHPStorm specifics on how to make this work. Added a blurb to the summary about that (needs wordsmithing, but doesn't need to hold this up). Moving to RTBC.

dww’s picture

Status: Reviewed & tested by the community » Needs review

Bah, x-post. Looks like there's still confusion on PHPStorm. I think y'all IDE users should sort that out before this is RTBC. 😅

mstrelan’s picture

I suspect "Optimize imports" depends on other configuration such as inspections and code style. For example there is an option to sort use statements alphabetically or by length (who would use that?). Possibly it even looks at phpcs configuration.

joachim’s picture

Status: Needs review » Needs work

> use statements must be located after the initial @file phpDoc block (and after the namespace declaration, if any).

The wording of this looks like it dates back to when most of our files were procedural and had a @file docblock and only rarely had a namespace.

That's not the case now - we mostly have class files.

It should say something like:

> use statements must be located after the initial @file phpDoc block (if any) and after the namespace declaration (if any).

dww’s picture

Issue summary: View changes
Status: Needs work » Needs review

@joachim: Feel free to make minor edits like that yourself. You don't need to set to NW for something like that, just fix it.

dww’s picture

Issue summary: View changes

Merged the 2nd and 3rd bullet points, since they were both about case sensitive sorting.

kingdutch’s picture

The relevant PHPStorm issue to get this fixed for IDE users is https://youtrack.jetbrains.com/issue/WI-26078/Automatically-added-use-st...

quietone’s picture

Issue summary: View changes

This section already has information about the placement of use statements in the file. For classes the placement is shown by example and for files without a namespace there is a item for it. Therefore, I don't think the first item of the proposed text changes is needed.

And I disagree with adding how to setup an IDE in the Coding Standards. Perhaps, that is not the intention.

Also, I expect that any software developer knows what case-sensitive ordering is so we don't need the last line. Correct me if I am wrong.

So, altogether I think this proposed text could be like this.

use statements should be in case-sensitive order.
Example:

use Drupal\Core\Controller\ControllerBase;
use Drupal\car\CarInterface;
use Drupal\door\DoorInterface;

Opinions?

joachim’s picture

> Also, I expect that any software developer knows what case-sensitive ordering is so we don't need the last line. Correct me if I am wrong.

I personally have no idea in what order it places upper and lower case.

Also, some IDEs handle ordering differently -- I can't find the issue, but I recently discovered that. IIRC it was something like the ordering of 'Foo\Bar' and 'FooBar' was different in PHP and VSCode, or PHP and one of the PHPCS-type tools.

dww’s picture

For the record, I’m not proposing the “standards” include PHPStorm instructions. I only added it as a related documentation change that we could / should do while fixing this.

dww’s picture

Issue summary: View changes

Clarifying the summary a bit

quietone’s picture

Issue summary: View changes
StatusFileSize
new53.22 KB

I personally have no idea in what order it places upper and lower case.

Ah, good to know. I guess that is my bias. I did a lot of work in Octal and used to know the number for many characters. And, of course, using man ascii when I forget.

I did some reading on hyphens and I think one is not needed after upper and lower. And since this last sentence is explaining the first maybe they should be together like this.

use statements must be sorted alphabetically, in case-sensitive order. Case-sensitive ordering places upper case before lower case, so Core precedes car.
Example:

use Drupal\Core\Controller\ControllerBase;
use Drupal\car\CarInterface;
use Drupal\door\DoorInterface;

I use PHPStorm and it has always used case sensitive sorting and I have not changed any setting for this. Is this different for others? Here is the relevant screenshot, which does not have an option for changing the case sensitivity of the sort. Maybe the behavior was different on older versions?

joachim’s picture

I found the issue! https://github.com/slevomat/coding-standard/issues/1667

There is disagreement on the relative ordering of \ and _.

Quoting from there:

The coding standard wants the lines to be like this:

```
use Drupal\action_link\Ajax\ActionLinkMessageCommand;
use Drupal\action_link\Entity\ActionLinkInterface;
use Drupal\action_link\Plugin\ActionLinkStyle\Ajax;
use Drupal\action_link_formatter_links\DisplayBuildAlter;
```

But if I select them in VSCode and do 'Sort lines ascending' I get this:

```
use Drupal\action_link_formatter_links\DisplayBuildAlter;
use Drupal\action_link\Ajax\ActionLinkMessageCommand;
use Drupal\action_link\Entity\ActionLinkInterface;
use Drupal\action_link\Plugin\ActionLinkStyle\Ajax;
```

So basically, I was ordering using the command in VSCode, then getting the PHPCS error, fixing it to satisfy that, then next time I import a class, VSCode arranges the order again in a way that makes PHPCS unhappy.

klausi’s picture

Priority: Normal » Major

Merged #3470716: Configure use-statement sniff to be case sensitive in Coder to switch on case-sensitive sorting.

Escalating this again to major, as we still have no coding standard although this rule is running in Coder for a year now.

I like the proposed text of @quiteone in #67.

jonathan1055’s picture

Issue summary: View changes

I have updated the issue summary with the proposed changes by @quietone in #67

I think we are now at step 4 - Review by the Coding Standards Committee. But according to the full detailed steps the issues needs to marked RTBC for that. Do we need to address the problem in #68 about \ vs _ ?

jweowu’s picture

There's really no cause for confusion. Backslash is ascii 92. Underscore is ascii 95. Hence backslash sorts before underscore.

I can't think of a locale we might be interested in where that order would flip (if any even exist). Certainly `C` and `UTF-8` locales sort these characters in ascii sequence, and I don't think there's any reason why we'd deviate.

printf '\\\n_\n' | env LC_COLLATE=C sort
printf '\\\n_\n' | env LC_COLLATE=UTF-8 sort

If VSCode is defaulting to the reverse when sorting lines of text, that just sounds like a VSCode bug/feature, either to be fixed upstream or configured to be more sensible (but I couldn't guess which is more likely).

mstrelan’s picture

Agree with #71, VSCode is wrong and there is an existing issue for it.

jonathan1055’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

Thank you @jweowu and @mstrelan, that is excellent. I have updated the proposed text in the issue summary and matched it in the change record.

So this issue is now RTBC and we can move to Step 4 - review by Coding Standards Committee

joachim’s picture

+1 to #71 and #72.

acbramley’s picture

#48 doesn't seem to be correct, I've got a bunch of files failed on Diff module that have been sorted via PHPStorm. E.g

use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Functional\ViewTestBase;

If I reorder these with case sensitivity, and then run PHPStorm's action, it will put them back like this. This is going to cause major headaches for people using storm.

Pipeline https://git.drupalcode.org/project/diff/-/jobs/2824398
File https://git.drupalcode.org/project/diff/-/blob/2.x/tests/src/Functional/...

quietone’s picture

When I use Edit->Sort lines in PHPStorm it is case sensitive. Is the difference a plugin? The only plugin I have enabled for strings is 'Wrap to Column'

acbramley’s picture

@quietone yes that is different, PHPStorm has a built in "Optimise imports" function which, on save, will sort use statements. This is not case sensitive from my testing.

acbramley’s picture

This was also mentioned back in #62 - PHPStorm users are essentially now forced to disable Optimise imports which is quite a shame...

Developing contrib is going to become more cumbersome as well since phpcbf is not always runnable on contrib (e.g if it doesn't have a committed phpcs.xml file).

klausi’s picture

A workaround is to install the Drupal coding Standard in phpstorm with newest Coder, then the use statements will be ordered correctly.

I think we cannot revert the Coder change now as people are already adopting this new standard.

Please push phpstorm to fix this issue: https://youtrack.jetbrains.com/issue/WI-26078/Automatically-added-use-st...

acbramley’s picture

A workaround is to install the Drupal coding Standard in phpstorm with newest Coder, then the use statements will be ordered correctly.

I tried this and it did not work. The only thing I can think of is people have phpcbf running on save which of course would fix it but as per #78 is not always going to work.

acbramley’s picture

I've got something that works! Thank you @el7cosmos for showing me the Quality Tools > External Formatters setting.

Steps to setup code formatting on save:

1. Go to PHP > Quality Tools. Select "PHP Code Beautifier and Fixer" (this seems to run phpcbf)

2. Go to Tools > Actions on Save. Tick "Reformat code" and choose PHP files (or others if you want). Make sure Whole file is selected, changed lines doesn't seem to work properly).

Now when you save, it will effectively run phpcbf which seems quite fast and will fix up incorrectly ordered use statements, as well as other stuff for you.

This will still have issues with contrib development as per above, but is a nice stopgap.

plach’s picture

If you are using PHPStorm, please upvote the related issue. This should increase the likelihood we get a fix in timely fashion.

mpotter’s picture

Now that this update has broken all of our builds and I'm finding this issue for the first time, I feel like the initial "example" that people are referencing was misleading. The example of case-sensitive order was:

use Drupal\Core\Controller\ControllerBase;
use Drupal\car\CarInterface;
use Drupal\door\DoorInterface;
use Drupal\door_handle\HandleInterface;

But this is another example of what case-sensitive order would be:

use Drupal\Core\Controller\ControllerBase;
use Drupal\DoorHandle\Something
use Drupal\car\CarInterface;
use Drupal\door\DoorInterface;
use Drupal\door_handle\HandleInterface;

because "D" is < "c". This is actually more difficult for me personally to parse and read. I prefer what PHPStorm is doing. But I guess this decision has already been made and we are stuck with it because the new version was already released. But it's causing a major amount of work for us to update all of our projects to pass our linting/coder validation.

jonathan1055’s picture

Just for info, if you add the following to your project's phpcs.xml(.dist) file to put the case-sensitive setting back to false:

  <rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
    <properties>
      <property name="caseSensitive" value="false"/>
    </properties>
  </rule>

the order that phpcbf fixer gives for mpotter's example in #83 is:

use Drupal\car\CarInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\door\DoorInterface;
use Drupal\door_handle\HandleInterface;
use Drupal\DoorHandle\Something;
jweowu’s picture

it's causing a major amount of work for us to update all of our projects to pass our linting/coder validation.

It sounds like a relatively easy Perl script to update them all in bulk, FWIW.

klausi’s picture

No perl script needed, you can run phpcbf and it will bulk fix all use statement ordering for you.

minoroffense’s picture

I feel as though big coding standards changes like this should follow a core release or major versioning release of coder or something. We have versioning for core APIs why not versions of the coding spec.

We have dozens of individual modules to go and update now and every CI build is broken (including client releases where this spec has nothing to do with what they're trying to ship). Especially for what is arguably a cosmetic change to the spec.

Sure running the beautifier to autofix is great and all but it takes time to do that, time to explain to clients why everything is broken all of a sudden and more time to create follow up releases etc...

donquixote’s picture

Also, I expect that any software developer knows what case-sensitive ordering is so we don't need the last line. Correct me if I am wrong.

I would disagree.
A case-sensitive comparison is clear.
But a case-sensitive order, not so much. It could be derived from a character set (e.g. ASCII) where letters have numeric representations, but this is by no means clear. It could also be based on whatever specific php sort functions do.
E.g. for regular letters it could be "A, B, C, ..., a, b, c, ..." or it could be "A, a, B, b, C, c, ...".
For special letters like 'ä' it is less clear. Although I think we don't allow them in namespaces or module names, or people just avoid them and stick to a...z.

slevomat uses strcasecmp() and strcmp() depending on the setting.
This will result in "A, B, C, ..., a, b, c..." for regular chars.

Here are some experiments, https://3v4l.org/iUKZh

With my current version of PhpStorm, the auto sort with Ctrl+Alt+O (Optimize imports) puts the imports in the "wrong" order according to the new standard.
But I also see cases of the wrong order in Drupal core (maybe because I am not in latest version).

jonathan1055’s picture

Issue summary: View changes

Following the comments in #83 and #88 I have updated the proposed text and example, to show that all uppercase letters come before the lowercase letters.

As per comment #73 we are still at step 4 'Review by Coding Standards Committee'

Replying to donquixote in #88

... PhpStorm, the auto sort with Ctrl+Alt+O (Optimize imports) puts the imports in the "wrong" order ...

There is a known issue/bug in PHPStorm as mentioned in #79 and #82 above.

I also see cases of the wrong order in Drupal core

That is because Core has not been updated to use this standard yet, and it is not enabled in the core phpcs.xml config. There is an issue #2885792: Sort use statements which can be worked on, when Core 11.x gets updated to run with Coder 8.3.25

longwave’s picture

After upgrading Coder on a customer project and then running into the issues identified here where PHPStorm sorts differently by default, I am -1 on the specific proposal here. To me it's never been a problem the way things were, and I would prefer just to let PHPStorm handle things where possible instead of making my workflow more complicated. I very rarely have to manually do anything with use statements; usually I just let the IDE figure it out.

quietone’s picture

@longwave, what is the 'default' sorting for you? When I use PHPStorm to sort lines it is definitely alphabetical and case-sensitive.

mstrelan’s picture

The "sort lines" function in phpstorm works differently to automatic imports (e.g. with autosuggest, copy/pasting, etc) as well as the "optimize imports" function, so you only get the correct order if you manually choose to sort lines.

longwave’s picture

A simple example borrowed from my project. If I have:

use Drupal\Component\Utility\Html;

and let PHPStorm autocomplete the BlockContent entity class it will order them like this:

use Drupal\block_content\Entity\BlockContent;
use Drupal\Component\Utility\Html;

This fails the sniff:

   9 | ERROR | [x] Use statements should be sorted alphabetically. The first wrong one is Drupal\Component\Utility\Html.

Manually selecting the statements and invoking "Sort lines" will re-sort them as per the sniff:

use Drupal\Component\Utility\Html;
use Drupal\block_content\Entity\BlockContent;

But pressing Ctrl+Alt+O "optimize imports" will re-sort them the other way again:

use Drupal\block_content\Entity\BlockContent;
use Drupal\Component\Utility\Html;

In other words PHPStorm defaults to the "wrong" order for automatic operations and only chooses the order here if you manually select and sort the lines.

longwave’s picture

PSR-12 specifies the order of blocks of code in the file, but it does not specify the order of use statements. Similarly, PSR-2 does not specify order either.

catch’s picture

Status: Reviewed & tested by the community » Needs review

Let's move this back to needs review. The idea of coding standards is so that stylistic things don't get in the way when writing code, but if this is incompatible across this many IDEs, then it's going to get in the way (and already is given that coder apparently enabled this by default already, although probably good overall since it's flagged it earlier).

We could adopt the standard, but postpone/revert coder implementation and core implementing the phpcs rule until at least the most popular IDEs are compatible, but not sure who that helps.

I don't use an IDE so it doesn't affect me but fiddling with stuff like this is one of the reasons I've continued to not use one.

damienmckenna’s picture

Taking a leaf out of the PSR coding standard approach here.. if there's an existing tool that sorts them in a case-insensitive way, and if most text editors (*cough* Textmate *cough*) & IDEs want to sort them in a case-insensitive way, why are we not following the existing defacto standard of case-insensitive sorting? This feels like trying to shoehorn a specific approach on the community and on IDE developers rather than adopting what's already in use.

joachim’s picture

There's also the problem of disagreements between the order of \ and _.

e.g. in WizardPluginBase:

use Drupal\views\Views;
use Drupal\views_ui\ViewUI;
klausi’s picture

Hm, sorry that this caused problems for many people :-|

I think it is probably best to open a new Coder issue to carefully list the pros and cons of a revert, and also workarounds like configuring phpcbf in Phpstorm or disabling this sniff so that you don't have a problem. Some projects have adopted the case-sensitive standard already, so a revert creates work again for them.

Vscode: line sorting is almost correct in our case-sensitive standard and wrong in our case-insensitive standard.
Phpstorm: line sorting is correct in the case-sensititve standard, but "optimize import" is wrong in the case sensitive standard. Reversed for the case-insensitive standard.
Textmate: line sorting is wrong in the case-sensititve standard.

Difficult choice how and where we want to be wrong with our standard. I would say Vscode is more important than Textmate for example, but a hard choice.

Having no sorting standard for use statements is even worse, makes it harder to find use statements in longer lists.

acbramley’s picture

Some projects have adopted the case-sensitive standard already, so a revert creates work again for them.

This was the stance just days after the change was committed. Now we've seen how many more people it's affecting. I think it's safe to say it would have been easier for majority of people to revert this change then. Even now it may still be. Commits are easier to revert than having to phpcbf all of your contrib modules for reasons already explained above.

jweowu’s picture

Just to be clear, it was also affecting people prior to the recent change. It's simply affecting different people since the change (and so the latter set of people have mostly only become aware of the problem recently).

Reverting the change means that the original set of people get the problem again. Which is an option, for sure, but it's not making the problem go away.

donquixote’s picture

In the issue summary I don't see any argument why the proposed order (case sensitive) would be preferable to something else.
I also don't see a statistical analysis for existing code in and outside of Drupal.
(might be in the comments)
I only see the argument that having a convention is better than having no convention.

As for existing code, the following two regex searches can be used to search in a vendor directory:
(I did the search in PhpStorm with case sensitivity and multiline enabled)

use ((\w+\\)+\w+)[A-Z]\w+.*;
use \1[a-z]\w+.*;
use ((\w+\\)+\w+)[a-z]\w+.*;
use \1[A-Z]\w+.*;

The "\1" references a previously captured substring.

For all the code I found, it seemed that case insensitive sorting was used.
But even in a typical Drupal project these were just a few matches overall.

Drupal is an outlier because module names in namespaces are lowercase.
(I remember back in the days I argued for this, so that we see the literal module name in the namespace, not a captitalized form. I still think this was the right choice.)

donquixote’s picture

Reverting the change means that the original set of people get the problem again. Which is an option, for sure, but it's not making the problem go away.

I would propose a more flexible solution:
- We choose one default sort order that will also be used for Drupal core. This should follow what existing tools already support, and what is common in existing 3rd party code.
- Contrib modules or website projects are allowed to use their own order. We can reference documentation how to configure this with Coder and phpcs.xml.

krystalcode’s picture

So, what is the purpose of sorting use statements? Is it to please PHPStorm or X, Y or Z text editor so that when you use its sorting feature it is sorted the way the editor wants? And then run PHPCS and get it to give you green lights so that the pipeline passes? Or is it to randomly choose an algorithm i.e. case-sensitive or case-insensitive and agree that everybody follows it?

To me, no, it's about human readability. Even that, using find/search functionality in most text editors you can easily find a use statement directly without having to look for it with the eye. So, the most important circumstance where it matters is when you want to review all use statements in a file, or to find a statement that you don't quite remember what exactly it is and can't use search e.g. I don't know which PSR-18 HTTP client is being used so I can't search specifically for "Guzzle" to find the import statement - I need to look for it manually.

That is, we should be taking an approach based on how the human eye/brain would more comfortably and quickly read the use statements, not how a software or algorithm would do. The software does not care, give it a rule and it will follow it.

What I have concluded to work best is to follow a more intelligent approach and use logical groups of statements separated by an empty or comment line. Do that only if the number of statements justify it - otherwise it's not necessary e.g. if it's more than 5. And if the number of statements in a group grows too much, break it further into subgroups.

Note that PSR-12 might not define the sorting algorithm to use, but it does define as a MUST to group use statements for classes, functions and constants.

I find it really easy for the eye to find what it's looking for when I follow this method.

Here is an example from group.module:

use Drupal\Component\Utility\Html;                                                                                                                                                                                                     
use Drupal\Core\Access\AccessResult;                                                                                                                                                                                                   
use Drupal\Core\Config\Entity\ConfigEntityInterface;                                                                                                                                                                                   
use Drupal\Core\Database\Query\AlterableInterface;                                                                                                                                                                                     
use Drupal\Core\Database\Query\SelectInterface;                                                                                                                                                                                        
use Drupal\Core\Entity\EntityInterface;                                                                                                                                                                                                
use Drupal\Core\Field\FieldDefinitionInterface;                                                                                                                                                                                        
use Drupal\Core\Field\FieldItemListInterface;                                                                                                                                                                                          
use Drupal\Core\Form\FormStateInterface;                                                                                                                                                                                               
use Drupal\Core\Render\Element;                                                                                                                                                                                                        
use Drupal\Core\Routing\RouteMatchInterface;                                                                                                                                                                                           
use Drupal\Core\Session\AccountInterface;                                                                                                                                                                                              
use Drupal\group\Entity\GroupRelationshipInterface;                                                                                                                                                                                    
use Drupal\group\Entity\GroupInterface;                                                                                                                                                                                                
use Drupal\group\Entity\Storage\ConfigWrapperStorageInterface;                                                                                                                                                                         
use Drupal\group\Entity\Storage\GroupRelationshipStorageInterface;                                                                                                                                                                     
use Drupal\group\Entity\Storage\GroupRoleStorageInterface;                                                                                                                                                                             
use Drupal\group\QueryAccess\EntityQueryAlter;                                                                                                                                                                                         
use Drupal\group\QueryAccess\GroupRelationshipQueryAlter;                                                                                                                                                                              
use Drupal\group\QueryAccess\GroupQueryAlter;                                                                                                                                                                                          
use Drupal\views\Plugin\views\query\QueryPluginBase;                                                                                                                                                                                   
use Drupal\views\Plugin\views\query\Sql;                                                                                                                                                                                               
use Drupal\views\ViewExecutable;

I would write this as follows:

// Drupal modules.
use Drupal\group\Entity\GroupRelationshipInterface;                                                                                                                                                                                    
use Drupal\group\Entity\GroupInterface;                                                                                                                                                                                                
use Drupal\group\Entity\Storage\ConfigWrapperStorageInterface;                                                                                                                                                                         
use Drupal\group\Entity\Storage\GroupRelationshipStorageInterface;                                                                                                                                                                     
use Drupal\group\Entity\Storage\GroupRoleStorageInterface;                                                                                                                                                                             
use Drupal\group\QueryAccess\EntityQueryAlter;                                                                                                                                                                                         
use Drupal\group\QueryAccess\GroupRelationshipQueryAlter;                                                                                                                                                                              
use Drupal\group\QueryAccess\GroupQueryAlter;                                                                                                                                                                                          
use Drupal\views\Plugin\views\query\QueryPluginBase;                                                                                                                                                                                   
use Drupal\views\Plugin\views\query\Sql;                                                                                                                                                                                               
use Drupal\views\ViewExecutable;

// Drupal core.
use Drupal\Component\Utility\Html;                                                                                                                                                                                                     
use Drupal\Core\Access\AccessResult;                                                                                                                                                                                                   
use Drupal\Core\Config\Entity\ConfigEntityInterface;                                                                                                                                                                                   
use Drupal\Core\Database\Query\AlterableInterface;                                                                                                                                                                                     
use Drupal\Core\Database\Query\SelectInterface;                                                                                                                                                                                        
use Drupal\Core\Entity\EntityInterface;                                                                                                                                                                                                
use Drupal\Core\Field\FieldDefinitionInterface;                                                                                                                                                                                        
use Drupal\Core\Field\FieldItemListInterface;                                                                                                                                                                                          
use Drupal\Core\Form\FormStateInterface;                                                                                                                                                                                               
use Drupal\Core\Render\Element;                                                                                                                                                                                                        
use Drupal\Core\Routing\RouteMatchInterface;                                                                                                                                                                                           
use Drupal\Core\Session\AccountInterface;                                                                                                                                                                                              

Here is an example from Drupal\graphql_core_schema\Plugin\GraphQL\Schema\CoreComposableSchema:

use Drupal\Core\Cache\CacheBackendInterface;                                                                                                                                                                                           
use Drupal\Core\DependencyInjection\DependencySerializationTrait;                                                                                                                                                                      
use Drupal\Core\Entity\EntityTypeManagerInterface;                                                                                                                                                                                     
use Drupal\Core\Extension\ModuleHandlerInterface;                                                                                                                                                                                      
use Drupal\Core\File\FileSystemInterface;                                                                                                                                                                                              
use Drupal\Core\Form\FormStateInterface;                                                                                                                                                                                               
use Drupal\Core\Render\Element\Checkboxes;                                                                                                                                                                                             
use Drupal\Core\TypedData\TypedDataTrait;                                                                                                                                                                                              
use Drupal\graphql\GraphQL\ResolverBuilder;                                                                                                                                                                                            
use Drupal\graphql\GraphQL\ResolverRegistry;                                                                                                                                                                                           
use Drupal\graphql\GraphQL\ResolverRegistryInterface;                                                                                                                                                                                  
use Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema;                                                                                                                                                                             
use Drupal\graphql\Plugin\SchemaExtensionPluginInterface;                                                                                                                                                                              
use Drupal\graphql\Plugin\SchemaExtensionPluginManager;                                                                                                                                                                                
use Drupal\graphql_core_schema\CoreComposableConfig;                                                                                                                                                                                   
use Drupal\graphql_core_schema\CoreComposableResolver;                                                                                                                                                                                 
use Drupal\graphql_core_schema\CoreSchemaExtensionInterface;                                                                                                                                                                           
use Drupal\graphql_core_schema\CoreSchemaInterfaceExtensionInterface;                                                                                                                                                                  
use Drupal\graphql_core_schema\EntitySchemaBuilder;                                                                                                                                                                                    
use Drupal\graphql_core_schema\Form\CoreComposableSchemaFormHelper;                                                                                                                                                                    
use Drupal\graphql_core_schema\GraphQL\Enums\DrupalDateFormatEnum;                                                                                                                                                                     
use Drupal\graphql_core_schema\GraphQL\Enums\EntityTypeEnum;                                                                                                                                                                           
use Drupal\graphql_core_schema\GraphQL\Enums\LangcodeEnum;                                                                                                                                                                             
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderGenerator;                                                                                                                                                                   
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderRegistry;                                                                                                                                                                    
use Drupal\graphql_core_schema\TypeAwareSchemaExtensionInterface;                                                                                                                                                                      
use Drupal\typed_data\DataFetcherTrait;                                                                                                                                                                                                
use GraphQL\Language\AST\DocumentNode;                                                                                                                                                                                                 
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;                                                                                                                                                                                  
use GraphQL\Language\AST\TypeDefinitionNode;                                                                                                                                                                                           
use GraphQL\Language\AST\UnionTypeDefinitionNode;                                                                                                                                                                                      
use GraphQL\Language\Parser;                                                                                                                                                                                                           
use GraphQL\Type\Schema;                                                                                                                                                                                                               
use GraphQL\Utils\BuildSchema;                                                                                                                                                                                                         
use GraphQL\Utils\SchemaExtender;                                                                                                                                                                                                      
use GraphQL\Utils\SchemaPrinter;                                                                                                                                                                                                       
use Symfony\Component\DependencyInjection\ContainerInterface;

I would write this as follows:

// Drupal modules.
use Drupal\graphql\GraphQL\ResolverBuilder;                                                                                                                                                                                            
use Drupal\graphql\GraphQL\ResolverRegistry;                                                                                                                                                                                           
use Drupal\graphql\GraphQL\ResolverRegistryInterface;                                                                                                                                                                                  
use Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema;                                                                                                                                                                             
use Drupal\graphql\Plugin\SchemaExtensionPluginInterface;                                                                                                                                                                              
use Drupal\graphql\Plugin\SchemaExtensionPluginManager;                                                                                                                                                                                
use Drupal\graphql_core_schema\CoreComposableConfig;                                                                                                                                                                                   
use Drupal\graphql_core_schema\CoreComposableResolver;                                                                                                                                                                                 
use Drupal\graphql_core_schema\CoreSchemaExtensionInterface;                                                                                                                                                                           
use Drupal\graphql_core_schema\CoreSchemaInterfaceExtensionInterface;                                                                                                                                                                  
use Drupal\graphql_core_schema\EntitySchemaBuilder;                                                                                                                                                                                    
use Drupal\graphql_core_schema\Form\CoreComposableSchemaFormHelper;                                                                                                                                                                    
use Drupal\graphql_core_schema\GraphQL\Enums\DrupalDateFormatEnum;                                                                                                                                                                     
use Drupal\graphql_core_schema\GraphQL\Enums\EntityTypeEnum;                                                                                                                                                                           
use Drupal\graphql_core_schema\GraphQL\Enums\LangcodeEnum;                                                                                                                                                                             
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderGenerator;                                                                                                                                                                   
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderRegistry;                                                                                                                                                                    
use Drupal\graphql_core_schema\TypeAwareSchemaExtensionInterface;                                                                                                                                                                      
use Drupal\typed_data\DataFetcherTrait;

// Drupal core.
use Drupal\Core\Cache\CacheBackendInterface;                                                                                                                                                                                           
use Drupal\Core\DependencyInjection\DependencySerializationTrait;                                                                                                                                                                      
use Drupal\Core\Entity\EntityTypeManagerInterface;                                                                                                                                                                                     
use Drupal\Core\Extension\ModuleHandlerInterface;                                                                                                                                                                                      
use Drupal\Core\File\FileSystemInterface;                                                                                                                                                                                              
use Drupal\Core\Form\FormStateInterface;                                                                                                                                                                                               
use Drupal\Core\Render\Element\Checkboxes;                                                                                                                                                                                             
use Drupal\Core\TypedData\TypedDataTrait;       
                                                                                                                                                                                       
// External libraries.                                                                                                                                                                                                
use GraphQL\Language\AST\DocumentNode;                                                                                                                                                                                                 
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;                                                                                                                                                                                  
use GraphQL\Language\AST\TypeDefinitionNode;                                                                                                                                                                                           
use GraphQL\Language\AST\UnionTypeDefinitionNode;                                                                                                                                                                                      
use GraphQL\Language\Parser;                                                                                                                                                                                                           
use GraphQL\Type\Schema;                                                                                                                                                                                                               
use GraphQL\Utils\BuildSchema;                                                                                                                                                                                                         
use GraphQL\Utils\SchemaExtender;                                                                                                                                                                                                      
use GraphQL\Utils\SchemaPrinter;                                                                                                                                                                                                       
use Symfony\Component\DependencyInjection\ContainerInterface;

And since the first group in this example is quite big, I'd break it further down to:

// This module.                                                                                                                                                         
use Drupal\graphql_core_schema\CoreComposableConfig;                                                                                                                                                                                   
use Drupal\graphql_core_schema\CoreComposableResolver;                                                                                                                                                                                 
use Drupal\graphql_core_schema\CoreSchemaExtensionInterface;                                                                                                                                                                           
use Drupal\graphql_core_schema\CoreSchemaInterfaceExtensionInterface;                                                                                                                                                                  
use Drupal\graphql_core_schema\EntitySchemaBuilder;                                                                                                                                                                                    
use Drupal\graphql_core_schema\Form\CoreComposableSchemaFormHelper;                                                                                                                                                                    
use Drupal\graphql_core_schema\GraphQL\Enums\DrupalDateFormatEnum;                                                                                                                                                                     
use Drupal\graphql_core_schema\GraphQL\Enums\EntityTypeEnum;                                                                                                                                                                           
use Drupal\graphql_core_schema\GraphQL\Enums\LangcodeEnum;                                                                                                                                                                             
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderGenerator;                                                                                                                                                                   
use Drupal\graphql_core_schema\SchemaBuilder\SchemaBuilderRegistry;                                                                                                                                                                    
use Drupal\graphql_core_schema\TypeAwareSchemaExtensionInterface;    
      
// Contrib modules.      
use Drupal\graphql\GraphQL\ResolverBuilder;                                                                                                                                                                                            
use Drupal\graphql\GraphQL\ResolverRegistry;                                                                                                                                                                                           
use Drupal\graphql\GraphQL\ResolverRegistryInterface;                                                                                                                                                                                  
use Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema;                                                                                                                                                                             
use Drupal\graphql\Plugin\SchemaExtensionPluginInterface;                                                                                                                                                                              
use Drupal\graphql\Plugin\SchemaExtensionPluginManager;      

// Core modules.                                                                                                                                                            
use Drupal\typed_data\DataFetcherTrait;

// Core libraries.
use Drupal\Core\Cache\CacheBackendInterface;                                                                                                                                                                                           
use Drupal\Core\DependencyInjection\DependencySerializationTrait;                                                                                                                                                                      
use Drupal\Core\Entity\EntityTypeManagerInterface;                                                                                                                                                                                     
use Drupal\Core\Extension\ModuleHandlerInterface;                                                                                                                                                                                      
use Drupal\Core\File\FileSystemInterface;                                                                                                                                                                                              
use Drupal\Core\Form\FormStateInterface;                                                                                                                                                                                               
use Drupal\Core\Render\Element\Checkboxes;                                                                                                                                                                                             
use Drupal\Core\TypedData\TypedDataTrait;       
                                                                                                                                                                                       
// External libraries.                                                                                                                                                                                                
use GraphQL\Language\AST\DocumentNode;                                                                                                                                                                                                 
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;                                                                                                                                                                                  
use GraphQL\Language\AST\TypeDefinitionNode;                                                                                                                                                                                           
use GraphQL\Language\AST\UnionTypeDefinitionNode;                                                                                                                                                                                      
use GraphQL\Language\Parser;                                                                                                                                                                                                           
use GraphQL\Type\Schema;                                                                                                                                                                                                               
use GraphQL\Utils\BuildSchema;                                                                                                                                                                                                         
use GraphQL\Utils\SchemaExtender;                                                                                                                                                                                                      
use GraphQL\Utils\SchemaPrinter;                                                                                                                                                                                                       
use Symfony\Component\DependencyInjection\ContainerInterface;

Now, to me that's very readable when I need to review or find an import statement - compared to trying to find it alphabetically. I may not even remember the exact fully qualified name, but I know that what I'm looking for is a module or an external library so my eye immediately goes where it needs to go.

Within each group, I think case-insensitive alphabetical sorting makes more sense. For a human, c is the same letter as C. The example in the description of this issue is not particularly useful for a human reader. Fortunately though, this mostly becomes a non-issue with the approach described above because in 99% of the cases modules are grouped separately than core and external libraries, and it's pretty much only Drupal modules that use lowercase.

I have documented this approach here: https://github.com/krystalcode/drupal8-coding-standards

When it comes to PHPCS, I'll come back to my initial point. I frequently get developers updating a file where I have used this method, only to change the import statements back to an unreadable blob with the only objective to make PHPCS go green. We're missing the point here.

That said, I think it wouldn't be difficult to write a Sniff that:

  • Allows single empty lines between use statements.
  • Allows for comment lines between use statements.
  • Recognizes groups of import statements using the empty lines as separators.
  • Requires alphabetical sorting - but only within each group.
  • Raises a warning if a group contains more than 10 import statements - configurable.
quietone’s picture

As asked for by @klausi, here is the Coder issue to discuss reverting, #3483028: Remove use-statement order sniff

donquixote’s picture

@krystalcode (#103):

So, what is the purpose of sorting use statements?

For me, the main purpose of ordering anything, including imports, is to avoid noise and conflicts in git, and duplicated entries resulting from faulty merges. These problems can be avoided if there is one canonical order that is followed by all developers and tools.

krystalcode’s picture

The way Git merges changes does not have much to do - if anything - with the sorting of the contents of the code. Conflicts most commonly happen when you edit the same lines.

5. use Drupal\address\AddressInterface;
6. use Drupal\node\NodeInterface;
7. use Drupal\user\UserInterface;

Branch A adds:

5. use Drupal\address\AddressInterface;
+ 6. use Drupal\group\GroupMembershipLoaderInterface;
7. use Drupal\node\NodeInterface;
8. use Drupal\user\UserInterface;

Branch B adds:

5. use Drupal\address\AddressInterface;
+ 6. use Drupal\commerce\ConfigurableFieldManagerInterface;
7. use Drupal\node\NodeInterface;
8. use Drupal\user\UserInterface;

The result:

use Drupal\address\AddressInterface;
<<<<<<< Branch A
use Drupal\group\GroupMembershipLoaderInterface;
=======
use Drupal\commerce\ConfigurableFieldManagerInterface;
>>>>>>> Branch B
use Drupal\node\NodeInterface;
use Drupal\user\UserInterface;

Both changes placed the import statements where they should and followed the correct alphabetical sorting. Note that I actually tested this to make sure I'm not saying anything inaccurate and got the behavior above.

jweowu’s picture

The git commit noise and conflicts are a result of multiple different users/IDEs applying different sorting rules, and re-arranging the order of lines which were previously sorted by a different program. That sort of noise can ping-pong back and forth if multiple developers are working on the same file (not just the same part of the file), because the list of Use statements at the start of the file is for the whole file. Enforcing a standard, well-defined, stable sort order ensures that this kind of noise won't happen (or at least won't ever be merged).

krystalcode’s picture

Ok got it, thanks for the explanation. I'm not sure really how common this problem is, but I guess I'm old school and prefer to manually craft code. I also understand the "lazy developer" syndrome where people want to focus on the actual logic that matters most and have their text editor do the boring stuff for them - not saying this in a demeaning way, I appreciate the practicality of such attitude. I write scripts to automate things for me all the time.

In that case I think it should be a suggestion and not phrased with MUST in the standards, there could be a default Sniff in Coder but also allowing for alternatives letting the maintainer of the code make a different choice.

quietone’s picture

Status: Needs review » Active

It isn't really feasible to implement this because of the way PHPStorm does sorting. If one used the 'Sort lines' menu item then the lines are sorted with case sensitivity whereas when use statements are automatically inserted they are without case sensitivity.

The issue for this is Automatically added use statements should use PHP natural sort order which is 10 years old.

solideogloria’s picture

RE #103, I definitely prefer what it looks like as-is compared to the groupings you showed. It makes more sense for Core use statements to come first, because Core code is the foundation, and the modules build on top of that. Plus, a single list of alphabetically ordered statements is the easiest to sort through visually (and programmatically).

krystalcode’s picture

@solideogloria each to its preference, that's why PHPCS rules are customizable. My objection is to make one or the other as a MUST in the standards.

Just to point out the following in your comment though.

It makes more sense for Core use statements to come first, because Core code is the foundation, and the modules build on top of that. Plus, a single list of alphabetically ordered statements is the easiest to sort through visually

The first sentence is exactly what I'm proposing i.e. logical grouping of import statements, whether core or modules come first that's secondary.

The second sentence is the opposite i.e. alphabetical sorting without logical grouping. Drupal/Core is not the first set of packages alphabetically, it just happens to be so in the examples that I listed.

solideogloria’s picture

And what would cause Core to not come first? Every contrib module import is already after Core, as its namespaces are lowercase.

donquixote’s picture

And what would cause Core to not come first? Every contrib module import is already after Core, as its namespaces are lowercase.

https://git.drupalcode.org/project/aws/-/blob/2.0.x/src/Entity/Profile.p...

In other words, code outside the Drupal namespace, mostly packages that live in /vendor/.
In fact, some 3rd party package namespaces will come before "Drupal\...", others will come after.
All of this is totally ok, and is not any different from e.g. symfony packages, where imports are sorted like "use Composer\...", "use Psr\...", "use Symfony\...", "use Twig\...".

These imports are going to be mostly managed and read by tools like IDEs or phpcbf.
As a developer, I don't want to scroll up and look at the imports.
When I write "new MyClass", the autocomplete will propose a matching qualified class name (class with namespace), and insert the import at the top.

EDIT: I actually don't know if other editors outside of PhpStorm help you with these imports :)

Any convention we introduce here has to be one that is supported by commonly used tools, and does not require new custom sorting specific to Drupal.

solideogloria’s picture

When I write "new MyClass", the autocomplete will propose a matching qualified class name (class with namespace), and insert the import at the top.

Any convention we introduce here has to be one that is supported by commonly used tools, and does not require new custom sorting specific to Drupal.

Agreed. I never type the use statements; they are automatically added.

donquixote’s picture

@krystalcode (#106)

The way Git merges changes does not have much to do - if anything - with the sorting of the contents of the code. Conflicts most commonly happen when you edit the same lines.

The problem with git vs imports is not just about actual conflicts.
If there is no strict order, there can also be duplication:

commit 1:

use N\A
+ use N\NewClass;
use N\B
use N\C

commit 2:

use N\A
use N\B
use N\C
+ use N\NewClass;

merge 1 + 2:

use N\A
use N\NewClass;
use N\B
use N\C
use N\NewClass;

Let's say we merge this, we notice the problem, and we want to clean it up.
But we actually have two branches that do this clean-up:

cleanup branch 1:

use N\A
- use N\NewClass;
use N\B
use N\C
use N\NewClass;

cleanup branch 2:

use N\A
use N\NewClass;
use N\B
use N\C
- use N\NewClass;

on merge, both of them are gone, and the import will be missing:

use N\A
use N\B
use N\C

Often enough, this does not happen in actual merges, but in a rebase or cherry-pick.
E.g. commit 1 above might be from a feature branch, then commit 2 is from a different feature that is merged upstream. Then branch 1 gets rebased on upstream, and what appears as "merge 1 + 2" above instead occurs as a commit in the rebase.

Having a strict order avoids these problems.
When there is ambiguity, the merge or rebase will see that, and the developer will be forced to sort it out.

johnalbin’s picture

Issue summary: View changes

The coding standards that the issue summary points to are deprecated. I've updated the IS with the new URL and the current wording.

The problem with git vs imports is not just about actual conflicts.
If there is no strict order, there can also be duplication:

This is an interesting point. Somewhat rare, but definitely possible. I should point out that any kind of alphabetical sorting (case-sensitive or case-insensitive) would fix that problem.

Given this point and either Git-related issues, I think we should implement a standard for sorting because any standard would prevent those issues. This specific kind of sorting doesn't matter for the Git-related issues, afaics.

Any convention we introduce here has to be one that […] does not require new custom sorting specific to Drupal.

Both PHP CS Fixer (Symfony's tool) and CS Code Sniffer have options to do case-sensitive or case-insensitive sorting. So neither of those could be considered "specific to Drupal".

Any convention we introduce here has to be one that is supported by commonly used tools…

Both PHPStorm and phpcbf are commonly used tools. In other words, if we make it necessary to run composer phpcbf on code contributions so that we can case-sensitive alphabetical sorting, that seems reasonable to me.

Except for those who want the status quo (no use sorting standard), I think the current discussion boils down to: should we case-sensitive or case-insensitive alphabetical sorting?

Case-insenstive alphabetical sorting

Outside computing, alphabetical sorting is always case-insensitive. I'm unaware of any non-computing sorts that would put Athens before aardvark.

I tested the latest version of PHPStorm, version 2026.1.1. It has configuration under Editor › Code Style › PHP › Code Conversion for:

Sort 'use' statements: either Alphabetically or By length

Its alphabetical sorting is case-insensitve. And the "Alphabetically" option is used by default.

If PHPStorm had a vote on Drupal's coding standards (it doesn't), it would be for case-insensitive alphabetical sorting. Both PHP CodeSniffer and PHP CS Fixer have an option to do case-sensitive alphabetical sorting; they both do case-insensitive alphabetical sorting by default when choosing to sort use statements.

Case-senstive alphabetical sorting

I'm pretty sure case-sensitive alphabetical sorting came about in computing because the sorting algorithm is dirt-cheap to do. Early CS programmers sorted on the ASCII code point and the code points for capital letters came before the code points for lowercase letters, e.g. A is 65 and a is 97.

Regardless of how it came about, is this sorting useful for us? Is putting uppercase before lowercase a useful sort?

Early in this issue people discussed a very Drupal-y sort:

  1. vendor
  2. Drupal\Component\
  3. Drupal\Core\
  4. core modules
  5. contrib modules
  6. etc.

But that would require custom code to make happen. And I agree with those who rejected it.

Case-sensitive sorting would provide this sorting:

  1. vendor + Drupal\Component + Drupal\Core mixed together alphabetically
  2. core modules + contribute modules mixed together

[edit: Whoops! Not quite. Corrected below:]

  1. Vendor components that sort before Drupal\ (like Asm89\) and Doctrine\)
  2. Drupal\Component components
  3. Drupal\Core components
  4. core modules + contribute modules mixed together, Drupal\a… through Drupal\z…
  5. Vendor components that sort after Drupal\ (like Symfony\)

So the question becomes: is this separation useful (aside from its alphabetical usefulness)?

My answer is… maybe? I don't have a strong opinion on the type of alphabetical sorting, but I'm leaning case-insensitive. Not really. Having Drupal\Core come before or after Drupal\block doesn't make any difference. They both will be in the middle of other vendor components in the list.

Case insensitive sort Case sensitive sort
use Asm89\Stack\Cors;
use Drupal\block\Entity;
use Drupal\Component\Diff;
use Drupal\Core\Ajax;
use Drupal\views\Entity;
use Symfony\Component;
use Asm89\Stack\Cors;
use Drupal\Component\Diff;
use Drupal\Core\Ajax;
use Drupal\block\Entity;
use Drupal\views\Entity;
use Symfony\Component;

I do think we should implement alphabetical sorting of use statements, regardless of the case sensitivity aspect.

solideogloria’s picture

Case-sensitive sorting would provide this sorting:

vendor + Drupal\Component + Drupal\Core mixed together alphabetically
core modules + contribute modules mixed together

Does Symfony count as vendor? Symfony imports would come after most of the above imports, either way. So I don't see a reason to use case-sensitive sorting. I think it's more useful to have it that way, anyway. Besides, most imports are done automatically when you add a reference. I can't remember the last time I typed a use statement. If I need to add an import for something I pasted in, I just retype the object's last letter, hit TAB, and the import is added. I think there is value in using the case-insensitive default that code editors have.

jweowu’s picture

I'm pretty sure case-sensitive alphabetical sorting came about in computing because the sorting algorithm is dirt-cheap to do. Early CS programmers sorted on the ASCII code point and the code points for capital letters came before the code points for lowercase letters, e.g. A is 65 and a is 97.

Regardless of how it came about, is this sorting useful for us? Is putting uppercase before lowercase a useful sort?

I think that very simplicity makes it useful for us, because it's unambiguous -- every tool which sorts lines of text by their character code points will reliably produce the exact same result.

The less-well-defined case-insensitive sorting features of some tools appear to vary (inevitably, but perhaps surprisingly); in some cases because case-insensitivity is just one of a set of criteria which are being applied (with the remainder of the criteria differing from one tool to the next -- perhaps depending on what those particular developers felt was intuitive); in other cases simply because "case-insensitive" is a poorly-defined criteria with inherent ambiguity. These differences can then manifest as unwanted noise in the version history. The previously-mentioned inconsistencies of VSCode are evidentially because it's using javascript's localeCompare() to compare characters. Which, could probably be expected to cause inconsistencies even if everyone was using that as a comparison function, as different developers may have different locales.

So I (still) think a well-defined unambiguous sort makes best sense (certainly if the goal is consistency), and sorting by ascii / code point must surely be the most widely-implemented of those options.