Problem/Motivation
Because GlobalDrupalSniff only checks the base class extended by the given class, it fails to detect a variety of instances where global Drupal calls are "bad." This might be a case where the base class simply isn't listed, such as any generic plugin that would extend PluginBase, or one that extends a class that extends one of the listed $baseClasses, or simply a generic service implementation that does not extend any other classes.
Steps to reproduce
Run DrupalPractice against any class that would go into /src/ that does not explicitly extend one of the classes listed in $baseClasses.
Proposed resolution
Replace the $baseClasses check with one that simply checks if this class's file resides in /src/, and add additional help text to the $warning to suggest that Plugins should implement ContainerFactoryPluginInterface.
Issue fork coder-3186635
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3186635-globaldrupalsniffs-use-of
changes, plain diff MR !1
Comments
Comment #3
mrweiner commentedOpened an MR that implements the Proposed Resolution from the issue description. Note that this does not yet include test coverage as I wanted to get the approach approved first. The patch also results in $extendsName being unused, but I didn't delete it.
Comment #4
mrweiner commentedRealizing that maybe this approach needs to make sure that static classes are excluded from the warnings as well?
Comment #5
klausiThanks, this sniff has changed a bit over the years because it triggered too many false positives. I think we should keep the base classes where we know it is safe to trigger a warning. In addition we could trigger the warning if "Plugin" is in the file path, I think that is a good idea.
Would that work for you?
Please create pull requests against https://github.com/pfrenssen/coder so that we see the test cases run there.
Setting priority down to normal, I think this is not a major a bug and Coder just underreports a couple of cases in this tricky sniff.
Comment #6
mrweiner commentedThanks for the feedback, happy to open a PR. If the recommendation is for all OOP code to leverage DI, though, I'm curious what implementations in /src would we not want to flag?
Right now if I create a Utility class /src/SubscriberCheck.php, I might call \Drupal::currentUser() and \Drupal::entityTypeManager() directly. I'd imagine that this should be flagged as a violation, as it should really be added as a service using DI, but right now it's completely ignored. Am I misunderstanding something?
I guess I see now that there's a guard clause earlier to explicitly exclude static classes, so maybe I just don't get why that would be desirable.
Comment #7
klausiHm, I checked the history of this sniff and it seems we wrote it very restrictive from the beginning to not introduce false positives. There are Drupal core APIs where you cannot use constructor injection like the Typed Data API (for example Drupal\Core\TypedData\Plugin\DataType\Language). Then you would have \Drupal calls in classes for example that cannot be avoided. Not sure if Drupal core has many APIs left that have this disadvantage. You could run the sniff on Drupal core and check where it would report problems and how many instances we have.
Then we could open up this sniff more and make special exceptions where we know Drupal core does not allow constructor injection.
Comment #8
mrweiner commentedActually just opened up a thread about this on Slack.
@jhodgdon
@larowlan
@mikelutz
He said that they don't run DrupalPractice on Core, though, so I'd wager those bits would get flagged no matter what. For entities, somebody dropped in https://www.drupal.org/project/drupal/issues/2142515, so I'd imagine those should be flagged until that is resolved.
So given all of that, I wonder if it might be worth changing the sniff to use a deny list of some sort instead of an approve list (like you mentioned). We'd want to ignore anything in /tests, and then Entities and FieldType/DataType plugins. Entities should be fairly simple to pick up, but the latter two might be trickier since the way to tell is from their namespace and annotations. Do you know if phpcs gives us access either of those?
Comment #9
mrweiner commentedComment #10
mrweiner commentedOpened a related issue in Coding Standards. Who knows when/if it will actually be addressed, but figured it made sense to link it here.
Comment #11
klausiPHPCS is not good at detecting class hierarchies, I would recommend PHPStan + https://github.com/mglaman/phpstan-drupal instead which also has a check for \Drupal calls and uses an actual PHP abstract tree parser to analyze code.
Otherwise I'm open to switch to a deny list in Coder if we can improve the sniff that way.
Comment #13
b_sharpe commentedAdded https://github.com/pfrenssen/coder/issues/238, please review.
Comment #14
klausiThanks, reviewed https://github.com/pfrenssen/coder/pull/239
Approach makes sense, please add test coverage.