PHP_CodeSniffer
[ class tree: PHP_CodeSniffer ] [ index: PHP_CodeSniffer ] [ all elements ]

Source for file ControlStructureSpacingSniff.php

Documentation is available at ControlStructureSpacingSniff.php

  1. <?php
  2. /**
  3.  * Checks that control structures have the correct spacing around brackets.
  4.  *
  5.  * @author    Greg Sherwood <gsherwood@squiz.net>
  6.  * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
  7.  * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  8.  */
  9.  
  10. namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace;
  11.  
  12. use PHP_CodeSniffer\Sniffs\Sniff;
  13. use PHP_CodeSniffer\Files\File;
  14. use PHP_CodeSniffer\Util\Tokens;
  15.  
  16. class ControlStructureSpacingSniff implements Sniff
  17. {
  18.  
  19.     /**
  20.      * A list of tokenizers this sniff supports.
  21.      *
  22.      * @var array 
  23.      */
  24.     public $supportedTokenizers = array(
  25.                                    'PHP',
  26.                                    'JS',
  27.                                   );
  28.  
  29.  
  30.     /**
  31.      * Returns an array of tokens this test wants to listen for.
  32.      *
  33.      * @return array 
  34.      */
  35.     public function register()
  36.     {
  37.         return array(
  38.                 T_IF,
  39.                 T_WHILE,
  40.                 T_FOREACH,
  41.                 T_FOR,
  42.                 T_SWITCH,
  43.                 T_DO,
  44.                 T_ELSE,
  45.                 T_ELSEIF,
  46.                 T_TRY,
  47.                 T_CATCH,
  48.                );
  49.  
  50.     }//end register()
  51.  
  52.  
  53.     /**
  54.      * Processes this test, when one of its tokens is encountered.
  55.      *
  56.      * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
  57.      * @param int                         $stackPtr  The position of the current token
  58.      *                                                in the stack passed in $tokens.
  59.      *
  60.      * @return void 
  61.      */
  62.     public function process(File $phpcsFile$stackPtr)
  63.     {
  64.         $tokens $phpcsFile->getTokens();
  65.  
  66.         if (isset($tokens[$stackPtr]['parenthesis_opener']=== true
  67.             && isset($tokens[$stackPtr]['parenthesis_closer']=== true
  68.         {
  69.             $parenOpener $tokens[$stackPtr]['parenthesis_opener'];
  70.             $parenCloser $tokens[$stackPtr]['parenthesis_closer'];
  71.             if ($tokens[($parenOpener + 1)]['code'=== T_WHITESPACE{
  72.                 $gap $tokens[($parenOpener + 1)]['length'];
  73.  
  74.                 if ($gap === 0{
  75.                     $phpcsFile->recordMetric($stackPtr'Spaces after control structure open parenthesis''newline');
  76.                     $gap 'newline';
  77.                 else {
  78.                     $phpcsFile->recordMetric($stackPtr'Spaces after control structure open parenthesis'$gap);
  79.                 }
  80.  
  81.                 $error 'Expected 0 spaces after opening bracket; %s found';
  82.                 $data  = array($gap);
  83.                 $fix   $phpcsFile->addFixableError($error($parenOpener + 1)'SpacingAfterOpenBrace'$data);
  84.                 if ($fix === true{
  85.                     $phpcsFile->fixer->replaceToken(($parenOpener + 1)'');
  86.                 }
  87.             else {
  88.                 $phpcsFile->recordMetric($stackPtr'Spaces after control structure open parenthesis'0);
  89.             }
  90.  
  91.             if ($tokens[$parenOpener]['line'=== $tokens[$parenCloser]['line']
  92.                 && $tokens[($parenCloser - 1)]['code'=== T_WHITESPACE
  93.             {
  94.                 $gap   $tokens[($parenCloser - 1)]['length'];
  95.                 $error 'Expected 0 spaces before closing bracket; %s found';
  96.                 $data  = array($gap);
  97.                 $fix   $phpcsFile->addFixableError($error($parenCloser - 1)'SpaceBeforeCloseBrace'$data);
  98.                 if ($fix === true{
  99.                     $phpcsFile->fixer->replaceToken(($parenCloser - 1)'');
  100.                 }
  101.  
  102.                 if ($gap === 0{
  103.                     $phpcsFile->recordMetric($stackPtr'Spaces before control structure close parenthesis''newline');
  104.                 else {
  105.                     $phpcsFile->recordMetric($stackPtr'Spaces before control structure close parenthesis'$gap);
  106.                 }
  107.             else {
  108.                 $phpcsFile->recordMetric($stackPtr'Spaces before control structure close parenthesis'0);
  109.             }
  110.         }//end if
  111.  
  112.         if (isset($tokens[$stackPtr]['scope_closer']=== false{
  113.             return;
  114.         }
  115.  
  116.         $scopeOpener $tokens[$stackPtr]['scope_opener'];
  117.         $scopeCloser $tokens[$stackPtr]['scope_closer'];
  118.  
  119.         for ($firstContent ($scopeOpener + 1)$firstContent $phpcsFile->numTokens; $firstContent++{
  120.             $code $tokens[$firstContent]['code'];
  121.  
  122.             if ($code === T_WHITESPACE
  123.                 || ($code === T_INLINE_HTML
  124.                 && trim($tokens[$firstContent]['content']=== '')
  125.             {
  126.                 continue;
  127.             }
  128.  
  129.             // Skip all empty tokens on the same line as the opener.
  130.             if ($tokens[$firstContent]['line'=== $tokens[$scopeOpener]['line']
  131.                 && (isset(Tokens::$emptyTokens[$code]=== true
  132.                 || $code === T_CLOSE_TAG)
  133.             {
  134.                 continue;
  135.             }
  136.  
  137.             break;
  138.         }
  139.  
  140.         // We ignore spacing for some structures that tend to have their own rules.
  141.         $ignore = array(
  142.                    T_FUNCTION             => true,
  143.                    T_CLASS                => true,
  144.                    T_INTERFACE            => true,
  145.                    T_TRAIT                => true,
  146.                    T_DOC_COMMENT_OPEN_TAG => true,
  147.                   );
  148.  
  149.         if (isset($ignore[$tokens[$firstContent]['code']]=== false
  150.             && $tokens[$firstContent]['line'>= ($tokens[$scopeOpener]['line'+ 2)
  151.         {
  152.             $gap ($tokens[$firstContent]['line'$tokens[$scopeOpener]['line'- 1);
  153.             $phpcsFile->recordMetric($stackPtr'Blank lines at start of control structure'$gap);
  154.  
  155.             $error 'Blank line found at start of control structure';
  156.             $fix   $phpcsFile->addFixableError($error$scopeOpener'SpacingAfterOpen');
  157.  
  158.             if ($fix === true{
  159.                 $phpcsFile->fixer->beginChangeset();
  160.                 $i ($scopeOpener + 1);
  161.                 while ($tokens[$i]['line'!== $tokens[$firstContent]['line']{
  162.                     // Start removing content from the line after the opener.
  163.                     if ($tokens[$i]['line'!== $tokens[$scopeOpener]['line']{
  164.                         $phpcsFile->fixer->replaceToken($i'');
  165.                     }
  166.  
  167.                     $i++;
  168.                 }
  169.  
  170.                 $phpcsFile->fixer->endChangeset();
  171.             }
  172.         else {
  173.             $phpcsFile->recordMetric($stackPtr'Blank lines at start of control structure'0);
  174.         }//end if
  175.  
  176.         if ($firstContent !== $scopeCloser{
  177.             $lastContent $phpcsFile->findPrevious(
  178.                 T_WHITESPACE,
  179.                 ($scopeCloser - 1),
  180.                 null,
  181.                 true
  182.             );
  183.  
  184.             $lastNonEmptyContent $phpcsFile->findPrevious(
  185.                 Tokens::$emptyTokens,
  186.                 ($scopeCloser - 1),
  187.                 null,
  188.                 true
  189.             );
  190.  
  191.             $checkToken $lastContent;
  192.             if (isset($tokens[$lastNonEmptyContent]['scope_condition']=== true{
  193.                 $checkToken $tokens[$lastNonEmptyContent]['scope_condition'];
  194.             }
  195.  
  196.             if (isset($ignore[$tokens[$checkToken]['code']]=== false
  197.                 && $tokens[$lastContent]['line'<= ($tokens[$scopeCloser]['line'- 2)
  198.             {
  199.                 $errorToken $scopeCloser;
  200.                 for ($i ($scopeCloser - 1)$i $lastContent$i--{
  201.                     if ($tokens[$i]['line'$tokens[$scopeCloser]['line']{
  202.                         $errorToken $i;
  203.                         break;
  204.                     }
  205.                 }
  206.  
  207.                 $gap ($tokens[$scopeCloser]['line'$tokens[$lastContent]['line'- 1);
  208.                 $phpcsFile->recordMetric($stackPtr'Blank lines at end of control structure'$gap);
  209.  
  210.                 $error 'Blank line found at end of control structure';
  211.                 $fix   $phpcsFile->addFixableError($error$errorToken'SpacingBeforeClose');
  212.  
  213.                 if ($fix === true{
  214.                     $phpcsFile->fixer->beginChangeset();
  215.                     $i ($scopeCloser - 1);
  216.                     for ($i ($scopeCloser - 1)$i $lastContent$i--{
  217.                         if ($tokens[$i]['line'=== $tokens[$scopeCloser]['line']{
  218.                             continue;
  219.                         }
  220.  
  221.                         if ($tokens[$i]['line'=== $tokens[$lastContent]['line']{
  222.                             break;
  223.                         }
  224.  
  225.                         $phpcsFile->fixer->replaceToken($i'');
  226.                     }
  227.  
  228.                     $phpcsFile->fixer->endChangeset();
  229.                 }
  230.             else {
  231.                 $phpcsFile->recordMetric($stackPtr'Blank lines at end of control structure'0);
  232.             }//end if
  233.         }//end if
  234.  
  235.         $trailingContent $phpcsFile->findNext(
  236.             T_WHITESPACE,
  237.             ($scopeCloser + 1),
  238.             null,
  239.             true
  240.         );
  241.  
  242.         if ($tokens[$trailingContent]['code'=== T_COMMENT{
  243.             // Special exception for code where the comment about
  244.             // an ELSE or ELSEIF is written between the control structures.
  245.             $nextCode $phpcsFile->findNext(
  246.                 Tokens::$emptyTokens,
  247.                 ($scopeCloser + 1),
  248.                 null,
  249.                 true
  250.             );
  251.  
  252.             if ($tokens[$nextCode]['code'=== T_ELSE
  253.                 || $tokens[$nextCode]['code'=== T_ELSEIF
  254.             {
  255.                 $trailingContent $nextCode;
  256.             }
  257.         }//end if
  258.  
  259.         if ($tokens[$trailingContent]['code'=== T_ELSE{
  260.             if ($tokens[$stackPtr]['code'=== T_IF{
  261.                 // IF with ELSE.
  262.                 return;
  263.             }
  264.         }
  265.  
  266.         if ($tokens[$trailingContent]['code'=== T_WHILE
  267.             && $tokens[$stackPtr]['code'=== T_DO
  268.         {
  269.             // DO with WHILE.
  270.             return;
  271.         }
  272.  
  273.         if ($tokens[$trailingContent]['code'=== T_CLOSE_TAG{
  274.             // At the end of the script or embedded code.
  275.             return;
  276.         }
  277.  
  278.         if (isset($tokens[$trailingContent]['scope_condition']=== true
  279.             && $tokens[$trailingContent]['scope_condition'!== $trailingContent
  280.             && isset($tokens[$trailingContent]['scope_opener']=== true
  281.             && $tokens[$trailingContent]['scope_opener'!== $trailingContent
  282.         {
  283.             // Another control structure's closing brace.
  284.             $owner $tokens[$trailingContent]['scope_condition'];
  285.             if ($tokens[$owner]['code'=== T_FUNCTION{
  286.                 // The next content is the closing brace of a function
  287.                 // so normal function rules apply and we can ignore it.
  288.                 return;
  289.             }
  290.  
  291.             if ($tokens[$owner]['code'=== T_CLOSURE
  292.                 && ($phpcsFile->hasCondition($stackPtrT_FUNCTION=== true
  293.                 || $phpcsFile->hasCondition($stackPtrT_CLOSURE=== true
  294.                 || isset($tokens[$stackPtr]['nested_parenthesis']=== true)
  295.             {
  296.                 return;
  297.             }
  298.  
  299.             if ($tokens[$trailingContent]['line'!== ($tokens[$scopeCloser]['line'+ 1)) {
  300.                 $error 'Blank line found after control structure';
  301.                 $fix   $phpcsFile->addFixableError($error$scopeCloser'LineAfterClose');
  302.  
  303.                 if ($fix === true{
  304.                     $phpcsFile->fixer->beginChangeset();
  305.                     $i ($scopeCloser + 1);
  306.                     while ($tokens[$i]['line'!== $tokens[$trailingContent]['line']{
  307.                         $phpcsFile->fixer->replaceToken($i'');
  308.                         $i++;
  309.                     }
  310.  
  311.                     $phpcsFile->fixer->addNewline($scopeCloser);
  312.                     $phpcsFile->fixer->endChangeset();
  313.                 }
  314.             }
  315.         else if ($tokens[$trailingContent]['code'!== T_ELSE
  316.             && $tokens[$trailingContent]['code'!== T_ELSEIF
  317.             && $tokens[$trailingContent]['code'!== T_CATCH
  318.             && $tokens[$trailingContent]['line'=== ($tokens[$scopeCloser]['line'+ 1)
  319.         {
  320.             $error 'No blank line found after control structure';
  321.             $fix   $phpcsFile->addFixableError($error$scopeCloser'NoLineAfterClose');
  322.             if ($fix === true{
  323.                 $phpcsFile->fixer->addNewline($scopeCloser);
  324.             }
  325.         }//end if
  326.  
  327.     }//end process()
  328.  
  329.  
  330. }//end class

Documentation generated on Mon, 11 Mar 2019 15:27:21 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.