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