[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/_cs/DokuWiki/Sniffs/WhiteSpace/ -> ScopeIndentSniff.php (source)

   1  <?php
   2  /**
   3   * DokuWiki_Sniffs_Whitespace_ScopeIndentSniff based on
   4   * Generic_Sniffs_Whitespace_ScopeIndentSniff.
   5   *
   6   * PHP version 5
   7   *
   8   * @category  PHP
   9   * @package   PHP_CodeSniffer
  10   * @author    Andreas Gohr <andi@splitbrain.org>
  11   * @author    Greg Sherwood <gsherwood@squiz.net>
  12   * @author    Marc McIntyre <mmcintyre@squiz.net>
  13   * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
  14   * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  15   * @version   CVS: $Id: ScopeIndentSniff.php 270281 2008-12-02 02:38:34Z squiz $
  16   * @link      http://pear.php.net/package/PHP_CodeSniffer
  17   */
  18  
  19  /**
  20   * Generic_Sniffs_Whitespace_ScopeIndentSniff.
  21   *
  22   * Checks that control structures are structured correctly, and their content
  23   * is indented correctly.
  24   *
  25   * @category  PHP
  26   * @package   PHP_CodeSniffer
  27   * @author    Greg Sherwood <gsherwood@squiz.net>
  28   * @author    Marc McIntyre <mmcintyre@squiz.net>
  29   * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
  30   * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
  31   * @version   Release: 1.2.0
  32   * @link      http://pear.php.net/package/PHP_CodeSniffer
  33   */
  34  class DokuWiki_Sniffs_WhiteSpace_ScopeIndentSniff implements PHP_CodeSniffer_Sniff
  35  {
  36  
  37      /**
  38       * The number of spaces code should be indented.
  39       *
  40       * @var int
  41       */
  42      protected $indent = 4;
  43  
  44      /**
  45       * Does the indent need to be exactly right.
  46       *
  47       * If TRUE, indent needs to be exactly $ident spaces. If FALSE,
  48       * indent needs to be at least $ident spaces (but can be more).
  49       *
  50       * @var bool
  51       */
  52      protected $exact = false;
  53  
  54      /**
  55       * Any scope openers that should not cause an indent.
  56       *
  57       * @var array(int)
  58       */
  59      protected $nonIndentingScopes = array();
  60  
  61  
  62      /**
  63       * Returns an array of tokens this test wants to listen for.
  64       *
  65       * @return array
  66       */
  67      public function register()
  68      {
  69          return PHP_CodeSniffer_Tokens::$scopeOpeners;
  70  
  71      }//end register()
  72  
  73  
  74      /**
  75       * Processes this test, when one of its tokens is encountered.
  76       *
  77       * @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.
  78       * @param int                  $stackPtr  The position of the current token
  79       *                                        in the stack passed in $tokens.
  80       *
  81       * @return void
  82       */
  83      public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
  84      {
  85          $tokens = $phpcsFile->getTokens();
  86  
  87          // If this is an inline condition (ie. there is no scope opener), then
  88          // return, as this is not a new scope.
  89          if (isset($tokens[$stackPtr]['scope_opener']) === false) {
  90              return;
  91          }
  92  
  93          if ($tokens[$stackPtr]['code'] === T_ELSE) {
  94              $next = $phpcsFile->findNext(
  95                  PHP_CodeSniffer_Tokens::$emptyTokens,
  96                  ($stackPtr + 1),
  97                  null,
  98                  true
  99              );
 100  
 101              // We will handle the T_IF token in another call to process.
 102              if ($tokens[$next]['code'] === T_IF) {
 103                  return;
 104              }
 105          }
 106  
 107          // Find the first token on this line.
 108          $firstToken = $stackPtr;
 109          for ($i = $stackPtr; $i >= 0; $i--) {
 110              // Record the first code token on the line.
 111              if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
 112                  $firstToken = $i;
 113              }
 114  
 115              // It's the start of the line, so we've found our first php token.
 116              if ($tokens[$i]['column'] === 1) {
 117                  break;
 118              }
 119          }
 120  
 121          // Based on the conditions that surround this token, determine the
 122          // indent that we expect this current content to be.
 123          $expectedIndent = $this->calculateExpectedIndent($tokens, $firstToken);
 124  
 125          if ($tokens[$firstToken]['column'] !== $expectedIndent) {
 126              if($this->exact || $tokens[$firstToken]['column'] < $expectedIndent){
 127                  $error  = 'Line indented incorrectly; expected ';
 128                  $error .= ($expectedIndent - 1).' spaces, found ';
 129                  $error .= ($tokens[$firstToken]['column'] - 1);
 130                  $phpcsFile->addError($error, $stackPtr);
 131              }elseif((($tokens[$firstToken]['column'] - 1) % $this->indent)){
 132                  $error  = 'Line indented not by multiple of '.$this->indent.'; expected ';
 133                  $error .= ($expectedIndent - 1).' spaces, found ';
 134                  $error .= ($tokens[$firstToken]['column'] - 1);
 135                  $phpcsFile->addError($error, $stackPtr);
 136              }
 137          }
 138  
 139          $scopeOpener = $tokens[$stackPtr]['scope_opener'];
 140          $scopeCloser = $tokens[$stackPtr]['scope_closer'];
 141  
 142          // Some scopes are expected not to have indents.
 143          if (in_array($tokens[$firstToken]['code'], $this->nonIndentingScopes) === false) {
 144              $indent = ($expectedIndent + $this->indent);
 145          } else {
 146              $indent = $expectedIndent;
 147          }
 148  
 149          $newline     = false;
 150          $commentOpen = false;
 151          $inHereDoc   = false;
 152  
 153          // Only loop over the content beween the opening and closing brace, not
 154          // the braces themselves.
 155          for ($i = ($scopeOpener + 1); $i < $scopeCloser; $i++) {
 156  
 157              // If this token is another scope, skip it as it will be handled by
 158              // another call to this sniff.
 159              if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true) {
 160                  if (isset($tokens[$i]['scope_opener']) === true) {
 161                      $i = $tokens[$i]['scope_closer'];
 162                  } else {
 163                      // If this token does not have a scope_opener indice, then
 164                      // it's probably an inline scope, so let's skip to the next
 165                      // semicolon. Inline scopes include inline if's, abstract
 166                      // methods etc.
 167                      $nextToken = $phpcsFile->findNext(T_SEMICOLON, $i, $scopeCloser);
 168                      if ($nextToken !== false) {
 169                          $i = $nextToken;
 170                      }
 171                  }
 172  
 173                  continue;
 174              }
 175  
 176              // If this is a HEREDOC then we need to ignore it as the
 177              // whitespace before the contents within the HEREDOC are
 178              // considered part of the content.
 179              if ($tokens[$i]['code'] === T_START_HEREDOC) {
 180                  $inHereDoc = true;
 181                  continue;
 182              } else if ($inHereDoc === true) {
 183                  if ($tokens[$i]['code'] === T_END_HEREDOC) {
 184                      $inHereDoc = false;
 185                  }
 186  
 187                  continue;
 188              }
 189  
 190              if ($tokens[$i]['column'] === 1) {
 191                  // We started a newline.
 192                  $newline = true;
 193              }
 194  
 195              if ($newline === true && $tokens[$i]['code'] !== T_WHITESPACE) {
 196                  // If we started a newline and we find a token that is not
 197                  // whitespace, then this must be the first token on the line that
 198                  // must be indented.
 199                  $newline    = false;
 200                  $firstToken = $i;
 201  
 202                  $column = $tokens[$firstToken]['column'];
 203  
 204                  // Special case for non-PHP code.
 205                  if ($tokens[$firstToken]['code'] === T_INLINE_HTML) {
 206                      $trimmedContentLength
 207                          = strlen(ltrim($tokens[$firstToken]['content']));
 208                      if ($trimmedContentLength === 0) {
 209                          continue;
 210                      }
 211  
 212                      $contentLength = strlen($tokens[$firstToken]['content']);
 213                      $column        = ($contentLength - $trimmedContentLength + 1);
 214                  }
 215  
 216                  // Check to see if this constant string spans multiple lines.
 217                  // If so, then make sure that the strings on lines other than the
 218                  // first line are indented appropriately, based on their whitespace.
 219                  if (in_array($tokens[$firstToken]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
 220                      if (in_array($tokens[($firstToken - 1)]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {
 221                          // If we find a string that directly follows another string
 222                          // then its just a string that spans multiple lines, so we
 223                          // don't need to check for indenting.
 224                          continue;
 225                      }
 226                  }
 227  
 228                  // This is a special condition for T_DOC_COMMENT and C-style
 229                  // comments, which contain whitespace between each line.
 230                  $comments = array(
 231                               T_COMMENT,
 232                               T_DOC_COMMENT
 233                              );
 234  
 235                  if (in_array($tokens[$firstToken]['code'], $comments) === true) {
 236                      $content = trim($tokens[$firstToken]['content']);
 237                      if (preg_match('|^/\*|', $content) !== 0) {
 238                          // Check to see if the end of the comment is on the same line
 239                          // as the start of the comment. If it is, then we don't
 240                          // have to worry about opening a comment.
 241                          if (preg_match('|\*/$|', $content) === 0) {
 242                              // We don't have to calculate the column for the
 243                              // start of the comment as there is a whitespace
 244                              // token before it.
 245                              $commentOpen = true;
 246                          }
 247                      } else if ($commentOpen === true) {
 248                          if ($content === '') {
 249                              // We are in a comment, but this line has nothing on it
 250                              // so let's skip it.
 251                              continue;
 252                          }
 253  
 254                          $contentLength = strlen($tokens[$firstToken]['content']);
 255                          $trimmedContentLength
 256                              = strlen(ltrim($tokens[$firstToken]['content']));
 257  
 258                          $column = ($contentLength - $trimmedContentLength + 1);
 259                          if (preg_match('|\*/$|', $content) !== 0) {
 260                              $commentOpen = false;
 261                          }
 262                      }//end if
 263                  }//end if
 264  
 265                  // The token at the start of the line, needs to have its' column
 266                  // greater than the relative indent we set above. If it is less,
 267                  // an error should be shown.
 268                  if ($column !== $indent) {
 269                      if ($this->exact === true || $column < $indent) {
 270                          $error  = 'Line indented incorrectly; expected ';
 271                          if ($this->exact === false) {
 272                              $error .= 'at least ';
 273                          }
 274  
 275                          $error .= ($indent - 1).' spaces, found ';
 276                          $error .= ($column - 1);
 277                          $phpcsFile->addError($error, $firstToken);
 278                      }
 279                  }
 280              }//end if
 281          }//end for
 282  
 283      }//end process()
 284  
 285  
 286      /**
 287       * Calculates the expected indent of a token.
 288       *
 289       * @param array $tokens   The stack of tokens for this file.
 290       * @param int   $stackPtr The position of the token to get indent for.
 291       *
 292       * @return int
 293       */
 294      protected function calculateExpectedIndent(array $tokens, $stackPtr)
 295      {
 296          $conditionStack = array();
 297  
 298          // Empty conditions array (top level structure).
 299          if (empty($tokens[$stackPtr]['conditions']) === true) {
 300              return 1;
 301          }
 302  
 303          $tokenConditions = $tokens[$stackPtr]['conditions'];
 304          foreach ($tokenConditions as $id => $condition) {
 305              // If it's an indenting scope ie. it's not in our array of
 306              // scopes that don't indent, add it to our condition stack.
 307              if (in_array($condition, $this->nonIndentingScopes) === false) {
 308                  $conditionStack[$id] = $condition;
 309              }
 310          }
 311  
 312          return ((count($conditionStack) * $this->indent) + 1);
 313  
 314      }//end calculateExpectedIndent()
 315  
 316  
 317  }//end class
 318  
 319  ?>