Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** Generic_Sniffs_Whitespace_ScopeIndentSniff.** PHP version 5** @category PHP* @package PHP_CodeSniffer* @author Greg Sherwood <gsherwood@squiz.net>* @author Marc McIntyre <mmcintyre@squiz.net>* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence* @version CVS: $Id: ScopeIndentSniff.php 270281 2008-12-02 02:38:34Z squiz $* @link http://pear.php.net/package/PHP_CodeSniffer*//*** Generic_Sniffs_Whitespace_ScopeIndentSniff.** Checks that control structures are structured correctly, and their content* is indented correctly. This sniff will throw errors if tabs are used* for indentation rather than spaces.** @category PHP* @package PHP_CodeSniffer* @author Greg Sherwood <gsherwood@squiz.net>* @author Marc McIntyre <mmcintyre@squiz.net>* @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence* @version Release: 1.2.1* @link http://pear.php.net/package/PHP_CodeSniffer*/class Generic_Sniffs_WhiteSpace_ScopeIndentSniff implements PHP_CodeSniffer_Sniff{/*** The number of spaces code should be indented.** @var int*/protected $indent = 4;/*** Does the indent need to be exactly right.** If TRUE, indent needs to be exactly $ident spaces. If FALSE,* indent needs to be at least $ident spaces (but can be more).** @var bool*/protected $exact = false;/*** Any scope openers that should not cause an indent.** @var array(int)*/protected $nonIndentingScopes = array();/*** Returns an array of tokens this test wants to listen for.** @return array*/public function register(){return PHP_CodeSniffer_Tokens::$scopeOpeners;}//end register()/*** Processes this test, when one of its tokens is encountered.** @param PHP_CodeSniffer_File $phpcsFile All the tokens found in the document.* @param int $stackPtr The position of the current token* in the stack passed in $tokens.** @return void*/public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr){$tokens = $phpcsFile->getTokens();// If this is an inline condition (ie. there is no scope opener), then// return, as this is not a new scope.if (isset($tokens[$stackPtr]['scope_opener']) === false) {return;}if ($tokens[$stackPtr]['code'] === T_ELSE) {$next = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens,($stackPtr + 1),null,true);// We will handle the T_IF token in another call to process.if ($tokens[$next]['code'] === T_IF) {return;}}// Find the first token on this line.$firstToken = $stackPtr;for ($i = $stackPtr; $i >= 0; $i--) {// Record the first code token on the line.if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {$firstToken = $i;}// It's the start of the line, so we've found our first php token.if ($tokens[$i]['column'] === 1) {break;}}// Based on the conditions that surround this token, determine the// indent that we expect this current content to be.$expectedIndent = $this->calculateExpectedIndent($tokens, $firstToken);if ($tokens[$firstToken]['column'] !== $expectedIndent) {$error = 'Line indented incorrectly; expected ';$error .= ($expectedIndent - 1).' spaces, found ';$error .= ($tokens[$firstToken]['column'] - 1);$phpcsFile->addError($error, $stackPtr);}$scopeOpener = $tokens[$stackPtr]['scope_opener'];$scopeCloser = $tokens[$stackPtr]['scope_closer'];// Some scopes are expected not to have indents.if (in_array($tokens[$firstToken]['code'], $this->nonIndentingScopes) === false) {$indent = ($expectedIndent + $this->indent);} else {$indent = $expectedIndent;}$newline = false;$commentOpen = false;$inHereDoc = false;// Only loop over the content beween the opening and closing brace, not// the braces themselves.for ($i = ($scopeOpener + 1); $i < $scopeCloser; $i++) {// If this token is another scope, skip it as it will be handled by// another call to this sniff.if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$scopeOpeners) === true) {if (isset($tokens[$i]['scope_opener']) === true) {$i = $tokens[$i]['scope_closer'];} else {// If this token does not have a scope_opener indice, then// it's probably an inline scope, so let's skip to the next// semicolon. Inline scopes include inline if's, abstract// methods etc.$nextToken = $phpcsFile->findNext(T_SEMICOLON, $i, $scopeCloser);if ($nextToken !== false) {$i = $nextToken;}}continue;}// If this is a HEREDOC then we need to ignore it as the// whitespace before the contents within the HEREDOC are// considered part of the content.if ($tokens[$i]['code'] === T_START_HEREDOC) {$inHereDoc = true;continue;} else if ($inHereDoc === true) {if ($tokens[$i]['code'] === T_END_HEREDOC) {$inHereDoc = false;}continue;}if ($tokens[$i]['column'] === 1) {// We started a newline.$newline = true;}if ($newline === true && $tokens[$i]['code'] !== T_WHITESPACE) {// If we started a newline and we find a token that is not// whitespace, then this must be the first token on the line that// must be indented.$newline = false;$firstToken = $i;$column = $tokens[$firstToken]['column'];// Special case for non-PHP code.if ($tokens[$firstToken]['code'] === T_INLINE_HTML) {$trimmedContentLength= strlen(ltrim($tokens[$firstToken]['content']));if ($trimmedContentLength === 0) {continue;}$contentLength = strlen($tokens[$firstToken]['content']);$column = ($contentLength - $trimmedContentLength + 1);}// Check to see if this constant string spans multiple lines.// If so, then make sure that the strings on lines other than the// first line are indented appropriately, based on their whitespace.if (in_array($tokens[$firstToken]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {if (in_array($tokens[($firstToken - 1)]['code'], PHP_CodeSniffer_Tokens::$stringTokens) === true) {// If we find a string that directly follows another string// then its just a string that spans multiple lines, so we// don't need to check for indenting.continue;}}// This is a special condition for T_DOC_COMMENT and C-style// comments, which contain whitespace between each line.$comments = array(T_COMMENT,T_DOC_COMMENT);if (in_array($tokens[$firstToken]['code'], $comments) === true) {$content = trim($tokens[$firstToken]['content']);if (preg_match('|^/\*|', $content) !== 0) {// Check to see if the end of the comment is on the same line// as the start of the comment. If it is, then we don't// have to worry about opening a comment.if (preg_match('|\*/$|', $content) === 0) {// We don't have to calculate the column for the// start of the comment as there is a whitespace// token before it.$commentOpen = true;}} else if ($commentOpen === true) {if ($content === '') {// We are in a comment, but this line has nothing on it// so let's skip it.continue;}$contentLength = strlen($tokens[$firstToken]['content']);$trimmedContentLength= strlen(ltrim($tokens[$firstToken]['content']));$column = ($contentLength - $trimmedContentLength + 1);if (preg_match('|\*/$|', $content) !== 0) {$commentOpen = false;}}//end if}//end if// The token at the start of the line, needs to have its' column// greater than the relative indent we set above. If it is less,// an error should be shown.if ($column !== $indent) {if ($this->exact === true || $column < $indent) {$error = 'Line indented incorrectly; expected ';if ($this->exact === false) {$error .= 'at least ';}$error .= ($indent - 1).' spaces, found ';$error .= ($column - 1);$phpcsFile->addError($error, $firstToken);}}}//end if}//end for}//end process()/*** Calculates the expected indent of a token.** @param array $tokens The stack of tokens for this file.* @param int $stackPtr The position of the token to get indent for.** @return int*/protected function calculateExpectedIndent(array $tokens, $stackPtr){$conditionStack = array();// Empty conditions array (top level structure).if (empty($tokens[$stackPtr]['conditions']) === true) {return 1;}$tokenConditions = $tokens[$stackPtr]['conditions'];foreach ($tokenConditions as $id => $condition) {// If it's an indenting scope ie. it's not in our array of// scopes that don't indent, add it to our condition stack.if (in_array($condition, $this->nonIndentingScopes) === false) {$conditionStack[$id] = $condition;}}return ((count($conditionStack) * $this->indent) + 1);}//end calculateExpectedIndent()}//end class?>