Inheritance: extends lithium\core\StaticObject
Example #1
0
 /**
  * Takes an instance of an object (usually a Collection object) containing test
  * instances. Introspects the test subject classes to extract cyclomatic complexity data.
  *
  * @param object $report Instance of Report which is calling apply.
  * @param array $tests The test to apply this filter on
  * @param array $options Not used.
  * @return object|void Returns the instance of `$tests`.
  */
 public static function apply($report, $tests, array $options = array())
 {
     $results = array();
     foreach ($tests->invoke('subject') as $class) {
         $results[$class] = array();
         if (!($methods = Inspector::methods($class, 'ranges', array('public' => false)))) {
             continue;
         }
         foreach ($methods as $method => $lines) {
             $lines = Inspector::lines($class, $lines);
             $branches = Parser::tokenize(join("\n", (array) $lines), array('include' => static::$_include));
             $results[$class][$method] = count($branches) + 1;
             $report->collect(__CLASS__, $results);
         }
     }
     return $tests;
 }
Example #2
0
 protected function _display($file)
 {
     if (!($matches = Parser::tokenize(file_get_contents($file)))) {
         $this->stop(1, 'no matches');
     }
     if (!$this->show) {
         $this->out($file . ':');
     }
     $rows = array(array('', 'ID', 'LINE', 'TYPE', 'TEXT'));
     foreach ($matches as $match) {
         if (($id = substr(sha1($file . $match['line']), 0, 4)) == $this->show) {
             $this->stop(0, $file);
         }
         $rows[] = array('', $id, $match['line'], $match['type'], $match['text']);
     }
     if (!$this->show) {
         $this->out($this->columns($rows));
         $this->hr();
         $this->nl();
     }
 }
Example #3
0
 /**
  * Gets the static and dynamic dependencies for a class or group of classes.
  *
  * @param mixed $classes Either a string specifying a class, or a numerically indexed array
  *        of classes
  * @param array $options
  * @return array An array of the static and dynamic class dependencies
  * @todo Document valid options
  */
 public static function dependencies($classes, array $options = array())
 {
     $defaults = array('type' => null);
     $options += $defaults;
     $static = $dynamic = array();
     $trim = function ($c) {
         return trim(trim($c, '\\'));
     };
     $join = function ($i) {
         return join('', $i);
     };
     foreach ((array) $classes as $class) {
         $data = explode("\n", file_get_contents(Libraries::path($class)));
         $data = "<?php \n" . join("\n", preg_grep('/^\\s*use /', $data)) . "\n ?>";
         $classes = array_map($join, Parser::find($data, 'use *;', array('return' => 'content', 'lineBreaks' => true, 'startOfLine' => true, 'capture' => array('T_STRING', 'T_NS_SEPARATOR'))));
         if ($classes) {
             $static = array_unique(array_merge($static, array_map($trim, $classes)));
         }
         $classes = static::info($class . '::$_classes', array('value'));
         if (isset($classes['value'])) {
             $dynamic = array_merge($dynamic, array_map($trim, array_values($classes['value'])));
         }
     }
     if (empty($options['type'])) {
         return array_unique(array_merge($static, $dynamic));
     }
     $type = $options['type'];
     return isset(${$type}) ? ${$type} : null;
 }
