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 PHPDBG's code coverage functionality. 17 * 18 * @codeCoverageIgnore 19 */ 20class PHPDBG implements Driver 21{ 22 /** 23 * Constructor. 24 */ 25 public function __construct() 26 { 27 if (PHP_SAPI !== 'phpdbg') { 28 throw new RuntimeException( 29 'This driver requires the PHPDBG SAPI' 30 ); 31 } 32 33 if (!function_exists('phpdbg_start_oplog')) { 34 throw new RuntimeException( 35 'This build of PHPDBG does not support code coverage' 36 ); 37 } 38 } 39 40 /** 41 * Start collection of code coverage information. 42 * 43 * @param bool $determineUnusedAndDead 44 */ 45 public function start($determineUnusedAndDead = true) 46 { 47 phpdbg_start_oplog(); 48 } 49 50 /** 51 * Stop collection of code coverage information. 52 * 53 * @return array 54 */ 55 public function stop() 56 { 57 static $fetchedLines = []; 58 59 $dbgData = phpdbg_end_oplog(); 60 61 if ($fetchedLines == []) { 62 $sourceLines = phpdbg_get_executable(); 63 } else { 64 $newFiles = array_diff( 65 get_included_files(), 66 array_keys($fetchedLines) 67 ); 68 69 if ($newFiles) { 70 $sourceLines = phpdbg_get_executable( 71 ['files' => $newFiles] 72 ); 73 } else { 74 $sourceLines = []; 75 } 76 } 77 78 foreach ($sourceLines as $file => $lines) { 79 foreach ($lines as $lineNo => $numExecuted) { 80 $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; 81 } 82 } 83 84 $fetchedLines = array_merge($fetchedLines, $sourceLines); 85 86 return $this->detectExecutedLines($fetchedLines, $dbgData); 87 } 88 89 /** 90 * Convert phpdbg based data into the format CodeCoverage expects 91 * 92 * @param array $sourceLines 93 * @param array $dbgData 94 * 95 * @return array 96 */ 97 private function detectExecutedLines(array $sourceLines, array $dbgData) 98 { 99 foreach ($dbgData as $file => $coveredLines) { 100 foreach ($coveredLines as $lineNo => $numExecuted) { 101 // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. 102 // make sure we only mark lines executed which are actually executable. 103 if (isset($sourceLines[$file][$lineNo])) { 104 $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; 105 } 106 } 107 } 108 109 return $sourceLines; 110 } 111} 112