/** * Will iterate over each line checking if tabs are only first * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $message = 'Tabs can only appear at the beginning of the line'; $lines = $testable->lines(); $tokens = $testable->tokens(); $currLine = 1; $allowTabs = true; foreach ($tokens as $token) { $content = str_replace("\r\n", "\n", $token['content']); $isNewLine = $token['line'] > $currLine || $token['id'] === T_WHITESPACE && preg_match('/\\n/', $content); if ($isNewLine) { $currLine = $token['line']; $allowTabs = true; } if ($token['id'] !== T_WHITESPACE) { $allowTabs = false; continue; } if ($allowTabs) { $isInvalidTab = !preg_match('/^(\\n?\\t?)+ *$/', $content); } else { $isInvalidTab = preg_match('/\\t/', $content); } if ($isInvalidTab) { $this->addViolation(array('message' => $message, 'line' => $token['line'])); } } }
/** * Will iterate the given tokens finding them based on the keys of self::$_tokenMap. * Upon finding the matching tokens it will attempt to match the line against a regular * expression proivded in tokenMap and if none are found add a violation from the message * provided in tokenMap. * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $lines = $testable->lines(); $tokens = $testable->tokens(); $filtered = $testable->findAll(array_keys($this->_tokenMap)); foreach ($filtered as $tokenId) { $token = $tokens[$tokenId]; $tokenMap = $this->_tokenMap[$token['id']]; $patterns = $tokenMap['patterns']; $body = $this->_extractContent($tokenId, $tokens); $singleLine = $this->_matchPattern($patterns, $body); $multiLine = false; if (!$singleLine) { foreach ($patterns as $key => $value) { $patterns[$key] .= 's'; } $multiLine = $this->_matchPattern($patterns, $body); } if (!$singleLine && !$multiLine) { $this->addViolation(array('message' => $this->_tokenMap[$token['id']]['message'], 'line' => $token['line'])); } elseif (!$singleLine) { $this->addWarning(array('message' => $this->_tokenMap[$token['id']]['message'] . ' on a single line.', 'line' => $token['line'])); } } }
/** * Iterates over T_USE tokens, gets the aliased name into an array and * validates it was used within the script. * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $tokens = $testable->tokens(); $lines = $testable->lines(); $typeCache = $testable->typeCache(); $matches = array(); if (!isset($typeCache[T_USE])) { return; } foreach ($typeCache[T_USE] as $tokenId) { $token = $tokens[$tokenId]; $line = $lines[$token['line'] - 1]; if (preg_match('/^use (?:([^ ]+ as )|(.*\\\\))?(.*);$/i', $line, $matches) === 1) { $count = 0; if (in_array($matches[3], $this->_ignored)) { continue; } foreach ($typeCache[T_STRING] as $stringId) { if (strcasecmp($tokens[$stringId]['content'], $matches[3]) === 0) { $count++; } if ($count === 2) { break; } } if ($count < 2) { $this->addViolation(array('message' => 'Class ' . $matches[3] . ' was never called', 'line' => $token['line'])); } } } }
/** * Will iterate tokens looking for comments and if found will determine the regex * to test the comment against. * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $tokens = $testable->tokens(); $lines = $testable->lines(); $lineCache = $testable->lineCache(); $inspectable = array(T_CLASS, T_VARIABLE, T_FUNCTION, T_CONST, T_DOUBLE_COLON); foreach ($testable->findAll(array(T_DOC_COMMENT)) as $tokenId) { $token = $tokens[$tokenId]; $nextLine = $token['line'] + count(preg_split('/\\r\\n|\\r|\\n/', $token['content'])); $parentId = false; if (isset($lineCache[$nextLine])) { $parentId = $testable->findNext($inspectable, $lineCache[$nextLine]); } if ($parentId === false && $token['line'] !== 2) { $this->addViolation(array('message' => 'Docblocks should only be at the beginning of the page or ' . 'before a class/function or static call.', 'line' => $token['line'])); continue; } $parent = $tokens[$parentId]; $content = null; if ($token['line'] === 2) { $match = 'PAGE'; } else { switch ($parent['id']) { case T_CLASS: $match = 'CLASS'; break; case T_DOUBLE_COLON: case T_FUNCTION: $match = 'METHOD'; break; case T_VARIABLE: case T_CONST: $match = 'VARIABLE'; break; } } if (in_array($parent['id'], array(T_FUNCTION, T_VARIABLE), true)) { $content .= $tokens[$tokenId - 1]['content']; } $content .= $token['content']; $pattern = $this->compilePattern($match); $correctFormat = preg_match($this->compilePattern($match), $content) === 1; $hasTags = preg_match($this->compilePattern('HAS_TAGS'), $content) === 1; $correctTagFormat = preg_match($this->compilePattern('TAG_FORMAT'), $content) === 1; if (!$correctFormat) { $this->addViolation(array('message' => 'Docblocks are in the incorrect format.', 'line' => $token['line'])); } elseif ($hasTags && !$correctTagFormat) { $this->addViolation(array('hasTags' => (int) $hasTags, 'correctTagFormat' => (int) $correctTagFormat, 'tagFormat' => $this->compilePattern('TAG_FORMAT'), 'message' => 'Tags should be last and have a blank docblock line.', 'line' => $token['line'])); } } }
/** * Will iterate the lines looking for $patterns while keeping track of how many tabs * the current line should have. * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $followerCount = 0; $lines = $testable->lines(); $tabMessage = 'Incorrect tab indention {:actual} should be {:predicted}.'; $spaceMessage = 'Incorrect space indention {:actual} should be >= {:predicted}.'; foreach ($lines as $lineIndex => $line) { if (!$this->_shouldIgnoreLine($lineIndex, $testable)) { $actual = $this->_getIndent($line); $predicted = $this->_getPredictedIndent($lineIndex, $testable); if ($predicted['tab'] !== null && $actual['tab'] !== $predicted['tab']) { $this->addViolation(array('message' => String::insert($tabMessage, array('predicted' => $predicted['tab'], 'actual' => $actual['tab'])), 'line' => $lineIndex + 1)); } if ($predicted['minSpace'] !== null && $actual['space'] < $predicted['minSpace']) { $this->addViolation(array('message' => String::insert($spaceMessage, array('predicted' => $predicted['minSpace'], 'actual' => $actual['space'])), 'line' => $lineIndex + 1)); } } } }
/** * Will iterate the lines until it finds one with $requiredTokens * Once found it will find all short tags using $findPattern and * match against them using $matchedPattern * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $message = 'Inline HTML should be in the following format: "<?=$var; ?>"'; $lines = $testable->lines(); $tokens = $testable->tokens(); $lineCache = $testable->lineCache(); $matches = array(); foreach ($lines as $lineNumber => $line) { $lineTokens = isset($lineCache[$lineNumber]) ? $lineCache[$lineNumber] : array(); if ($this->hasRequiredTokens($tokens, $lineTokens)) { preg_match_all($this->findPattern, $line, $matches); foreach ($matches as $match) { if (isset($match[0]) && preg_match($this->matchPattern, $match[0]) === 0) { $this->addViolation(array('message' => $message, 'line' => $lineNumber)); } } } } }
/** * Will iterate over each line checking for blank lines * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $lines = $testable->lines(); $tokens = $testable->tokens(); $lastBlankLineId = -2; foreach ($lines as $lineId => $line) { $lineNumber = $lineId + 1; $ignore = false; $key = $testable->findTokenByLine($lineNumber); if (isset($tokens[$key])) { $token = $tokens[$key]; $ignore = in_array($token['id'], $this->ignoreableTokens, true); } if (!$ignore && preg_match('/^$/', $line) === 1) { if ($lastBlankLineId + 1 === $lineId) { $this->addViolation(array('message' => 'Multiple blank lines.', 'line' => $lineNumber)); } $lastBlankLineId = $lineId; } } }
/** * Will iterate the tokens for $inspectableTokens, once found it'll find * the next content taht doesn't have the token $skipTokens and validate * it has no parentheses * * @param Testable $testable The testable object * @return void */ public function apply($testable, array $config = array()) { $message = 'Construct {:name} should not contain parentheses and be on its own line.'; $tokens = $testable->tokens(); $inspectable = $testable->findAll($this->inspectableTokens); $lines = $testable->lines(); foreach ($inspectable as $key) { $token = $tokens[$key]; $lineIndex = $token['line'] - 1; if (isset($lines[$lineIndex])) { $line = $lines[$lineIndex]; $next = $key + 1; if (in_array($token['id'], $this->ownLineTokens)) { $pattern = '/^' . $this->pattern . '$/'; } else { $pattern = '/' . $this->pattern . '$/'; } if (preg_match($pattern, $line) === 0) { $this->addViolation(array('message' => String::insert($message, array('name' => $token['name'])), 'line' => $token['line'])); } } } }