public static executable ( mixed $class, array $options = [] ) : array | ||
$class | mixed | Class name as a string or object instance. |
$options | array | Set of options: - `'self'` _boolean_: If `true` (default), only returns lines of methods defined in `$class`, excluding methods from inherited classes. - `'methods'` _array_: An arbitrary list of methods to search, as a string (single method name) or array of method names. - `'filter'` _boolean_: If `true`, filters out lines containing only whitespace or braces. Note: for some reason, the Zend engine does not report `switch` and `try` statements as executable lines, as well as parts of multi-line assignment statements, so they are filtered out as well. |
return | array | Returns an array of the executable line numbers of the class. |
public static function executable($class, array $options = array())
{
$defaults = array('self' => true, 'filter' => true, 'methods' => array(), 'empty' => array(' ', "\t", '}', ')', ';'), 'pattern' => null, 'blockOpeners' => array('switch (', 'try {', '} else {', 'do {', '} while'));
$options += $defaults;
if (empty($options['pattern']) && $options['filter']) {
$pattern = str_replace(' ', '\\s*', join('|', array_map(function ($str) {
return preg_quote($str, '/');
}, $options['blockOpeners'])));
$pattern = join('|', array("({$pattern})", "\\\$(.+)\\(\$", "\\s*['\"]\\w+['\"]\\s*=>\\s*.+[\\{\\(]\$", "\\s*['\"]\\w+['\"]\\s*=>\\s*['\"]*.+['\"]*\\s*"));
$options['pattern'] = "/^({$pattern})/";
}
if (!$class instanceof ReflectionClass) {
$class = new ReflectionClass(is_object($class) ? get_class($class) : $class);
}
$options += array('group' => false);
$result = array_filter(static::methods($class, 'ranges', $options));
if ($options['filter'] && $class->getFileName() && $result) {
$lines = static::lines($class->getFileName(), $result);
$start = key($lines);
$code = implode("\n", $lines);
$tokens = token_get_all('<' . '?php' . $code);
$tmp = array();
foreach ($tokens as $token) {
if (is_array($token)) {
if (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT, T_WHITESPACE))) {
$tmp[] = $token[2];
}
}
}
$filteredLines = array_values(array_map(function ($ln) use($start) {
return $ln + $start - 1;
}, array_unique($tmp)));
$lines = array_intersect_key($lines, array_flip($filteredLines));
$result = array_keys(array_filter($lines, function ($line) use($options) {
$line = trim($line);
$empty = preg_match($options['pattern'], $line);
return $empty ? false : str_replace($options['empty'], '', $line) !== '';
}));
}
return $result;
}
/** * Analyzes code coverage results collected from XDebug, and performs coverage density analysis. * * @param object $report The report instance running this filter and aggregating results * @param array $classes A list of classes to analyze coverage on. By default, gets all * defined subclasses of lithium\test\Unit which are currently in memory. * @return array|void Returns an array indexed by file and line, showing the number of * instances each line was called. */ public static function analyze($report, array $classes = array()) { $data = static::collect($report->results['filters'][__CLASS__]); $classes = $classes ?: array_filter(get_declared_classes(), function ($class) use($data) { $unit = 'lithium\\test\\Unit'; return !is_subclass_of($class, $unit) || array_key_exists($class, $data); }); $classes = array_values(array_intersect((array) $classes, array_keys($data))); $densities = $result = array(); foreach ($classes as $class) { $classMap = array($class => Libraries::path($class)); $densities += static::_density($data[$class], $classMap); } $executableLines = array(); if (!empty($classes)) { $executableLines = array_combine($classes, array_map(function ($cls) { return Inspector::executable($cls, array('public' => false)); }, $classes)); } foreach ($densities as $class => $density) { $executable = $executableLines[$class]; $covered = array_intersect(array_keys($density), $executable); $uncovered = array_diff($executable, $covered); $percentage = round(count($covered) / (count($executable) ?: 1), 4) * 100; $result[$class] = compact('class', 'executable', 'covered', 'uncovered', 'percentage'); } $result = static::collectLines($result); return $result; }