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.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

attiks created an issue. See original summary.

tatarbj’s picture

Status: Active » Needs review

There is three interfaces what need to be renamed because those don't have the word 'Interface' in their names:

  • DatabaseException -> when we rename it it will generate problems, so need to be refactored as well, as i saw there are plenty of places where this interface is used, don't know can we do it before RC1.
  • Two test interface under \Drupal\Tests\Component\Utility\ArgumentsResolverTest which are very easy to rename it
attiks’s picture

Issue summary: View changes
attiks’s picture

Best to file separate issues for those, because it will have a huge impact.

DuaelFr’s picture

Issue tags: -Novice

As 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.

pfrenssen’s picture

Issue summary: View changes

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

andypost’s picture

Status: Needs review » Active

nothing to review

pfrenssen’s picture

Issue summary: View changes
pfrenssen’s picture

Issue summary: View changes
andypost’s picture

FILE: ...ypost/drupal/core/lib/Drupal/Core/Database/DatabaseException.php
----------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
----------------------------------------------------------------------
 13 | WARNING | Interface names should always have the suffix
    |         | "Interface"
----------------------------------------------------------------------


FILE: ...e/tests/Drupal/Tests/Component/Utility/ArgumentsResolverTest.php
----------------------------------------------------------------------
FOUND 0 ERRORS AND 2 WARNINGS AFFECTING 2 LINES
----------------------------------------------------------------------
 207 | WARNING | Interface names should always have the suffix
     |         | "Interface"
 213 | WARNING | Interface names should always have the suffix
     |         | "Interface"
----------------------------------------------------------------------
pfrenssen’s picture

Issue summary: View changes
Mile23’s picture

Version: 8.1.x-dev » 8.2.x-dev
Status: Active » Needs review
FileSize
19.55 KB

Moving 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 to DatabaseExceptionInterface, which you may now commence arguing over. :-)

klausi’s picture

Status: Needs review » Needs work

We 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?

klausi’s picture

In the meantime I think we should have // @codingStandardsIgnoreStart + End comments in those files and proceed.

Mile23’s picture

So then the solution at the intersection of not breaking the API vs having coding standards is:

  • Deprecate DatabaseException now for removal in Drupal 9.
  • Add standards-ignore tags around it.
  • Create DatabaseExceptionInterface which extends DatabaseException.
  • Replace DatabaseException usages in core with DatabaseExceptionInterface.
tstoeckler’s picture

I agree with #16 except for

Create DatabaseExceptionInterface which extends DatabaseException.

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.

klausi’s picture

@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.

tstoeckler’s picture

Ahh you're totally right, sorry.

Mile23’s picture

Title: Fix 'Drupal.Classes.InterfaceName' coding standard » Deprecate DatabaseException to fix 'Drupal.Classes.InterfaceName' coding standard errors
Status: Needs work » Needs review
FileSize
20.6 KB
1.75 KB

leave a comment that it should be renamed in Drupal 9.

We have a formalized way of doing that, and it's called @deprecated. :-)

This patch:

  • Deprecates DatabaseException for removal before Drupal 9.0.0.
  • Adds DatabaseExceptionInterface which extends DatabaseException.
  • Changes all usages of DatabaseException to DatabaseExceptionInterface.
  • Fixes interface names in ArgumentsResolverTest.php
  • Adds the coding standard to phpcs.xml.dist

As always: If someone wants to split this up into separate issues that's fine.

dawehner’s picture

The patch fixes all instances of the new rule.

+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -410,7 +410,7 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
     }
-    catch (DatabaseException $e) {
+    catch (DatabaseExceptionInterface $e) {
       // This may happen when changing field storage schema, since we are not

+++ b/core/modules/dblog/src/Logger/DbLog.php
@@ -91,7 +91,7 @@ public function log($level, $message, array $context = array()) {
         // Only handle database related exceptions.
-        ($e instanceof DatabaseException || $e instanceof \PDOException) &&
+        ($e instanceof DatabaseExceptionInterface || $e instanceof \PDOException) &&
         // Avoid an endless loop of re-write attempts.

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?

klausi’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
@@ -80,7 +80,7 @@ public function query($query, array $args = array(), $options = array()) {
-    catch (DatabaseException $e) {
+    catch (DatabaseExceptionInterface $e) {

this 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.

dawehner’s picture

Issue tags: +Novice

So yeah, let's ignore the DatabaseException file using @codingStandardsIgnoreFile

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

ZeiP’s picture

Status: Needs work » Needs review
FileSize
3.58 KB

Attached 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.)

Antti J. Salminen’s picture

Looks 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.

Antti J. Salminen’s picture

Status: Needs review » Needs work

Forgot the status change...

dimaro’s picture

I 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?

dimaro’s picture

Status: Needs work » Needs review
ZeiP’s picture

Status: Needs review » Needs work

@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.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Database/DatabaseException.php
    @@ -9,5 +10,8 @@
    + *
    + * @deprecated in Drupal 8.4.x for removal before Drupal 9.0.0. Use
    + * Drupal\Core\Database\DatabaseExceptionInterface instead.
      */
    -interface DatabaseException { }
    +interface DatabaseException {}
    

    Can we add a @trigger_error here, see https://www.drupal.org/core/deprecation

  2. +++ b/core/lib/Drupal/Core/Database/DatabaseException.php
    @@ -9,5 +10,8 @@
    -interface DatabaseException { }
    +interface DatabaseException {}
    

    This 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.

pk188’s picture

Status: Needs work » Needs review
FileSize
20.22 KB

#29 failed to apply. Re rolled and fixed #32.

Status: Needs review » Needs work

The last submitted patch, 33: preview-2572631-33.patch, failed testing. View results

pk188’s picture

Status: Needs work » Needs review
FileSize
21.18 KB
kporras07’s picture

Hi,
I rerolled the patch against last dev version.

Antti J. Salminen’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record

I 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.

Mile23’s picture

Title: Deprecate DatabaseException to fix 'Drupal.Classes.InterfaceName' coding standard errors » Fix 'Drupal.Classes.InterfaceName' coding standard
Issue tags: -Needs change record +Needs followup

OK, let's do the following in this issue:

  • Add the sniffer interface rule to phpcs.xml.dist.
  • Mark DatabaseException to ignore CS.

Then in followups, do the deprecation process:

  • Mark DatabaseException as deprecated.
  • Create alternative interface.
  • Sweep up after.
ZeiP’s picture

Status: Needs work » Needs review
FileSize
1.21 KB

Attached is a patch containing just the changes for the new issue scope.

ZeiP’s picture

Whoops, adding the ArgumentsResolverTest.php fixes back.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mfernea’s picture

Here's a re-roll of #40. I still think that // @codingStandardsIgnoreLine is better than // @codingStandardsIgnoreFile.

mfernea’s picture

Status: Needs review » Needs work

The last submitted patch, 43: 2572631-40_phpcs_interfacename.patch, failed testing. View results

mfernea’s picture

Status: Needs work » Needs review
FileSize
3.66 KB

Another re-roll.

andriyun’s picture

Status: Needs review » Reviewed & tested by the community

LGTM

  • catch committed e630c78 on 8.5.x
    Issue #2572631 by ZeiP, Mile23, mfernea, pk188, dimaro, kporras07,...

  • catch committed 0a7d9f8 on 8.4.x
    Issue #2572631 by ZeiP, Mile23, mfernea, pk188, dimaro, kporras07,...

catch credited catch.

catch’s picture

Version: 8.5.x-dev » 8.4.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.