Php_codesniffer: Question: could PHPCS be extended to sniff Laravel's blade syntax?

Created on 16 Jan 2015  ·  12Comments  ·  Source: squizlabs/PHP_CodeSniffer

  • Not an issue but a question regarding the appropriateness and extendibility of PHPCS and running its rules such as ControlSignatureSniff against Laravel's blade syntax?

Most helpful comment

In favor of Laravel Community, I think this feature is important. Hope it'll be implemented soon.

All 12 comments

  • Not really - I already have a custom ruleset but wondered how I could utilise PHPCS to cover inconsistencies in blade syntax using rules like ControlSignatureSniff

I dont know blade. Can you provide an code example of the inconsistencies?

Scroll down to If Statements to see an example of blade syntax. More interested in spacing/layout issues which in PHP can be corrected with sniffs like ControlSignatureSniff.

I can expand if you need me to

Do you know how PHPCS tokenizes the @ keywords? You can figure this out when you check the code with php -vv .... I assume its just string, than you need to implement everything by you own.

  • Listen to every string
  • Check if its one of the keywords (if, else, elseif, ...)
  • Implement the violation check

For me it looks like the file is a mixture of custom keywords and HTML. Since I never did this, I have no idea if my approach will work.

Thanks

Could you provide a snippet of a custom rule that would achieve this to get me started?

Its tokenized to T_INLINE_HTML.

cat test.blade.php                                                                                                                               ⏎
<html>
    <body>
        @section('sidebar')
            This is the master sidebar.
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif

@unless (Auth::check())
    You are not signed in.
@endunless
$ phpcs -vv --standard=Generic --sniffs=Generic.Strings.UnnecessaryStringConcatSniff /tmp/test.blade.php

Process token 0 on line 1 [col:1;len:6;lvl:0;]: T_INLINE_HTML => <html>\n
    Process token 1 on line 2 [col:1;len:10;lvl:0;]: T_INLINE_HTML => ····<body>\n
    Process token 2 on line 3 [col:1;len:27;lvl:0;]: T_INLINE_HTML => ········@section('sidebar')\n
    Process token 3 on line 4 [col:1;len:39;lvl:0;]: T_INLINE_HTML => ············This·is·the·master·sidebar.\n
    Process token 4 on line 5 [col:1;len:13;lvl:0;]: T_INLINE_HTML => ········@show\n
    Process token 5 on line 6 [col:1;len:0;lvl:0;]: T_INLINE_HTML => \n
    Process token 6 on line 7 [col:1;len:31;lvl:0;]: T_INLINE_HTML => ········<div·class="container">\n
    Process token 7 on line 8 [col:1;len:29;lvl:0;]: T_INLINE_HTML => ············@yield('content')\n
    Process token 8 on line 9 [col:1;len:14;lvl:0;]: T_INLINE_HTML => ········</div>\n
    Process token 9 on line 10 [col:1;len:11;lvl:0;]: T_INLINE_HTML => ····</body>\n
    Process token 10 on line 11 [col:1;len:7;lvl:0;]: T_INLINE_HTML => </html>\n
    Process token 11 on line 12 [col:1;len:0;lvl:0;]: T_INLINE_HTML => \n
    Process token 12 on line 13 [col:1;len:27;lvl:0;]: T_INLINE_HTML => @if·(count($records)·===·1)\n
    Process token 13 on line 14 [col:1;len:22;lvl:0;]: T_INLINE_HTML => ····I·have·one·record!\n
    Process token 14 on line 15 [col:1;len:29;lvl:0;]: T_INLINE_HTML => @elseif·(count($records)·>·1)\n
    Process token 15 on line 16 [col:1;len:28;lvl:0;]: T_INLINE_HTML => ····I·have·multiple·records!\n
    Process token 16 on line 17 [col:1;len:5;lvl:0;]: T_INLINE_HTML => @else\n
    Process token 17 on line 18 [col:1;len:29;lvl:0;]: T_INLINE_HTML => ····I·don't·have·any·records!\n
    Process token 18 on line 19 [col:1;len:6;lvl:0;]: T_INLINE_HTML => @endif\n
    Process token 19 on line 20 [col:1;len:0;lvl:0;]: T_INLINE_HTML => \n
    Process token 20 on line 21 [col:1;len:23;lvl:0;]: T_INLINE_HTML => @unless·(Auth::check())\n
    Process token 21 on line 22 [col:1;len:26;lvl:0;]: T_INLINE_HTML => ····You·are·not·signed·in.\n
    Process token 22 on line 23 [col:1;len:10;lvl:0;]: T_INLINE_HTML => @endunless\n

So you need to register that token:

public function register()
{
    return array(
        T_INLINE_HTML,    
    );
}//end register()

...

public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) {
    $tokens = $phpcsFile->getTokens();
    if ($tokens[$stackPtr]['content'] === '@if') {
        $endif = $phpcsFile->findNext(T_INLINE_HTML, $stackPtr + 1, null, false, '@endif');    
        // Now you have the scope opener and scope closer for this condition and are able to move between them.
   }
}

This approach only works for conditons which are not nested. Than you must implement detection which @ endif belongs to which @if and so on. PHPCS provides all this by scope opener/closer properties for native supported Tokenizers.

Thanks for this - something I am going to have to invest some time in to cover what I would like to cover.

Do you think other Laravel users would find this useful?

For me it seems your need to put too much effort into such checks. If it would help you or you need to implement such checks to help your collegues to meet the company CGL, it might worth it. Otherwise I don't know. I have no idea how big the community is and if they are interested into that. Such checks are worth nothing if there is no mindset of code quality behind.

Only you knows if it makes sense, if ppls would use it and if you like to put effort into this. A good indication is always if there is a document which describes the rules, like a Coding Guideline. Otherwise its hard to justifiy your sniffs.

Doesn't sound right. The Blade template engine, used in Laravel, uses it's own PHP-alike syntax. Because of this it's unlikely that any tools that are doing PHP tokenizing like PHP_CodeSniffer will be able to parse and validate it properly.

I think you'd need a custom tokenizer to really make the blade syntax easy to write sniffs for. You can write sniffs to check for inline HTML and parse it out that way, but you'll probably need to tokenize it in there anyway to make the code easy to check.

But that tokenizer is not going to produce something that the existing sniffs would be able to check and fix. So you'd also need to write custom sniffs to check every part of the blade syntax.

It is entirely possible to fork PHPCS and make it do this. But it's a heap of work and not something I could help with given my current workload.

In favor of Laravel Community, I think this feature is important. Hope it'll be implemented soon.

Was this page helpful?
0 / 5 - 0 ratings