comparison vendor/phpunit/php-code-coverage/src/CodeCoverage.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2 /*
3 * This file is part of the PHP_CodeCoverage 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
11 use SebastianBergmann\Environment\Runtime;
12
13 /**
14 * Provides collection functionality for PHP code coverage information.
15 *
16 * @since Class available since Release 1.0.0
17 */
18 class PHP_CodeCoverage
19 {
20 /**
21 * @var PHP_CodeCoverage_Driver
22 */
23 private $driver;
24
25 /**
26 * @var PHP_CodeCoverage_Filter
27 */
28 private $filter;
29
30 /**
31 * @var bool
32 */
33 private $cacheTokens = false;
34
35 /**
36 * @var bool
37 */
38 private $checkForUnintentionallyCoveredCode = false;
39
40 /**
41 * @var bool
42 */
43 private $forceCoversAnnotation = false;
44
45 /**
46 * @var bool
47 */
48 private $mapTestClassNameToCoveredClassName = false;
49
50 /**
51 * @var bool
52 */
53 private $addUncoveredFilesFromWhitelist = true;
54
55 /**
56 * @var bool
57 */
58 private $processUncoveredFilesFromWhitelist = false;
59
60 /**
61 * @var mixed
62 */
63 private $currentId;
64
65 /**
66 * Code coverage data.
67 *
68 * @var array
69 */
70 private $data = array();
71
72 /**
73 * @var array
74 */
75 private $ignoredLines = array();
76
77 /**
78 * @var bool
79 */
80 private $disableIgnoredLines = false;
81
82 /**
83 * Test data.
84 *
85 * @var array
86 */
87 private $tests = array();
88
89 /**
90 * Constructor.
91 *
92 * @param PHP_CodeCoverage_Driver $driver
93 * @param PHP_CodeCoverage_Filter $filter
94 * @throws PHP_CodeCoverage_Exception
95 */
96 public function __construct(PHP_CodeCoverage_Driver $driver = null, PHP_CodeCoverage_Filter $filter = null)
97 {
98 if ($driver === null) {
99 $driver = $this->selectDriver();
100 }
101
102 if ($filter === null) {
103 $filter = new PHP_CodeCoverage_Filter;
104 }
105
106 $this->driver = $driver;
107 $this->filter = $filter;
108 }
109
110 /**
111 * Returns the PHP_CodeCoverage_Report_Node_* object graph
112 * for this PHP_CodeCoverage object.
113 *
114 * @return PHP_CodeCoverage_Report_Node_Directory
115 * @since Method available since Release 1.1.0
116 */
117 public function getReport()
118 {
119 $factory = new PHP_CodeCoverage_Report_Factory;
120
121 return $factory->create($this);
122 }
123
124 /**
125 * Clears collected code coverage data.
126 */
127 public function clear()
128 {
129 $this->currentId = null;
130 $this->data = array();
131 $this->tests = array();
132 }
133
134 /**
135 * Returns the PHP_CodeCoverage_Filter used.
136 *
137 * @return PHP_CodeCoverage_Filter
138 */
139 public function filter()
140 {
141 return $this->filter;
142 }
143
144 /**
145 * Returns the collected code coverage data.
146 * Set $raw = true to bypass all filters.
147 *
148 * @param bool $raw
149 * @return array
150 * @since Method available since Release 1.1.0
151 */
152 public function getData($raw = false)
153 {
154 if (!$raw && $this->addUncoveredFilesFromWhitelist) {
155 $this->addUncoveredFilesFromWhitelist();
156 }
157
158 // We need to apply the blacklist filter a second time
159 // when no whitelist is used.
160 if (!$raw && !$this->filter->hasWhitelist()) {
161 $this->applyListsFilter($this->data);
162 }
163
164 return $this->data;
165 }
166
167 /**
168 * Sets the coverage data.
169 *
170 * @param array $data
171 * @since Method available since Release 2.0.0
172 */
173 public function setData(array $data)
174 {
175 $this->data = $data;
176 }
177
178 /**
179 * Returns the test data.
180 *
181 * @return array
182 * @since Method available since Release 1.1.0
183 */
184 public function getTests()
185 {
186 return $this->tests;
187 }
188
189 /**
190 * Sets the test data.
191 *
192 * @param array $tests
193 * @since Method available since Release 2.0.0
194 */
195 public function setTests(array $tests)
196 {
197 $this->tests = $tests;
198 }
199
200 /**
201 * Start collection of code coverage information.
202 *
203 * @param mixed $id
204 * @param bool $clear
205 * @throws PHP_CodeCoverage_Exception
206 */
207 public function start($id, $clear = false)
208 {
209 if (!is_bool($clear)) {
210 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
211 1,
212 'boolean'
213 );
214 }
215
216 if ($clear) {
217 $this->clear();
218 }
219
220 $this->currentId = $id;
221
222 $this->driver->start();
223 }
224
225 /**
226 * Stop collection of code coverage information.
227 *
228 * @param bool $append
229 * @param mixed $linesToBeCovered
230 * @param array $linesToBeUsed
231 * @return array
232 * @throws PHP_CodeCoverage_Exception
233 */
234 public function stop($append = true, $linesToBeCovered = array(), array $linesToBeUsed = array())
235 {
236 if (!is_bool($append)) {
237 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
238 1,
239 'boolean'
240 );
241 }
242
243 if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) {
244 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
245 2,
246 'array or false'
247 );
248 }
249
250 $data = $this->driver->stop();
251 $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed);
252
253 $this->currentId = null;
254
255 return $data;
256 }
257
258 /**
259 * Appends code coverage data.
260 *
261 * @param array $data
262 * @param mixed $id
263 * @param bool $append
264 * @param mixed $linesToBeCovered
265 * @param array $linesToBeUsed
266 * @throws PHP_CodeCoverage_Exception
267 */
268 public function append(array $data, $id = null, $append = true, $linesToBeCovered = array(), array $linesToBeUsed = array())
269 {
270 if ($id === null) {
271 $id = $this->currentId;
272 }
273
274 if ($id === null) {
275 throw new PHP_CodeCoverage_Exception;
276 }
277
278 $this->applyListsFilter($data);
279 $this->applyIgnoredLinesFilter($data);
280 $this->initializeFilesThatAreSeenTheFirstTime($data);
281
282 if (!$append) {
283 return;
284 }
285
286 if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') {
287 $this->applyCoversAnnotationFilter(
288 $data,
289 $linesToBeCovered,
290 $linesToBeUsed
291 );
292 }
293
294 if (empty($data)) {
295 return;
296 }
297
298 $size = 'unknown';
299 $status = null;
300
301 if ($id instanceof PHPUnit_Framework_TestCase) {
302 $_size = $id->getSize();
303
304 if ($_size == PHPUnit_Util_Test::SMALL) {
305 $size = 'small';
306 } elseif ($_size == PHPUnit_Util_Test::MEDIUM) {
307 $size = 'medium';
308 } elseif ($_size == PHPUnit_Util_Test::LARGE) {
309 $size = 'large';
310 }
311
312 $status = $id->getStatus();
313 $id = get_class($id) . '::' . $id->getName();
314 } elseif ($id instanceof PHPUnit_Extensions_PhptTestCase) {
315 $size = 'large';
316 $id = $id->getName();
317 }
318
319 $this->tests[$id] = array('size' => $size, 'status' => $status);
320
321 foreach ($data as $file => $lines) {
322 if (!$this->filter->isFile($file)) {
323 continue;
324 }
325
326 foreach ($lines as $k => $v) {
327 if ($v == PHP_CodeCoverage_Driver::LINE_EXECUTED) {
328 if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) {
329 $this->data[$file][$k][] = $id;
330 }
331 }
332 }
333 }
334 }
335
336 /**
337 * Merges the data from another instance of PHP_CodeCoverage.
338 *
339 * @param PHP_CodeCoverage $that
340 */
341 public function merge(PHP_CodeCoverage $that)
342 {
343 $this->filter->setBlacklistedFiles(
344 array_merge($this->filter->getBlacklistedFiles(), $that->filter()->getBlacklistedFiles())
345 );
346
347 $this->filter->setWhitelistedFiles(
348 array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles())
349 );
350
351 foreach ($that->data as $file => $lines) {
352 if (!isset($this->data[$file])) {
353 if (!$this->filter->isFiltered($file)) {
354 $this->data[$file] = $lines;
355 }
356
357 continue;
358 }
359
360 foreach ($lines as $line => $data) {
361 if ($data !== null) {
362 if (!isset($this->data[$file][$line])) {
363 $this->data[$file][$line] = $data;
364 } else {
365 $this->data[$file][$line] = array_unique(
366 array_merge($this->data[$file][$line], $data)
367 );
368 }
369 }
370 }
371 }
372
373 $this->tests = array_merge($this->tests, $that->getTests());
374
375 }
376
377 /**
378 * @param bool $flag
379 * @throws PHP_CodeCoverage_Exception
380 * @since Method available since Release 1.1.0
381 */
382 public function setCacheTokens($flag)
383 {
384 if (!is_bool($flag)) {
385 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
386 1,
387 'boolean'
388 );
389 }
390
391 $this->cacheTokens = $flag;
392 }
393
394 /**
395 * @since Method available since Release 1.1.0
396 */
397 public function getCacheTokens()
398 {
399 return $this->cacheTokens;
400 }
401
402 /**
403 * @param bool $flag
404 * @throws PHP_CodeCoverage_Exception
405 * @since Method available since Release 2.0.0
406 */
407 public function setCheckForUnintentionallyCoveredCode($flag)
408 {
409 if (!is_bool($flag)) {
410 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
411 1,
412 'boolean'
413 );
414 }
415
416 $this->checkForUnintentionallyCoveredCode = $flag;
417 }
418
419 /**
420 * @param bool $flag
421 * @throws PHP_CodeCoverage_Exception
422 */
423 public function setForceCoversAnnotation($flag)
424 {
425 if (!is_bool($flag)) {
426 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
427 1,
428 'boolean'
429 );
430 }
431
432 $this->forceCoversAnnotation = $flag;
433 }
434
435 /**
436 * @param bool $flag
437 * @throws PHP_CodeCoverage_Exception
438 */
439 public function setMapTestClassNameToCoveredClassName($flag)
440 {
441 if (!is_bool($flag)) {
442 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
443 1,
444 'boolean'
445 );
446 }
447
448 $this->mapTestClassNameToCoveredClassName = $flag;
449 }
450
451 /**
452 * @param bool $flag
453 * @throws PHP_CodeCoverage_Exception
454 */
455 public function setAddUncoveredFilesFromWhitelist($flag)
456 {
457 if (!is_bool($flag)) {
458 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
459 1,
460 'boolean'
461 );
462 }
463
464 $this->addUncoveredFilesFromWhitelist = $flag;
465 }
466
467 /**
468 * @param bool $flag
469 * @throws PHP_CodeCoverage_Exception
470 */
471 public function setProcessUncoveredFilesFromWhitelist($flag)
472 {
473 if (!is_bool($flag)) {
474 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
475 1,
476 'boolean'
477 );
478 }
479
480 $this->processUncoveredFilesFromWhitelist = $flag;
481 }
482
483 /**
484 * @param bool $flag
485 * @throws PHP_CodeCoverage_Exception
486 */
487 public function setDisableIgnoredLines($flag)
488 {
489 if (!is_bool($flag)) {
490 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
491 1,
492 'boolean'
493 );
494 }
495
496 $this->disableIgnoredLines = $flag;
497 }
498
499 /**
500 * Applies the @covers annotation filtering.
501 *
502 * @param array $data
503 * @param mixed $linesToBeCovered
504 * @param array $linesToBeUsed
505 * @throws PHP_CodeCoverage_Exception_UnintentionallyCoveredCode
506 */
507 private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed)
508 {
509 if ($linesToBeCovered === false ||
510 ($this->forceCoversAnnotation && empty($linesToBeCovered))) {
511 $data = array();
512
513 return;
514 }
515
516 if (empty($linesToBeCovered)) {
517 return;
518 }
519
520 if ($this->checkForUnintentionallyCoveredCode) {
521 $this->performUnintentionallyCoveredCodeCheck(
522 $data,
523 $linesToBeCovered,
524 $linesToBeUsed
525 );
526 }
527
528 $data = array_intersect_key($data, $linesToBeCovered);
529
530 foreach (array_keys($data) as $filename) {
531 $_linesToBeCovered = array_flip($linesToBeCovered[$filename]);
532
533 $data[$filename] = array_intersect_key(
534 $data[$filename],
535 $_linesToBeCovered
536 );
537 }
538 }
539
540 /**
541 * Applies the blacklist/whitelist filtering.
542 *
543 * @param array $data
544 */
545 private function applyListsFilter(array &$data)
546 {
547 foreach (array_keys($data) as $filename) {
548 if ($this->filter->isFiltered($filename)) {
549 unset($data[$filename]);
550 }
551 }
552 }
553
554 /**
555 * Applies the "ignored lines" filtering.
556 *
557 * @param array $data
558 */
559 private function applyIgnoredLinesFilter(array &$data)
560 {
561 foreach (array_keys($data) as $filename) {
562 if (!$this->filter->isFile($filename)) {
563 continue;
564 }
565
566 foreach ($this->getLinesToBeIgnored($filename) as $line) {
567 unset($data[$filename][$line]);
568 }
569 }
570 }
571
572 /**
573 * @param array $data
574 * @since Method available since Release 1.1.0
575 */
576 private function initializeFilesThatAreSeenTheFirstTime(array $data)
577 {
578 foreach ($data as $file => $lines) {
579 if ($this->filter->isFile($file) && !isset($this->data[$file])) {
580 $this->data[$file] = array();
581
582 foreach ($lines as $k => $v) {
583 $this->data[$file][$k] = $v == -2 ? null : array();
584 }
585 }
586 }
587 }
588
589 /**
590 * Processes whitelisted files that are not covered.
591 */
592 private function addUncoveredFilesFromWhitelist()
593 {
594 $data = array();
595 $uncoveredFiles = array_diff(
596 $this->filter->getWhitelist(),
597 array_keys($this->data)
598 );
599
600 foreach ($uncoveredFiles as $uncoveredFile) {
601 if (!file_exists($uncoveredFile)) {
602 continue;
603 }
604
605 if ($this->processUncoveredFilesFromWhitelist) {
606 $this->processUncoveredFileFromWhitelist(
607 $uncoveredFile,
608 $data,
609 $uncoveredFiles
610 );
611 } else {
612 $data[$uncoveredFile] = array();
613
614 $lines = count(file($uncoveredFile));
615
616 for ($i = 1; $i <= $lines; $i++) {
617 $data[$uncoveredFile][$i] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED;
618 }
619 }
620 }
621
622 $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
623 }
624
625 /**
626 * @param string $uncoveredFile
627 * @param array $data
628 * @param array $uncoveredFiles
629 */
630 private function processUncoveredFileFromWhitelist($uncoveredFile, array &$data, array $uncoveredFiles)
631 {
632 $this->driver->start();
633 include_once $uncoveredFile;
634 $coverage = $this->driver->stop();
635
636 foreach ($coverage as $file => $fileCoverage) {
637 if (!isset($data[$file]) &&
638 in_array($file, $uncoveredFiles)) {
639 foreach (array_keys($fileCoverage) as $key) {
640 if ($fileCoverage[$key] == PHP_CodeCoverage_Driver::LINE_EXECUTED) {
641 $fileCoverage[$key] = PHP_CodeCoverage_Driver::LINE_NOT_EXECUTED;
642 }
643 }
644
645 $data[$file] = $fileCoverage;
646 }
647 }
648 }
649
650 /**
651 * Returns the lines of a source file that should be ignored.
652 *
653 * @param string $filename
654 * @return array
655 * @throws PHP_CodeCoverage_Exception
656 * @since Method available since Release 2.0.0
657 */
658 private function getLinesToBeIgnored($filename)
659 {
660 if (!is_string($filename)) {
661 throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
662 1,
663 'string'
664 );
665 }
666
667 if (!isset($this->ignoredLines[$filename])) {
668 $this->ignoredLines[$filename] = array();
669
670 if ($this->disableIgnoredLines) {
671 return $this->ignoredLines[$filename];
672 }
673
674 $ignore = false;
675 $stop = false;
676 $lines = file($filename);
677 $numLines = count($lines);
678
679 foreach ($lines as $index => $line) {
680 if (!trim($line)) {
681 $this->ignoredLines[$filename][] = $index + 1;
682 }
683 }
684
685 if ($this->cacheTokens) {
686 $tokens = PHP_Token_Stream_CachingFactory::get($filename);
687 } else {
688 $tokens = new PHP_Token_Stream($filename);
689 }
690
691 $classes = array_merge($tokens->getClasses(), $tokens->getTraits());
692 $tokens = $tokens->tokens();
693
694 foreach ($tokens as $token) {
695 switch (get_class($token)) {
696 case 'PHP_Token_COMMENT':
697 case 'PHP_Token_DOC_COMMENT':
698 $_token = trim($token);
699 $_line = trim($lines[$token->getLine() - 1]);
700
701 if ($_token == '// @codeCoverageIgnore' ||
702 $_token == '//@codeCoverageIgnore') {
703 $ignore = true;
704 $stop = true;
705 } elseif ($_token == '// @codeCoverageIgnoreStart' ||
706 $_token == '//@codeCoverageIgnoreStart') {
707 $ignore = true;
708 } elseif ($_token == '// @codeCoverageIgnoreEnd' ||
709 $_token == '//@codeCoverageIgnoreEnd') {
710 $stop = true;
711 }
712
713 if (!$ignore) {
714 $start = $token->getLine();
715 $end = $start + substr_count($token, "\n");
716
717 // Do not ignore the first line when there is a token
718 // before the comment
719 if (0 !== strpos($_token, $_line)) {
720 $start++;
721 }
722
723 for ($i = $start; $i < $end; $i++) {
724 $this->ignoredLines[$filename][] = $i;
725 }
726
727 // A DOC_COMMENT token or a COMMENT token starting with "/*"
728 // does not contain the final \n character in its text
729 if (isset($lines[$i-1]) && 0 === strpos($_token, '/*') && '*/' === substr(trim($lines[$i-1]), -2)) {
730 $this->ignoredLines[$filename][] = $i;
731 }
732 }
733 break;
734
735 case 'PHP_Token_INTERFACE':
736 case 'PHP_Token_TRAIT':
737 case 'PHP_Token_CLASS':
738 case 'PHP_Token_FUNCTION':
739 $docblock = $token->getDocblock();
740
741 $this->ignoredLines[$filename][] = $token->getLine();
742
743 if (strpos($docblock, '@codeCoverageIgnore') || strpos($docblock, '@deprecated')) {
744 $endLine = $token->getEndLine();
745
746 for ($i = $token->getLine(); $i <= $endLine; $i++) {
747 $this->ignoredLines[$filename][] = $i;
748 }
749 } elseif ($token instanceof PHP_Token_INTERFACE ||
750 $token instanceof PHP_Token_TRAIT ||
751 $token instanceof PHP_Token_CLASS) {
752 if (empty($classes[$token->getName()]['methods'])) {
753 for ($i = $token->getLine();
754 $i <= $token->getEndLine();
755 $i++) {
756 $this->ignoredLines[$filename][] = $i;
757 }
758 } else {
759 $firstMethod = array_shift(
760 $classes[$token->getName()]['methods']
761 );
762
763 do {
764 $lastMethod = array_pop(
765 $classes[$token->getName()]['methods']
766 );
767 } while ($lastMethod !== null &&
768 substr($lastMethod['signature'], 0, 18) == 'anonymous function');
769
770 if ($lastMethod === null) {
771 $lastMethod = $firstMethod;
772 }
773
774 for ($i = $token->getLine();
775 $i < $firstMethod['startLine'];
776 $i++) {
777 $this->ignoredLines[$filename][] = $i;
778 }
779
780 for ($i = $token->getEndLine();
781 $i > $lastMethod['endLine'];
782 $i--) {
783 $this->ignoredLines[$filename][] = $i;
784 }
785 }
786 }
787 break;
788
789 case 'PHP_Token_NAMESPACE':
790 $this->ignoredLines[$filename][] = $token->getEndLine();
791
792 // Intentional fallthrough
793 case 'PHP_Token_OPEN_TAG':
794 case 'PHP_Token_CLOSE_TAG':
795 case 'PHP_Token_USE':
796 $this->ignoredLines[$filename][] = $token->getLine();
797 break;
798 }
799
800 if ($ignore) {
801 $this->ignoredLines[$filename][] = $token->getLine();
802
803 if ($stop) {
804 $ignore = false;
805 $stop = false;
806 }
807 }
808 }
809
810 $this->ignoredLines[$filename][] = $numLines + 1;
811
812 $this->ignoredLines[$filename] = array_unique(
813 $this->ignoredLines[$filename]
814 );
815
816 sort($this->ignoredLines[$filename]);
817 }
818
819 return $this->ignoredLines[$filename];
820 }
821
822 /**
823 * @param array $data
824 * @param array $linesToBeCovered
825 * @param array $linesToBeUsed
826 * @throws PHP_CodeCoverage_Exception_UnintentionallyCoveredCode
827 * @since Method available since Release 2.0.0
828 */
829 private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed)
830 {
831 $allowedLines = $this->getAllowedLines(
832 $linesToBeCovered,
833 $linesToBeUsed
834 );
835
836 $message = '';
837
838 foreach ($data as $file => $_data) {
839 foreach ($_data as $line => $flag) {
840 if ($flag == 1 &&
841 (!isset($allowedLines[$file]) ||
842 !isset($allowedLines[$file][$line]))) {
843 $message .= sprintf(
844 '- %s:%d' . PHP_EOL,
845 $file,
846 $line
847 );
848 }
849 }
850 }
851
852 if (!empty($message)) {
853 throw new PHP_CodeCoverage_Exception_UnintentionallyCoveredCode(
854 $message
855 );
856 }
857 }
858
859 /**
860 * @param array $linesToBeCovered
861 * @param array $linesToBeUsed
862 * @return array
863 * @since Method available since Release 2.0.0
864 */
865 private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed)
866 {
867 $allowedLines = array();
868
869 foreach (array_keys($linesToBeCovered) as $file) {
870 if (!isset($allowedLines[$file])) {
871 $allowedLines[$file] = array();
872 }
873
874 $allowedLines[$file] = array_merge(
875 $allowedLines[$file],
876 $linesToBeCovered[$file]
877 );
878 }
879
880 foreach (array_keys($linesToBeUsed) as $file) {
881 if (!isset($allowedLines[$file])) {
882 $allowedLines[$file] = array();
883 }
884
885 $allowedLines[$file] = array_merge(
886 $allowedLines[$file],
887 $linesToBeUsed[$file]
888 );
889 }
890
891 foreach (array_keys($allowedLines) as $file) {
892 $allowedLines[$file] = array_flip(
893 array_unique($allowedLines[$file])
894 );
895 }
896
897 return $allowedLines;
898 }
899
900 /**
901 * @return PHP_CodeCoverage_Driver
902 * @throws PHP_CodeCoverage_Exception
903 */
904 private function selectDriver()
905 {
906 $runtime = new Runtime;
907
908 if (!$runtime->canCollectCodeCoverage()) {
909 throw new PHP_CodeCoverage_Exception('No code coverage driver available');
910 }
911
912 if ($runtime->isHHVM()) {
913 return new PHP_CodeCoverage_Driver_HHVM;
914 } elseif ($runtime->isPHPDBG()) {
915 return new PHP_CodeCoverage_Driver_PHPDBG;
916 } else {
917 return new PHP_CodeCoverage_Driver_Xdebug;
918 }
919 }
920 }