Setting the standard

Fragment block

of headless adventure about ReactJS, more
Default blog

Make sure that IDE respects the coding standards

Shame on me

I really love writing blogs. Some blog are nice to write because I get the chance to really research some less know problems and find a great deal of satifcation from getting it al together and getting a real deep understanding of some tool or API I am using. Other blogs do not have such a high goal, and this is one of them. This blog is not written for some deep longing to knowledge, this blog is written to finally document something that I only do whenever I get a new development environment. The shame is on me because I did not do this the last time I got a new one. So the main audience of this blog is me. But maybe this will help some of you too, and in any case it will be a reminder to myself for when I get my next new development laptop.

Coding standards

Should I explain why following the codings standards is not some kind of boring red tape guideline but actually is something that will give you a lot of pleasure and fulfilment? I will only say this: imagine you take over an existing project and the previous developer did not added comment blocks like: /** * Implements hook_search_api_query_alter(). */ Of course you could search on "_search_api_query_alter", but what if you are looking for that one "hook_view" that you think you must change to implement some new functionality: even a bare Drupal 7.59 code base will give you 643 hits, against 2 (correct) hits for "Implements hook_view()". So if you do not think coding standards are useful, just stop reading here: you will need the time to browse through your hundreds of useless search hits. Of course most Drupal developers will use the "implements"-code block, and adhering to all Drupal coding standards can be quite a challenge if you have to do so by hand. But luckily, to paraphrase a well-known Drupal quote: there is a tool for that!

Definition of the standard

I will not describe the full Drupal coding standard here, it is well defined and described on API documentation and comment standards. But there are a number of points that I will described here in more detail because these are the things that I always have trouble remembering (I told you I myself am the primary audience for this blog).

Function parameters

According to the Drupal Coding Standards (or DCS for short) the correct way to define parameters in a function docblock is: * @param string $mail * The email address. The description can be longer if necessary, and if so, * you can wrap it to another line. * @param string $from * (optional) The email address to send the mail from, if different from * the site-wide address. So, I wonder, why I almost always type: * @param string $mail The email address. The description can be longer if necessary, and if so, * you can wrap it to another line. * @param string $from (optional) The email address to send the mail from, if different from the site-wide address. Which, by the way, did not totally spring from my twisted mind: it is the coding standard used in Java-programming and, I think, used to be the standard for C++ development when I learned to program years and years ago. But, since we agreed too use the Drupal coding standards, it should be like the first example above. Oh, and my unspoken argument "Well, at least I added some documentation" is not something I would dare to say out loud in any company (and should be erased from my mind as soon as possible).

Hook implementations

