/**
  * 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']));
         }
     }
 }
 /**
  * With lots of various rules we created 4 various rulesets for operators. If one
  * of the tokens or content is found we use the given regex on the joined array.
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $tokens = $testable->tokens();
     foreach ($this->inspector as $inspector) {
         if (isset($inspector['tokens'])) {
             $byToken = $testable->findAll($inspector['tokens']);
         } else {
             $byToken = array();
         }
         if (isset($inspector['content'])) {
             $byContent = $testable->findAllContent($inspector['content']);
         } else {
             $byContent = array();
         }
         foreach (array_merge($byToken, $byContent) as $id) {
             $token = $tokens[$id];
             $isPHP = $testable->isPHP($token['line']);
             if ($isPHP && empty($token['isString'])) {
                 $pattern = String::insert($inspector['regex'], array('content' => preg_quote($token['content'], "/")));
                 $firstId = $id - $inspector['relativeTokens']['before'];
                 $firstId = $firstId < 0 ? 0 : $firstId;
                 $length = $inspector['relativeTokens']['length'];
                 $inspectTokens = array_slice($tokens, $firstId, $length);
                 $html = null;
                 foreach ($inspectTokens as $htmlToken) {
                     $html .= $htmlToken['content'];
                 }
                 if (preg_match($pattern, $html) === 0) {
                     $this->addViolation(array('message' => String::insert($inspector['message'], $token), '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 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 over each line checking if any weak comparison operators
  * are used within the code.
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $tokens = $testable->tokens();
     $message = 'Weak comparison operator {:key} used, try {:value} instead';
     $filtered = $testable->findAll(array_keys($this->inspectableTokens));
     foreach ($filtered as $id) {
         $token = $tokens[$id];
         $this->addWarning(array('message' => String::insert($message, array('key' => token_name($token['id']), 'value' => $this->inspectableTokens[$token['id']])), '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();
     $comments = $testable->findAll(array(T_COMMENT));
     foreach ($comments as $tokenId) {
         $token = $tokens[$tokenId];
         $parentId = $tokens[$tokenId]['parent'];
         if ($parentId === -1 || $tokens[$parentId]['id'] !== T_FUNCTION) {
             $this->addViolation(array('message' => 'Inline comments should never appear.', 'line' => $token['line']));
         } elseif (preg_match('/^test/', Parser::label($parentId, $tokens)) === 0) {
             $this->addViolation(array('message' => 'Inline comments should only appear in testing methods.', 'line' => $token['line']));
         }
     }
 }
 /**
  * Iterates tokens looking for cast tokens then testing against a regex
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $message = 'Casting in the incorrect format, try: "(array) $object;"';
     $tokens = $testable->tokens();
     $filtered = $testable->findAll($this->tokens);
     foreach ($filtered as $id) {
         $token = $tokens[$id];
         $html = '';
         foreach (array_slice($tokens, $id, 3) as $t) {
             $html .= $t['content'];
         }
         if (preg_match($this->pattern, $html) === 0) {
             $this->addViolation(array('message' => $message, '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();
     $filtered = $testable->findAll(array(T_VARIABLE));
     $message = 'Superglobal usage detected in class';
     $currLine = 1;
     foreach ($filtered as $key) {
         $token = $tokens[$key];
         if (preg_match(static::PATTERN, $token['content'])) {
             $isNewLine = $token['line'] > $currLine;
             $currLine = $token['line'];
             if (!$isNewLine) {
                 continue;
             }
             $this->addViolation(array('message' => $message, 'line' => $token['line']));
         }
     }
 }
 /**
  * Will iterate the tokens looking for a T_CLASS which should be
  * in CamelCase and match the file name
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $tokens = $testable->tokens();
     $pathinfo = pathinfo($testable->config('path'));
     if ($pathinfo['extension'] !== 'php') {
         return;
     }
     $filtered = $testable->findAll(array(T_CLASS));
     foreach ($filtered as $key) {
         $token = $tokens[$key];
         $className = $tokens[$key + 2]['content'];
         if ($className !== Inflector::camelize($className)) {
             $this->addViolation(array('message' => 'Class name is not in CamelCase style', 'line' => $token['line']));
         } elseif ($className !== $pathinfo['filename']) {
             $this->addViolation(array('message' => 'Class name and file name should match', '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 the tokens looking for functions validating they have the
  * correct camelBack naming style.
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $tokens = $testable->tokens();
     $filtered = $testable->findAll(array(T_FUNCTION));
     foreach ($filtered as $key) {
         $token = $tokens[$key];
         $label = Parser::label($key, $tokens);
         $modifiers = Parser::modifiers($key, $tokens);
         $isClosure = Parser::closure($key, $tokens);
         if (in_array($label, $this->_magicMethods)) {
             continue;
         }
         if ($testable->findNext(array(T_PROTECTED), $modifiers) !== false) {
             $label = preg_replace('/^_/', '', $label);
         }
         if (!$isClosure && $label !== Inflector::camelize($label, false)) {
             $this->addViolation(array('message' => 'Function "' . $label . '" is not in camelBack style', 'line' => $tokens[$tokens[$key]['parent']]['line']));
         }
     }
 }
 /**
  * Will iterate all the tokens looking for tokens in inspectableTokens
  * The token needs an access modifier if it is a T_FUNCTION or T_VARIABLE
  * and is in the first level of T_CLASS. This prevents functions and variables
  * inside methods and outside classes to register violations.
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $message = '{:name} has no declared visibility.';
     $tokens = $testable->tokens();
     $classes = $testable->findAll(array(T_CLASS));
     $filtered = $testable->findAll($this->inspectableTokens);
     foreach ($classes as $classId) {
         $children = $tokens[$classId]['children'];
         foreach ($children as $member) {
             if (!in_array($member, $filtered)) {
                 continue;
             }
             $modifiers = Parser::modifiers($member, $tokens);
             $visibility = $testable->findNext($this->findTokens, $modifiers);
             if ($visibility === false) {
                 $token = $tokens[$member];
                 $this->addViolation(array('modifiers' => $modifiers, 'message' => String::insert($message, $token), 'line' => $token['line']));
             }
         }
     }
 }
 /**
  * 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 looking for protected methods and variables, once
  * found it will validate the name of it's parent starts with an underscore.
  *
  * @param  Testable $testable The testable object
  * @return void
  */
 public function apply($testable, array $config = array())
 {
     $message = 'Protected method {:name} must start with an underscore';
     $tokens = $testable->tokens();
     $filtered = $testable->findAll(array(T_PROTECTED));
     foreach ($filtered as $tokenId) {
         $token = $tokens[$tokenId];
         $parent = $testable->findNext(array(T_FUNCTION, T_VARIABLE), $tokenId);
         $parentLabel = Parser::label($parent, $tokens);
         if (substr($parentLabel, 0, 1) !== '_') {
             $classTokenId = $testable->findNext(array(T_STRING), $token['parent']);
             $classname = $tokens[$classTokenId]['content'];
             $params = array('message' => String::insert($message, array('name' => $parentLabel)), 'line' => $token['line']);
             if ($this->_strictMode($classname)) {
                 $this->addViolation($params);
             } else {
                 $this->addWarning($params);
             }
         }
     }
 }
 /**
  * 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']));
             }
         }
     }
 }
 /**
  * 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']));
         }
     }
 }
Example #18
0
 /**
  * @param \ascertain\Testable $testable
  * @param string $exceptionClass
  */
 function __construct(Testable $testable, $exceptionClass = null)
 {
     $this->properties = $testable->export();
     $this->exceptionClass = $exceptionClass;
 }