Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace SebastianBergmann\CodeCoverage\Driver; Chris@14: Chris@14: use SebastianBergmann\CodeCoverage\RuntimeException; Chris@14: Chris@14: /** Chris@14: * Driver for Xdebug's code coverage functionality. Chris@14: * Chris@14: * @codeCoverageIgnore Chris@14: */ Chris@14: class Xdebug implements Driver Chris@14: { Chris@14: /** Chris@14: * Cache the number of lines for each file Chris@14: * Chris@14: * @var array Chris@14: */ Chris@14: private $cacheNumLines = []; Chris@14: Chris@14: /** Chris@14: * Constructor. Chris@14: */ Chris@14: public function __construct() Chris@14: { Chris@14: if (!\extension_loaded('xdebug')) { Chris@14: throw new RuntimeException('This driver requires Xdebug'); Chris@14: } Chris@14: Chris@14: if (\version_compare(\phpversion('xdebug'), '2.2.1', '>=') && Chris@14: !\ini_get('xdebug.coverage_enable')) { Chris@14: throw new RuntimeException( Chris@14: 'xdebug.coverage_enable=On has to be set in php.ini' Chris@14: ); Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * Start collection of code coverage information. Chris@14: * Chris@14: * @param bool $determineUnusedAndDead Chris@14: */ Chris@14: public function start($determineUnusedAndDead = true) Chris@14: { Chris@14: if ($determineUnusedAndDead) { Chris@14: \xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); Chris@14: } else { Chris@14: \xdebug_start_code_coverage(); Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * Stop collection of code coverage information. Chris@14: * Chris@14: * @return array Chris@14: */ Chris@14: public function stop() Chris@14: { Chris@14: $data = \xdebug_get_code_coverage(); Chris@14: \xdebug_stop_code_coverage(); Chris@14: Chris@14: return $this->cleanup($data); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param array $data Chris@14: * Chris@14: * @return array Chris@14: */ Chris@14: private function cleanup(array $data) Chris@14: { Chris@14: foreach (\array_keys($data) as $file) { Chris@14: unset($data[$file][0]); Chris@14: Chris@14: if (\strpos($file, 'xdebug://debug-eval') !== 0 && \file_exists($file)) { Chris@14: $numLines = $this->getNumberOfLinesInFile($file); Chris@14: Chris@14: foreach (\array_keys($data[$file]) as $line) { Chris@14: if ($line > $numLines) { Chris@14: unset($data[$file][$line]); Chris@14: } Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: return $data; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param string $file Chris@14: * Chris@14: * @return int Chris@14: */ Chris@14: private function getNumberOfLinesInFile($file) Chris@14: { Chris@14: if (!isset($this->cacheNumLines[$file])) { Chris@14: $buffer = \file_get_contents($file); Chris@14: $lines = \substr_count($buffer, "\n"); Chris@14: Chris@14: if (\substr($buffer, -1) !== "\n") { Chris@14: $lines++; Chris@14: } Chris@14: Chris@14: $this->cacheNumLines[$file] = $lines; Chris@14: } Chris@14: Chris@14: return $this->cacheNumLines[$file]; Chris@14: } Chris@14: }