A very common sight when browsing through Drupal code is: /** * Implements hook_help(). */ function blog_help($section) { (...) So this is quite fine and follows the coding standards, does it not? Well, yes, is does follow the coding standards, but whenever I have to take over any project I wish that the previous coder has used the following format: /** * Implements hook_help(). * * Reason WHY I have implemented this hook! * */ function blog_help($section) { (...) But wait, you could say: that is not the DCS! But, actually, it is. If you read the paragraph about Hook implementation is says: if appropriate, you can add additional details pertinent to the particular hook implementation in a separate paragraph after the summary. I take that at the time the previous coder implemented the hook there was a clear reason to do so, and it surely is interesting to me as the new coder to understand why this hook was implemented.

Installing code sniffer and the Drupal Coding Standards

There is good documentation about how to install Code Sniffer on Installing Coder Sniffer. If you are using composer to install code sniffer the steps to follow are as described below. See the reference link Composer: Installation - Linux / Unix / OSX if you have not already installed composer. I will be installing the packages in the global directory of composer, to see which directory this is and which packages are already present use: > composer global show To install the DCS and all of its dependencies simply use: > composer global require drupal/coder On my computer the output of this is: Changed current directory to /home/louis/.config/composer Using version ^8.2 for drupal/coder ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 3 installs, 0 updates, 0 removals - Installing symfony/yaml (v4.0.9): Downloading (100%) - Installing squizlabs/php_codesniffer (2.9.1): Downloading (100%) - Installing drupal/coder (8.2.12): Cloning 984c54a7b1 from cache Writing lock file Generating autoload files To make it easier to register DCS for use with code sniffer, we also install the "dealerdirect/phpcodesniffer-composer-installer"-package: > composer global require dealerdirect/phpcodesniffer-composer-installer Output: Changed current directory to /home/louis/.config/composer Using version ^0.4.4 for dealerdirect/phpcodesniffer-composer-installer ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 1 install, 0 updates, 0 removals - Installing dealerdirect/phpcodesniffer-composer-installer (v0.4.4): Downloading (100%) dealerdirect/phpcodesniffer-composer-installer suggests installing dealerdirect/qa-tools (All the PHP QA tools you"ll need) Writing lock file Generating autoload files PHP CodeSniffer Config installed_paths set to /home/louis/.config/composer/vendor/drupal/coder/coder_sniffer To list the install standards use: > phpcs -i The installed coding standards are Squiz, MySource, PSR2, Zend, PSR1, PEAR, PHPCS, Drupal and DrupalPractice So now we are ready to roll!

Configuring PHPStorm

To configure PHP Storm for using code sniffer, open a project and go to the menu entry (this may vary in different version of PHPStorm, I am using version "2018.2.1"): File | Settings Then click open: Languages & Frameworks | PHP | Code Sniffer First you have to tell PHPStorm the location of lthe Code Sniffer executable, which can be found by opening a command prompt and typing: > which phpcs /home/louis/.config/composer/vendor/bin/phpcs Of course the exact location will depend on your local environment. Enter the found path by clicking on the "..."-button next to the field "PHP Code Sniffer (phpcs) path:" and click the validate button to make sure the path is correct. In my case clicking the "Validate"-button gave: OK, PHP_CodeSniffer version 2.9.1 (stable) by Squiz (http://www.squiz.net) Next make sure the coding standards are used by going to Files | Settings | Editor | Inspections | PHP | PHP Code Sniffer validation Make sure the checkbox behind it is set. To set the correct standard click on the round-arrows-button next to "Coding standard:" in the right pane and open the select. You should now see the same standards as given by the command "phpcs -i" above. Choose "Drupal" as standard and click "Ok". Now open a Drupal module file (e.g. core/modules/path/path.module) and watch for warnings and errors in the right scrollbar. You will notice that even a core-module still will show some warnings, but no errors. To check everything is working lets add a small custom module. In a shell go to the "modules/custom" module of your Drupal site and type: > drush gen module and answer the questions. This will generate an empty module-skeleton for you. I named mine "codesniffertest". Open the .module file and add the following code: function codesniffertestTester($first, $second){ return $first . $second; } You will the first line will get a red underline and if you hover the mouse over it, the errors that code sniffer found will be shown. So now you know what is wrong, but how to fix it?

Command line tool

Of course you want the ease and functions of PHPStorm, but in this case the command line offers a number of options that will make you life easier. Lets change directory to the directory of the custom module we created above and check the .module file: > phpcs codesniffertest.module --standard=Drupal FILE: ...upal-8.5.3/modules/custom/codesniffertest/codesniffertest.module ---------------------------------------------------------------------- FOUND 4 ERRORS AFFECTING 2 LINES ---------------------------------------------------------------------- 12 | ERROR | [x] Missing function doc comment 12 | ERROR | [ ] Invalid function name, expected | | codesniffertest_tester but found | | codesniffertestTester 12 | ERROR | [x] Expected 1 space before opening brace; found 0 13 | ERROR | [x] Line indented incorrectly; expected 2 spaces, found | | 0 ---------------------------------------------------------------------- PHPCBF CAN FIX THE 3 MARKED SNIFF VIOLATIONS AUTOMATICALLY ---------------------------------------------------------------------- Time: 44ms; Memory: 6Mb We are getting some valuable information in the sentence PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY (why phpcs is shouting at us will remain a mystery...). So lets try this and type: > phpcbf codesniffertest.module --standard=Drupal Changing into directory /home/louis/Sources/drupal-8.5.3/modules/custom/codesniffertest Processing codesniffertest.module [PHP => 60 tokens in 14 lines]... DONE in 5ms (3 fixable violations) => Fixing file: 0/3 violations remaining [made 2 passes]... DONE in 16ms Patched 1 file Time: 72ms; Memory: 6Mb If we now look at the code in the file it will be changed to: /** * */ function codesniffertestTester($first, $second) { return $first . $second; } Better, but not perfect. Change the code to: /** * @param string $first * first parameter * @param string $second * second parameter * @return string * return the compund string */ function codesniffertest_tester($first, $second) { return $first . $second; } Run phpcs again in the shell: > phpcs codesniffertest.module --standard=Drupal FILE: ...upal-8.5.3/modules/custom/codesniffertest/codesniffertest.module ---------------------------------------------------------------------- FOUND 6 ERRORS AFFECTING 4 LINES ---------------------------------------------------------------------- 12 | ERROR | [ ] Missing short description in doc comment 14 | ERROR | [ ] Parameter comment must start with a capital letter 14 | ERROR | [x] Parameter comment must end with a full stop 16 | ERROR | [ ] Parameter comment must start with a capital letter 16 | ERROR | [x] Parameter comment must end with a full stop 18 | ERROR | [x] Return comment indentation must be 3 spaces, found | | 4 spaces ---------------------------------------------------------------------- PHPCBF CAN FIX THE 3 MARKED SNIFF VIOLATIONS AUTOMATICALLY ---------------------------------------------------------------------- Time: 46ms; Memory: 6Mb And run phpcbf again: > phpcbf codesniffertest.module --standard=Drupal Changing into directory /home/louis/Sources/drupal-8.5.3/modules/custom/codesniffertest Processing codesniffertest.module [PHP => 103 tokens in 22 lines]... DONE in 7ms (3 fixable violations) => Fixing file: 0/3 violations remaining [made 2 passes]... DONE in 22ms Patched 1 file Time: 79ms; Memory: 6Mb The resulting code will be: /** * @param string $first * first parameter. * @param string $second * second parameter. * @return string * return the compund string */ function codesniffertest_tester($first, $second) { return $first . $second; } Still not flawless, to get perfection edit the code manually to: /** * Test strings by concatenation. * * @param string $first * First parameter. * @param string $second * Second parameter. * * @return string * return the compund string */ function codesniffertest_tester($first, $second) { return $first . $second; } Finally this will satisfy the DCS completely and so we are ready to move on to our next piece of code. By the way, to check a complete directory and its sub-directories use wildcards like: > phpcs * --standard=Drupal

Conclusion

To be honest, I expected a bit more of the "phpcbf" utility. There probably is a good reason why an error like "Parameter comment must end with a full stop" can be automatically fixed and an error like "Parameter comment must start with a capital letter" not. Or why correcting the error "Missing function doc comment" only places an empty doc-block. If you ask me, the parameters of the function can be found quite easily and should be added to the doc-block. Of course this would mean that there will be incomplete parameter comments since the utility of course can not add a real description, but at least this would give the coder some kind of stub to fill. Luckily PHPStorm can add a doc-block, but this is not coded according to DCS and you have to do this manually file by file. But one should keep in mind that using tools like Code Sniffer ideally should be done right from the beginning of a project. When taking over a project in which Code Sniffer was not used right from the start "phpcbf" can help to fix a number of problems, but it will never result automatically in code that will completly adhere to the Drupal Coding Standards.

References

API documentation and comment standards API documentation and comment standards This page covers: How to write documentation for PHP code so that the API module can successfully parse it and display it on http://api.drupal.org (and other similar sites). The Drupal project"s standards for API documentation and comments in PHP code, whose purpose is that the API module can parse/display the documentation, that programmers looking at the PHP files can read/understand the documentation, and that integrated developer environments (IDEs) can work successfully with the code and documentation. A reference to the tags used in this documentation. PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards. PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards. Installing Coder Sniffer Overview PHP_CodeSniffer is a library that tokenises PHP, JavaScript and CSS files and detects violations of a defined set of coding standards. It works with Drupal 6, 7, or 8. (After you install Coder Sniffer, see command line options for running it here.) Composer: Installation - Linux / Unix / OSX