CodeCoverageListener::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 4
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the leanphp/phpspec-code-coverage package
4
 *
5
 * @author  ek9 <[email protected]>
6
 *
7
 * @license MIT
8
 *
9
 * For the full copyright and license information, please see the LICENSE file
10
 * that was distributed with this source code.
11
 *
12
 */
13
namespace LeanPHP\PhpSpec\CodeCoverage\Listener;
14
15
use PhpSpec\Console\ConsoleIO;
16
use PhpSpec\Event\ExampleEvent;
17
use PhpSpec\Event\SuiteEvent;
18
use SebastianBergmann\CodeCoverage\CodeCoverage;
19
use SebastianBergmann\CodeCoverage\Report;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
22
/**
23
 * @author Henrik Bjornskov
24
 */
25
class CodeCoverageListener implements EventSubscriberInterface
26
{
27
    private $coverage;
28
    private $reports;
29
    private $io;
30
    private $options;
31
    private $enabled;
32
    private $skipCoverage;
33
34
    /**
35
     * @param ConsoleIO    $io
36
     * @param CodeCoverage $coverage
37
     * @param array        $reports
38
     * @param boolean      $skipCoverage
39
     */
40
    public function __construct(ConsoleIO $io, CodeCoverage $coverage, array $reports, $skipCoverage = false)
41
    {
42
        $this->io = $io;
43
        $this->coverage = $coverage;
44
        $this->reports  = $reports;
45
        $this->options  = [
46
            'whitelist' => ['src', 'lib'],
47
            'blacklist' => ['test', 'vendor', 'spec'],
48
            'whitelist_files' => [],
49
            'blacklist_files' => [],
50
            'output'    => ['html' => 'coverage'],
51
            'format'    => ['html'],
52
        ];
53
54
        $this->enabled = extension_loaded('xdebug') || (PHP_SAPI === 'phpdbg');
55
        $this->skipCoverage = $skipCoverage;
56
    }
57
58
    /**
59
     * Note: We use array_map() instead of array_walk() because the latter expects
60
     * the callback to take the value as the first and the index as the seconds parameter.
61
     *
62
     * @param SuiteEvent $event
63
     */
64
    public function beforeSuite(SuiteEvent $event) : void
65
    {
66
        if (!$this->enabled || $this->skipCoverage) {
67
            return;
68
        }
69
70
        $filter = $this->coverage->filter();
71
72
        array_map(
73
            [$filter, 'addDirectoryToWhitelist'],
74
            $this->options['whitelist']
75
        );
76
        array_map(
77
            [$filter, 'removeDirectoryFromWhitelist'],
78
            $this->options['blacklist']
79
        );
80
        array_map(
81
            [$filter, 'addFileToWhitelist'],
82
            $this->options['whitelist_files']
83
        );
84
        array_map(
85
            [$filter, 'removeFileFromWhitelist'],
86
            $this->options['blacklist_files']
87
        );
88
    }
89
90
    /**
91
     * @param ExampleEvent $event
92
     */
93
    public function beforeExample(ExampleEvent $event): void
94
    {
95
        if (!$this->enabled || $this->skipCoverage) {
96
            return;
97
        }
98
99
        $example = $event->getExample();
100
101
        $name = strtr('%spec%::%example%', [
102
            '%spec%' => $example->getSpecification()->getClassReflection()->getName(),
103
            '%example%' => $example->getFunctionReflection()->getName(),
104
        ]);
105
106
        $this->coverage->start($name);
107
    }
108
109
    /**
110
     * @param ExampleEvent $event
111
     */
112
    public function afterExample(ExampleEvent $event): void
113
    {
114
        if (!$this->enabled || $this->skipCoverage) {
115
            return;
116
        }
117
118
        $this->coverage->stop();
119
    }
120
121
    /**
122
     * @param SuiteEvent $event
123
     */
124
    public function afterSuite(SuiteEvent $event): void
125
    {
126
        if (!$this->enabled || $this->skipCoverage) {
127
            if ($this->io && $this->io->isVerbose()) {
128
                if (!$this->enabled) {
129
                    $this->io->writeln('No code coverage will be generated as neither Xdebug nor phpdbg was detected.');
130
                } elseif ($this->skipCoverage) {
131
                    $this->io->writeln('Skipping code coverage generation');
132
                }
133
            }
134
135
            return;
136
        }
137
138
        if ($this->io && $this->io->isVerbose()) {
139
            $this->io->writeln('');
140
        }
141
142
        foreach ($this->reports as $format => $report) {
143
            if ($this->io && $this->io->isVerbose()) {
144
                $this->io->writeln(sprintf('Generating code coverage report in %s format ...', $format));
145
            }
146
147
            if ($report instanceof Report\Text) {
148
                $output = $report->process($this->coverage, /* showColors */ $this->io->isDecorated());
149
                $this->io->writeln($output);
150
            } else {
151
                $report->process($this->coverage, $this->options['output'][$format]);
152
            }
153
        }
154
    }
155
156
    /**
157
     * @param array $options
158
     */
159
    public function setOptions(array $options): void
160
    {
161
        $this->options = $options + $this->options;
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167
    public static function getSubscribedEvents(): array
168
    {
169
        return [
170
            'beforeExample' => ['beforeExample', -10],
171
            'afterExample'  => ['afterExample', -10],
172
            'beforeSuite'   => ['beforeSuite', -10],
173
            'afterSuite'    => ['afterSuite', -10],
174
        ];
175
    }
176
}
177