Mercurial > hg > isophonics-drupal-site
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 } |