Example #4
0
 /**
  * Finds a pattern in a block of code.
  *
  * @param string $code
  * @param string $pattern
  * @param array $options The list of options to be used when parsing / matching `$code`:
  *              - 'ignore': An array of token names to ignore while parsing, defaults to
  *               `array('T_WHITESPACE')`
  *              - 'lineBreaks': If true, all tokens in a single pattern match must appear on the
  *                same line of code, defaults to false
  *              - 'startOfLine': If true, the pattern must match starting with the beginning of
  *                the line of code to be matched, defaults to false
  * @return array
  */
 public static function find($code, $pattern, $options = array())
 {
     $defaults = array('all' => true, 'capture' => array(), 'ignore' => array('T_WHITESPACE'), 'return' => true, 'lineBreaks' => false, 'startOfLine' => false);
     $options += $defaults;
     $results = array();
     $matches = array();
     $patternMatch = array();
     $ret = $options['return'];
     $tokens = new Collection(array('items' => static::tokenize($code, $options)));
     $pattern = new Collection(array('items' => static::tokenize($pattern, $options)));
     $breaks = function ($token) use(&$tokens, &$matches, &$patternMatch, $options) {
         if (!$options['lineBreaks']) {
             return true;
         }
         if (empty($patternMatch) && !$options['startOfLine']) {
             return true;
         }
         if (empty($patternMatch)) {
             $prev = $tokens->prev();
             $tokens->next();
         } else {
             $prev = reset($patternMatch);
         }
         if (empty($patternMatch) && $options['startOfLine']) {
             return $token['line'] > $prev['line'];
         }
         return $token['line'] == $prev['line'];
     };
     $capture = function ($token) use(&$matches, &$patternMatch, $tokens, $breaks, $options) {
         if (is_null($token)) {
             $matches = $patternMatch = array();
             return false;
         }
         if (empty($patternMatch)) {
             $prev = $tokens->prev();
             $tokens->next();
             if ($options['startOfLine'] && $token['line'] == $prev['line']) {
                 $patternMatch = $matches = array();
                 return false;
             }
         }
         $patternMatch[] = $token;
         if (empty($options['capture']) || !in_array($token['name'], $options['capture'])) {
             return true;
         }
         if (!$breaks($token)) {
             $matches = array();
             return true;
         }
         $matches[] = $token;
         return true;
     };
     $executors = array('*' => function (&$tokens, &$pattern) use($options, $capture) {
         $closing = $pattern->next();
         $tokens->prev();
         while (($t = $tokens->next()) && !Parser::matchToken($closing, $t)) {
             $capture($t);
         }
         $pattern->next();
     });
     $tokens->rewind();
     $pattern->rewind();
     while ($tokens->valid()) {
         if (!$pattern->valid()) {
             $pattern->rewind();
             if (!empty($matches)) {
                 $results[] = array_map(function ($i) use($ret) {
                     return isset($i[$ret]) ? $i[$ret] : $i;
                 }, $matches);
             }
             $capture(null);
         }
         $p = $pattern->current();
         $t = $tokens->current();
         switch (true) {
             case static::matchToken($p, $t):
                 $capture($t) ? $pattern->next() : $pattern->rewind();
                 break;
             case isset($executors[$p['name']]):
                 $exec = $executors[$p['name']];
                 $exec($tokens, $pattern);
                 break;
             default:
                 $capture(null);
                 $pattern->rewind();
                 break;
         }
         $tokens->next();
     }
     return $results;
 }
Example #5
0
    public function testParserGuessesLineBleed()
    {
        $code = <<<EOD
if (false) {
\treturn true;
}
EOD;
        $tokens = Parser::tokenize($code);
        $this->assertIdentical('}', $tokens[13]['content']);
        $this->assertIdentical(3, $tokens[13]['line']);
    }
