• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JBZoo / PHPUnit / 5048293906

pending completion
5048293906

push

github

GitHub
Rollback to PHPUnit 9 (#26)

170 of 261 relevant lines covered (65.13%)

1.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/CovCatcher.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - PHPUnit.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/PHPUnit
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\PHPUnit;
18

19
use JBZoo\Data\Data;
20
use JBZoo\Utils\Env;
21
use JBZoo\Utils\Sys;
22
use SebastianBergmann\CodeCoverage\CodeCoverage;
23
use SebastianBergmann\CodeCoverage\Driver\Selector;
24
use SebastianBergmann\CodeCoverage\Filter;
25
use SebastianBergmann\CodeCoverage\Report\Clover;
26
use SebastianBergmann\CodeCoverage\Report\Html\Facade;
27
use SebastianBergmann\CodeCoverage\Report\PHP;
28
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
29

30
/**
31
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
32
 */
33
class CovCatcher
34
{
35
    public const MODE_REQUIRE      = 'require';
36
    public const MODE_REQUIRE_ONCE = 'require_once';
37

38
    protected bool $isStarted = false;
39

40
    protected ?CodeCoverage $coverage;
41

42
    /** @var array<bool|string> */
43
    protected array $default = [
44
        'cov'        => true,
45
        'xml'        => false,
46
        'html'       => false,
47
        'src'        => './src',
48
        'build_xml'  => './build/coverage_xml',
49
        'build_cov'  => './build/coverage_cov',
50
        'build_html' => './build/coverage_html',
51
    ];
52

53
    protected Data $config;
54

55
    protected string $hash = '';
56

57
    /**
58
     * @param array<bool|string> $options
59
     */
60
    public function __construct(string $testName = '', array $options = [])
61
    {
62
        if (!\class_exists(Data::class)) {
63
            throw new Exception('jbzoo/data is required for CovCatcher');
64
        }
65

66
        if (!\class_exists(Env::class)) {
67
            throw new Exception('jbzoo/utils is required for CovCatcher');
68
        }
69

70
        $this->initConfig($options);
71

72
        $postFixName = \str_replace('.', '', \uniqid('', true));
73
        $this->hash  = $testName !== '' ? "{$testName}__{$postFixName}" : $postFixName;
74

75
        $this->coverage = null;
76
        if (Sys::hasXdebug()) {
77
            $covFilter = self::prepareFilter($this->config->getString('src'));
78
            $driver    = (new Selector())->forLineAndPathCoverage($covFilter);
79

80
            $this->coverage = new CodeCoverage($driver, $covFilter);
81
        }
82
    }
83

84
    /**
85
     * Save report.
86
     */
87
    public function __destruct()
88
    {
89
        $this->stop();
90
        $this->createReports();
91
    }
92

93
    public function includeFile(string $filename, string $mode = self::MODE_REQUIRE): mixed
94
    {
95
        $this->start();
96

97
        $realpath = (string)\realpath($filename);
2✔
98

99
        if ($realpath !== '' && \file_exists($realpath) && \is_file($realpath)) {
2✔
100
            if ($mode === self::MODE_REQUIRE) {
2✔
101
                /** @psalm-suppress UnresolvableInclude */
102
                $result = require $realpath;
2✔
103
            } elseif ($mode === self::MODE_REQUIRE_ONCE) {
104
                /** @psalm-suppress UnresolvableInclude */
105
                $result = require_once $realpath;
106
            } else {
107
                throw new Exception("Undefined mode to include file: \"{$filename}\"");
108
            }
109
        } else {
110
            throw new Exception("Included file not found: \"{$filename}\"");
111
        }
112

113
        $this->stop();
2✔
114

115
        return $result;
116
    }
117

118
    /**
119
     * Start coverage process.
120
     */
121
    protected function start(): void
122
    {
123
        if (!$this->isStarted) {
124
            $this->isStarted = true;
125
            $this->coverage?->start($this->hash, true);
126
        }
127
    }
128

129
    /**
130
     * Stop or pause coverage process.
131
     */
132
    protected function stop(): void
133
    {
134
        if ($this->isStarted) {
2✔
135
            $this->isStarted = false;
2✔
136
            $this->coverage?->stop();
2✔
137
        }
138
    }
139

140
    /**
141
     * Stop or pause coverage process.
142
     */
143
    protected function createReports(): void
144
    {
145
        $reportXmlDir = $this->config->getString('build_xml');
146
        $isXmlEnabled = $this->config->getBool('xml');
147
        if ($isXmlEnabled) {
148
            self::prepareDirectory($reportXmlDir);
149
            $report = new Clover();
150
            if ($this->coverage !== null) {
151
                $report->process($this->coverage, $reportXmlDir . '/' . $this->hash . '.xml');
152
            }
153
        }
154

155
        $reportCovDir = $this->config->getString('build_cov');
156
        $isCovEnabled = $this->config->getBool('cov');
157
        if ($isCovEnabled) {
158
            self::prepareDirectory($reportCovDir);
159
            $report = new PHP();
160
            if ($this->coverage !== null) {
161
                $report->process($this->coverage, $reportCovDir . '/' . $this->hash . '.cov');
162
            }
163
        }
164

165
        $reportHtmlDir = $this->config->getString('build_html');
166
        $isHtmlEnabled = $this->config->getBool('html');
167
        if ($isHtmlEnabled) {
168
            self::prepareDirectory($reportHtmlDir);
169
            $report = new Facade();
170
            if ($this->coverage !== null) {
171
                $report->process($this->coverage, $reportHtmlDir . '/' . $this->hash);
172
            }
173
        }
174
    }
175

176
    /**
177
     * Prepare and init config.
178
     * @param array<null|bool|string> $options
179
     */
180
    protected function initConfig(array $options): void
181
    {
182
        $options = \array_filter($options, static fn ($option) => $option !== null);
183

184
        $this->config = new Data(\array_merge($this->default, $options));
185
    }
186

187
    protected static function prepareFilter(string $dirPath): Filter
188
    {
189
        $covFilter = new Filter();
190

191
        foreach ((new FileIteratorFacade())->getFilesAsArray($dirPath, '.php') as $file) {
192
            $covFilter->includeFile($file);
193
        }
194

195
        return $covFilter;
196
    }
197

198
    protected static function prepareDirectory(string $dirPath): void
199
    {
200
        /** @phan-suppress-next-line PhanPluginDuplicateIfCondition */
201
        if (!\is_dir($dirPath) && !\mkdir($dirPath, 0777, true) && !\is_dir($dirPath)) {
202
            throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dirPath));
203
        }
204
    }
205
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc