Part of #2571965: [meta] Fix PHP coding standards in core.
Approach
We are testing coding standards with PHP CodeSniffer, using the Drupal coding standards from the Coder module. Both of these packages are not installed in Drupal core. We need to do a couple of steps in order to download and configure them so we can run a coding standards check.
Step 1: Add the coding standard to the whitelist
Every coding standard is identified by a "sniff". For example, an imaginary coding standard that would require all llamas to be placed inside a square bracket fence would be called the "Drupal.AnimalControlStructure.BracketedFence
sniff". There are dozens of such coding standards, and to make the work easier we have started by only whitelisting the sniffs that pass. For the moment all coding standards that are not yet fixed are simply skipped during the test.
Open the file core/phpcs.xml.dist
and add a line for the sniff of this ticket. The sniff name is in the issue title. Make sure your patch will include the addition of this line.
Step 2: Install PHP CodeSniffer and the ruleset from the Coder module
Both of these packages are not installed by default in Drupal core, so we need to download them. This can be done with Composer, from the root folder of your Drupal installation:
$ composer require drupal/coder squizlabs/php_codesniffer
$ ./vendor/bin/phpcs --config-set installed_paths ../../drupal/coder/coder_sniffer
Once you have installed the phpcs package, you can list all the sniffs available to you like this:
$ ./vendor/bin/phpcs --standard=Drupal -e
This will give you a big list of sniffs, and the Drupal-based ones should be present.
Step 3: Prepare the phpcs.xml file
To speed up the testing you should make a copy of the file phpcs.xml.dist
(in the core/
folder) and save it as phpcs.xml
. This is the configuration file for PHP CodeSniffer.
We only want this phpcs.xml file to specify the sniff we're interested in. So we need to remove all the rule items, and add only our own sniff's rule. Rule items look like this:
<rule ref="Drupal.Classes.UnusedUseStatement"/>
Remove all of them, and add only the sniff from this issue title. This will make sure that our tests run quickly, and are not going to contain any output from unrelated sniffs.
Step 4: Run the test
Now you are ready to run the test! From within the core/
folder, run the following command to launch the test:
$ cd core/
$ ../vendor/bin/phpcs -p
This takes a couple of minutes. The -p
flag shows the progress, so you have a bunch of nice dots to look at while it is running.
Step 5: Fix the failures
When the test is complete it will present you a list of all the files that contain violations of your sniff, and the line numbers where the violations occur. You could fix all of these manually, but thankfully phpcbf
can fix many of them. You can call phpcbf like this:
$ ../vendor/bin/phpcbf
This will fix the errors in place. You can then make a diff of the changes using git. You can also re-run the test with phpcs and determine if that fixed all of them.
Comment | File | Size | Author |
---|---|---|---|
#45 | 2572631-45_phpcs_interfacename.patch | 3.66 KB | mfernea |
Comments
Comment #2
tatarbjThere is three interfaces what need to be renamed because those don't have the word 'Interface' in their names:
Comment #3
attiks CreditAttribution: attiks commentedComment #4
attiks CreditAttribution: attiks at Attiks commentedBest to file separate issues for those, because it will have a huge impact.
Comment #5
DuaelFrAs agreed between the mentors at Drupalcon, according to issues to avoid for novices, I am untagging this issue as "Beginner". This issue contains changes across a very wide range of files and might create too many other patches to need to be rerolled at this particular time. This patch has an automated way to be rerolled later so better to implement it after Drupalcon.
Comment #6
pfrenssenComment #8
andypostnothing to review
Comment #9
pfrenssenComment #10
pfrenssenComment #11
andypostComment #12
pfrenssenComment #13
Mile23Moving to 8.2.x since this is a code change.
This patch fixes the interface names in
ArgumentsResolverTest.php
.It also fixes
DatabaseException
by renaming it toDatabaseExceptionInterface
, which you may now commence arguing over. :-)Comment #14
klausiWe cannot rename interfaces in the Drupal 8 cycle since that would be an API break.
What would be a good way to duplicate the exception to DatabaseExceptionInterface and maintain API compatibility somehow?
interface DatabaseExceptionInterface extends DatabaseException
maybe? What are scenarios where this would break type compatibility?
Comment #15
klausiIn the meantime I think we should have // @codingStandardsIgnoreStart + End comments in those files and proceed.
Comment #16
Mile23So then the solution at the intersection of not breaking the API vs having coding standards is:
DatabaseException
now for removal in Drupal 9.DatabaseExceptionInterface
which extendsDatabaseException
.DatabaseException
usages in core withDatabaseExceptionInterface
.Comment #17
tstoecklerI agree with #16 except for
Let's do it the other way around, so the BC-removal is just deleting the old one. AFAIK that's also how we've handled this elsewhere.
Comment #18
klausi@tstoeckler: that is not possible. If contrib tries to catch the old DatabaseException then the new DatabaseExeceptionInterface will escape that clause because the interface is not a subtype of DatabaseException.
The only way is that the new interface extends the old one, not the other way around.
And even then I'm sceptical if we should really do this and if there are other API breaking special cases that we are not thinking of right now.
As a first step I would prefer to simply mark DatabaseException as // @codingStandardsIgnoreStart and leave a comment that it should be renamed in Drupal 9.
Comment #19
tstoecklerAhh you're totally right, sorry.
Comment #20
Mile23We have a formalized way of doing that, and it's called @deprecated. :-)
This patch:
As always: If someone wants to split this up into separate issues that's fine.
Comment #21
dawehnerThe patch fixes all instances of the new rule.
So I'm wondering about the potential BC break. What happens if you have an exception which throws something that implements DatabaseException, but not DatabaseExceptionInterface. Wouldn't that be a break here? Can't we keep the old exception here for less potential BC breaks?
Comment #22
klausithis shows the danger of the patch: code that throws DatabaseException will not be caught here.
So I think we should not do this and only ignore the coding standards on the file, leave a @todo to rename it in Drupal 9 and be done with it.
Comment #23
dawehnerSo yeah, let's ignore the
DatabaseException
file using@codingStandardsIgnoreFile
Comment #26
ZeiP CreditAttribution: ZeiP at Citrus Solutions Oy commentedAttached is a patch to enable the interfacename sniff, fix a few misnamed interfaces and exclude the DatabaseException from the check (but keeping the deprecated tag.)
Comment #27
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedLooks close to complete, good idea to preserve the @deprecated. I'd like to see the start/end tags changed to just a single "@codingStandardsIgnoreLine". The deprecation notice also should be changed from 8.2.x to 8.4.x now.
Comment #28
Antti J. Salminen CreditAttribution: Antti J. Salminen commentedForgot the status change...
Comment #29
dimaro CreditAttribution: dimaro at La Drupalera by Emergya commentedI think there is a big difference between patches #20 and #26. Please if I am wrong let me know.
Rerolled 8.2.x again and rebase against 8.4.x. Also I try to fix #27.
@dawehner I keep the new exceptions, maybe I have to change to preserve the old exceptions as you said at #21?
Comment #30
dimaro CreditAttribution: dimaro at La Drupalera by Emergya commentedComment #31
ZeiP CreditAttribution: ZeiP at Citrus Solutions Oy commented@dimaro, of course there's a big difference between the two patches, because in #22 it was suggested (and in #23 seconded) that the renaming should not take place. I'm not sure which one of these is the correct solution, but as that is the last suggested fix, you should probably comment on the suggestion if you think it's incorrect.
Since the patch in #29 is against the last suggestion, reverting back to Needs work.
Comment #32
dawehnerCan we add a
@trigger_error
here, see https://www.drupal.org/core/deprecationThis is really tricky. In case some existing code checked for
$exception instanceof DatabaseException
this will no longer work. To be honest ignoring CS errors here feels like the better solution, better as in not breaking potential existing code.Comment #33
pk188 CreditAttribution: pk188 at OpenSense Labs commented#29 failed to apply. Re rolled and fixed #32.
Comment #35
pk188 CreditAttribution: pk188 at OpenSense Labs commentedComment #36
kporras07 CreditAttribution: kporras07 at Manatí commentedHi,
I rerolled the patch against last dev version.
Comment #37
Antti J. Salminen CreditAttribution: Antti J. Salminen as a volunteer commentedI don't think we can handle this by ignoring the coding standards and delaying the potentially breaking changes anymore as was suggested in #22 because even future major versions of Drupal now won't be able to make backwards-compatibility breaking changes for features that weren't already deprecated. Thus from the options that have been suggested so far the only possible one I can see for making the change is deprecating the interface and also introducing the new one you are supposed to be using afterwards even if it has the potential of breaking code that uses "instanceof".
Maybe we need some more discussion on whether or not this is the case and if we should go forward with making the risky changes. If this is to be deprecated it will need a change record also.
Comment #38
Mile23OK, let's do the following in this issue:
DatabaseException
to ignore CS.Then in followups, do the deprecation process:
DatabaseException
as deprecated.Comment #39
ZeiP CreditAttribution: ZeiP at Citrus Solutions Oy commentedAttached is a patch containing just the changes for the new issue scope.
Comment #40
ZeiP CreditAttribution: ZeiP at Citrus Solutions Oy commentedWhoops, adding the ArgumentsResolverTest.php fixes back.
Comment #42
mfernea CreditAttribution: mfernea at AmeXio commentedHere's a re-roll of #40. I still think that
// @codingStandardsIgnoreLine
is better than// @codingStandardsIgnoreFile
.Comment #43
mfernea CreditAttribution: mfernea at AmeXio commentedComment #45
mfernea CreditAttribution: mfernea at AmeXio commentedAnother re-roll.
Comment #46
andriyun CreditAttribution: andriyun at Skilld, Drupal Ukraine Community commentedLGTM
Comment #47
andriyun CreditAttribution: andriyun at Skilld, Drupal Ukraine Community commentedComment #51
catchCommitted/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!