2020use function explode ;
2121use function file_exists ;
2222use function get_class ;
23- use function in_array ;
2423use function is_array ;
2524use function sort ;
2625use PHPUnit \Framework \TestCase ;
@@ -109,6 +108,13 @@ final class CodeCoverage
109108 */
110109 private $ parentClassesExcludedFromUnintentionallyCoveredCodeCheck = [];
111110
111+ /**
112+ * Determine if the data has been initialized or not.
113+ *
114+ * @var bool
115+ */
116+ private $ isInitialized = false ;
117+
112118 /**
113119 * @var ?CoveredFileAnalyser
114120 */
@@ -145,9 +151,10 @@ public function getReport(): Directory
145151 */
146152 public function clear (): void
147153 {
148- $ this ->currentId = null ;
149- $ this ->data = new ProcessedCodeCoverageData ;
150- $ this ->tests = [];
154+ $ this ->isInitialized = false ;
155+ $ this ->currentId = null ;
156+ $ this ->data = new ProcessedCodeCoverageData ;
157+ $ this ->tests = [];
151158 }
152159
153160 /**
@@ -163,12 +170,8 @@ public function filter(): Filter
163170 */
164171 public function getData (bool $ raw = false ): ProcessedCodeCoverageData
165172 {
166- if (!$ raw ) {
167- if ($ this ->processUncoveredFiles ) {
168- $ this ->processUncoveredFilesFromFilter ();
169- } elseif ($ this ->includeUncoveredFiles ) {
170- $ this ->addUncoveredFilesFromFilter ();
171- }
173+ if (!$ raw && $ this ->includeUncoveredFiles ) {
174+ $ this ->addUncoveredFilesFromFilter ();
172175 }
173176
174177 return $ this ->data ;
@@ -209,6 +212,10 @@ public function start($id, bool $clear = false): void
209212 $ this ->clear ();
210213 }
211214
215+ if ($ this ->isInitialized === false ) {
216+ $ this ->initializeData ();
217+ }
218+
212219 $ this ->currentId = $ id ;
213220
214221 $ this ->driver ->start ();
@@ -519,24 +526,6 @@ private function addUncoveredFilesFromFilter(): void
519526 }
520527 }
521528
522- /**
523- * @throws UnintentionallyCoveredCodeException
524- */
525- private function processUncoveredFilesFromFilter (): void
526- {
527- $ coveredFiles = $ this ->data ->coveredFiles ();
528-
529- $ this ->driver ->start ();
530-
531- foreach ($ this ->filter ->files () as $ file ) {
532- if (!in_array ($ file , $ coveredFiles , true ) && $ this ->filter ->isFile ($ file )) {
533- include_once $ file ;
534- }
535- }
536-
537- $ this ->append ($ this ->driver ->stop (), self ::UNCOVERED_FILES );
538- }
539-
540529 /**
541530 * @throws UnintentionallyCoveredCodeException
542531 * @throws ReflectionException
@@ -639,6 +628,36 @@ private function processUnintentionallyCoveredUnits(array $unintentionallyCovere
639628 return array_values ($ unintentionallyCoveredUnits );
640629 }
641630
631+ /**
632+ * @throws UnintentionallyCoveredCodeException
633+ */
634+ private function initializeData (): void
635+ {
636+ $ this ->isInitialized = true ;
637+
638+ if ($ this ->processUncoveredFiles ) {
639+ // by collecting dead code data here on an initial pass, future runs with test data do not need to
640+ if ($ this ->driver ->canDetectDeadCode ()) {
641+ $ this ->driver ->enableDeadCodeDetection ();
642+ }
643+
644+ $ this ->driver ->start ();
645+
646+ foreach ($ this ->filter ->files () as $ file ) {
647+ if ($ this ->filter ->isFile ($ file )) {
648+ include_once $ file ;
649+ }
650+ }
651+
652+ // having now collected dead code for the entire list of files, we can safely skip this data on subsequent runs
653+ if ($ this ->driver ->canDetectDeadCode ()) {
654+ $ this ->driver ->disableDeadCodeDetection ();
655+ }
656+
657+ $ this ->append ($ this ->driver ->stop (), self ::UNCOVERED_FILES );
658+ }
659+ }
660+
642661 private function coveredFileAnalyser (): CoveredFileAnalyser
643662 {
644663 if ($ this ->coveredFileAnalyser !== null ) {
0 commit comments