WordPress-Coding-Standards: Incorrect indentation using Atom Beautify with PHP_CodeSniffer and WordPress-Coding-Standards

Bug Description

I’m developing a WordPress theme, as a editor I use Atom with Atom Beautify. I have also installed as default beautifier “PHP_CodeSniffer” with the “WordPress-Coding-Standards “as ruleset (WordPress-VIP,WordPress,WordPress-Extra,WordPress-Docs,WordPress-Core).

Both, the “PHP_CodeSniffer” beautifier and the “WordPress-Coding-Standards” configuration file, work correctly, but they create files that are a mess, and in which some spaces become tabs instead others remain such spaces. Furthermore, the structure of the file itself is also incorrect (at least for what I would like to get).

I don’t know if the code shows the problem so I’m going to integrate the issue also with images.

Input Before Beautification

This is what the code looked like before. As you can see in the file, spaces are used and there is an initial indent that should not exist.

1

Expected Output

The beautified code should have looked like this.

2

Actual Output (image)

The beautified code actually looked like this. This is what happens after starting Atom Beautify. The initial indent has not been corrected, the general structure is incorrect, and there are both tabs and spaces.

3

Actual Output (code)

<?php
/**
 * The template for displaying 404 pages (not found)
 *
 * @link https://codex.wordpress.org/Creating_an_Error_404_Page
 *
 * @package help
 */

get_header(); ?>

  <!-- Main -->
  <main>
	<section id="error">
	  <div class="one">
		<div class="two">
		  <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
		</div>

		<div class="two">
		  <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
		</div>
	  </div>

	  <div class="one">
		<div class="two">
		  <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
		</div>
	  </div>
	</section>
  </main>

<?php get_footer(); ?>

Error Code

Environment

Question Answer
PHP version 5.6.30
PHP_CodeSniffer version 3.2.3
WPCS version 0.14.1 (I took this information from the file “CHANGELOG.md”)
WPCS install type Standalone
IDE (if relevant) Atom 1.38.2

Additional Context (optional)

Here instead you find my personal configuration file phpcs.xml.dist:

<?xml version="1.0" encoding="UTF-8"?>

<ruleset name="WordPress Custom">
  <description>Custom PHP_CodeSniffer ruleset</description>

  <arg name="tab-width" value="2"/>

  <rule ref="WordPress">
    <exclude name="Generic.WhiteSpace.DisallowSpaceIndent"/>
  </rule>

  <rule ref="Generic.WhiteSpace.ScopeIndent">
    <properties>
      <property name="indent" value="2"/>
      <property name="exact" value="true"/>
      <property name="tabIndent" value="false"/>
    </properties>
  </rule>

  <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>

  <rule ref="PEAR.Functions.FunctionCallSignature">
    <properties>
      <property name="indent" value="2"/>
    </properties>
  </rule>
</ruleset>

Tested Against develop branch?

  • I have verified the issue still exists in the develop branch of WPCS.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Comments: 19 (8 by maintainers)

Most upvoted comments

@vizzale Ok, well I’ve ran some tests and I’ll try and explain what is happening, but am a bit short of time, so if anything is unclear, please ask about it and I’ll try and explain better when I have a little more time.

  1. The WordPress-Core ruleset, which is included in the WordPress ruleset sets a number of properties for the Generic.WhiteSpace.ScopeIndent sniff. You overrule most in your custom ruleset, but not all. The ignoreIndentationTokens property set by WPCS - which you don’t overrule - actually excludes T_INLINE_HTML from being handled by the scope indent sniff which is why WPCS won’t touch the indentation of the HTML. If you want to change this, you need to overrule that property too in your custom ruleset, like so:
    <rule ref="Generic.WhiteSpace.ScopeIndent">
      <properties>
        <property name="indent" value="2"/>
        <property name="exact" value="true"/>
        <property name="tabIndent" value="false"/>
        <property name="ignoreIndentationTokens" type="array">
          <element value="T_HEREDOC"/>
          <element value="T_NOWDOC"/>
        </property>
      </properties>
    </rule>
    
  2. To make the ruleset completely 2-space-based, you will also need to set another property:
    <rule ref="PSR2.ControlStructures.SwitchDeclaration">
      <properties>
        <property name="indent" value="2"/>
      </properties>
    </rule>
    
    That sniff has a default of 4 spaces which WPCS doesn’t overrule as 4 spaces suits WPCS just fine, but for your 2-space default, you would want to change this.

Now the ruleset is set up properly for 2-space indents, you’ll find that the HTML indentation is still not being fixed (correctly).

The short of that, is that PHPCS only deals with PHP, JS and CSS. PHPCS does not parse HTML, nor does it reformat it. You may now say, “Hang on, but it did make changes initially…”. Yes, it did, but it only interchanged tabs vs spaces, it didn’t make any changes to the actual indentation as it has no concept of, nor keeps track of HTML open/close tags.

So what I suspect is happening here, is that Atom runs PHPCS and then runs some Atom-native HTML indentation formatting over the code.

To test this:

  1. Adjust your custom ruleset with the suggestions I made above.
  2. Using the sample code below (yes, I know horribly formatted, but that’s the point), run phpcs --standard=phpcs.xml.dist ./testfile.php over the test file (pointing to your custom ruleset as adjusted). You will see errors/warnings about precision alignment and end of line whitespace, but no errors reported about (scope) indentation.
  3. Next run phpcbf --standard=phpcs.xml.dist ./testfile.php --suffix=.fixed over the same test file (pointing to your custom ruleset as adjusted). This will output the result to a separate file with a .fixed suffix. Take note that the HTML indentation in the .fixed file is unchanged when compared to the original .php file.
  4. Now run the Atom Beautifier over the same code in testfile.php (which should still be unchanged up to now).
  5. Compare the testfile.php.fixed file (the PHPCBF output) with the file beautified by Atom to see the difference. Any differences you see are Atom doing something extra outside of the scope/rules set by WPCS.

I hope that explains it.

You may then say, but shouldn’t WPCS deal with HTML indentation too ? And the answer would be “no”. Your template is quite “clean” in that it has the open/close tags for each HTML element in the same block of inline HTML. For most PHP code, this is not the case. The open tags may be in one method and the close tags in another. There can be blocks of PHP in between blocks of inline HTML, open and close tags may be in different files (think header.php/footer.php) etc etc. In short, it would be impossible to reliably keep track of the HTML indentation needed, so there is no intention to add any checks of that kind to WPCS. (which is part of the reason why WPCS excludes inline HTML from the scope indent sniff in the first place)

Probably not the answer you were hoping for, but I hope it helps to understand how these things work anyhow.


Horribly formatted HTML sample code
<?php
/**
 * The template for displaying 404 pages (not found)
 *
 * @link https://codex.wordpress.org/Creating_an_Error_404_Page
 *
 * @package help
 */
 
get_header(); ?>

 <!-- Main -->
 <main>
                       <section id="error">
<div class="one">
        <div class="two">
   <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                     </div>

             <div class="two">
        <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
                                        </div>
       </div>

     <div class="one">
                  <div class="two">
         <p class="three">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
     </div>
   </div>
  </section>
</main>
 
<?php get_footer(); ?>