Example #6
0
 /**
  * Adds more token information than the base lithium tokenizer such as name,
  * parent, and children.
  *
  * @param string $code Source code to be tokenized.
  * @param array $options Options consists of:
  *        -'wrap': Boolean indicating whether or not to wrap the supplied
  *          code in PHP tags.
  *        -'ignore': An array containing PHP language tokens to ignore.
  *        -'include': If supplied, an array of the only language tokens
  *         to include in the output.
  * @return array An array of extracted information from the supplied source code:
  *         - lineCache: token ids indexed by line number
  *         - typeCache: token ids indexed by token type
  *         - meta: parsing information (level, etc.) indexed by token id
  *         - relationships: parent and child relations (token ids) indexed by token id
  */
 public static function tokenize($code, array $options = array())
 {
     $options += array('wrap' => true);
     $tokens = static::_tokenize(parent::tokenize($code, $options));
     static::$_bracketsChecksum = 0;
     $curParent = -1;
     $brackets = $curlyBrackets = $squareBrackets = 0;
     $level = $needNestUp = $nestLine = $nestLevel = 0;
     $nestLog = $lineCache = $typeCache = array();
     $inString = false;
     $inPhp = $options['wrap'] ? true : false;
     foreach ($tokens as $tokenId => $token) {
         $isString = $token['id'] === T_CONSTANT_ENCAPSED_STRING || $token['id'] === T_ENCAPSED_AND_WHITESPACE;
         $lineCache[$token['line']][] = $tokenId;
         if ($isString) {
             $carriageReturn = substr_count($token['content'], "\n");
             for ($i = 1; $i <= $carriageReturn; $i++) {
                 $lineCache[$token['line'] + $i][] = $tokenId;
             }
         }
         $typeCache[$token['id']][] = $tokenId;
         if ($token['id'] === T_CLOSE_TAG) {
             $needNestUp = 0;
             $nestLevel = 0;
             $inPhp = false;
         }
         if ($token['id'] === T_OPEN_TAG) {
             $inPhp = true;
         }
         if (!$inPhp) {
             continue;
         }
         if ($token['id'] === T_END_DOUBLE_QUOTE || $token['id'] === T_END_HEREDOC) {
             $inString = false;
         }
         if ($needNestUp > 0) {
             $status = $nestLog[$needNestUp];
             if (!$status['founded'] && in_array($token['content'], $status['nestOn'])) {
                 $nestLog[$needNestUp]['founded'] = true;
             }
             if ($token['line'] > $nestLine && $status['founded'] && !$status['applied']) {
                 $nestLevel++;
                 $nestLog[$needNestUp]['applied'] = true;
             }
         }
         $tokens[$tokenId] = static::_checksum($tokens[$tokenId], $isString || $inString);
         $tokens[$tokenId]['level'] = $level;
         $tokens[$tokenId]['parent'] = $curParent;
         $tokens[$tokenId]['children'] = array();
         if (isset($tokens[$curParent])) {
             $tokens[$curParent]['children'][] = $tokenId;
         }
         while (($parent = static::_isEndOfParent($tokenId, $curParent, $tokens)) !== false) {
             if (!$inString && static::$_parentTokens[$tokens[$curParent]['id']]['nestOn']) {
                 $needNestUp === 0 ?: $needNestUp--;
                 $nestLevel = $tokens[$curParent]['nestLevel'];
             }
             $level--;
             $curParent = $parent;
         }
         $tokens[$tokenId]['nestLevel'] = $inString ? null : $nestLevel;
         $tokens[$tokenId]['isString'] = $isString;
         if ($token['id'] === T_START_DOUBLE_QUOTE || $token['id'] === T_START_HEREDOC) {
             $inString = true;
         }
         if (static::_isParent($tokenId, $tokens)) {
             $tokens[$tokenId]['parent'] = $curParent;
             if (!$inString && ($nestOn = static::$_parentTokens[$token['id']]['nestOn'])) {
                 $nestLine = $token['line'];
                 $nestLog[++$needNestUp] = array('nestOn' => $nestOn, 'founded' => $nestOn === true ? true : false, 'applied' => false);
             }
             $curParent = $tokenId;
             $level++;
         }
     }
     if ($level !== 0 || $squareBrackets !== 0 || $curlyBrackets !== 0 || $brackets !== 0) {
         $smallTokens = array_slice($tokens, 0, 20);
         $exception = new ParserException('A parse error has been encountered.');
         $exception->parserData = compact('level', 'curlyBrackets', 'brackets', 'tokens');
         throw $exception;
     }
     return compact('tokens', 'lineCache', 'typeCache');
 }
 public function testFindingTokenPatterns()
 {
     $code = file_get_contents(\lithium\core\Libraries::path('lithium\\analysis\\Parser'));
     $expected = array('tokenize', 'matchToken', '_prepareMatchParams', 'token');
     $results = array_values(array_unique(array_map(function ($i) {
         return $i[0];
     }, Parser::find($code, 'static::_(*)', array('capture' => array('T_STRING'), 'return' => 'content')))));
     $this->assertEqual($expected, $results);
     $expected = array('\\ReflectionClass', '\\lithium\\core\\Libraries', '\\lithium\\util\\Collection', '\\lithium\\util\\Validator', '\\lithium\\util\\Set');
     $results = array_map(function ($i) {
         return join('', $i);
     }, $results = Parser::find($code, 'use *;', array('return' => 'content', 'lineBreaks' => true, 'startOfLine' => true, 'capture' => array('T_STRING', 'T_NS_SEPARATOR'))));
     $this->assertEqual($expected, $results);
     $code = 'function test($options) { return function($foo) use ($options) {';
     $code .= ' ClassName::method($options); ' . "\n" . ' $foo->method($options); }; }';
     list($results) = Parser::find($code, '_::_(', array('capture' => array('T_STRING'), 'return' => 'content'));
     $expected = array('ClassName', 'method');
     $this->assertEqual($expected, $results);
 }
Example #8
0
    public function testParserGuessesLineBleedWithNonWhitespace()
    {
        $code = <<<EOD
if (false) {
\t// hello world
}
EOD;
        $tokens = Parser::tokenize($code);
        $this->assertIdentical('}', $tokens[9]['content']);
        $this->assertIdentical(3, $tokens[9]['line']);
    }