1<?php 2/* 3 * This file is part of the php-code-coverage package. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace SebastianBergmann\CodeCoverage\Driver; 12 13use SebastianBergmann\CodeCoverage\RuntimeException; 14 15/** 16 * Driver for Xdebug's code coverage functionality. 17 * 18 * @codeCoverageIgnore 19 */ 20class Xdebug implements Driver 21{ 22 /** 23 * Cache the number of lines for each file 24 * 25 * @var array 26 */ 27 private $cacheNumLines = []; 28 29 /** 30 * Constructor. 31 */ 32 public function __construct() 33 { 34 if (!extension_loaded('xdebug')) { 35 throw new RuntimeException('This driver requires Xdebug'); 36 } 37 38 if (version_compare(phpversion('xdebug'), '2.2.1', '>=') && 39 !ini_get('xdebug.coverage_enable')) { 40 throw new RuntimeException( 41 'xdebug.coverage_enable=On has to be set in php.ini' 42 ); 43 } 44 } 45 46 /** 47 * Start collection of code coverage information. 48 * 49 * @param bool $determineUnusedAndDead 50 */ 51 public function start($determineUnusedAndDead = true) 52 { 53 if ($determineUnusedAndDead) { 54 xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); 55 } else { 56 xdebug_start_code_coverage(); 57 } 58 } 59 60 /** 61 * Stop collection of code coverage information. 62 * 63 * @return array 64 */ 65 public function stop() 66 { 67 $data = xdebug_get_code_coverage(); 68 xdebug_stop_code_coverage(); 69 70 return $this->cleanup($data); 71 } 72 73 /** 74 * @param array $data 75 * 76 * @return array 77 */ 78 private function cleanup(array $data) 79 { 80 foreach (array_keys($data) as $file) { 81 unset($data[$file][0]); 82 83 if (strpos($file, 'xdebug://debug-eval') !== 0 && file_exists($file)) { 84 $numLines = $this->getNumberOfLinesInFile($file); 85 86 foreach (array_keys($data[$file]) as $line) { 87 if ($line > $numLines) { 88 unset($data[$file][$line]); 89 } 90 } 91 } 92 } 93 94 return $data; 95 } 96 97 /** 98 * @param string $file 99 * 100 * @return int 101 */ 102 private function getNumberOfLinesInFile($file) 103 { 104 if (!isset($this->cacheNumLines[$file])) { 105 $buffer = file_get_contents($file); 106 $lines = substr_count($buffer, "\n"); 107 108 if (substr($buffer, -1) !== "\n") { 109 $lines++; 110 } 111 112 $this->cacheNumLines[$file] = $lines; 113 } 114 115 return $this->cacheNumLines[$file]; 116 } 117} 118