Passed
Push — nln-php7 ( 12eaac )
by Nicolas
05:28
created

Parser   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 99.21%

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 8
dl 0
loc 296
ccs 126
cts 127
cp 0.9921
rs 8.8
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A setEOL() 0 6 1
A enableIncludeSupport() 0 9 2
A enableExternalSupport() 0 9 2
A enableGroupSupport() 0 9 2
A parse() 0 20 2
A parseFromMasterFile() 0 21 4
A readFile() 0 30 4
A extractLines() 0 21 3
A trimLines() 0 4 1
A changeCurrentFile() 0 9 2
A extractSectionName() 0 12 2
A switchSectionParser() 0 9 2
A getVariables() 0 4 1
A getFileSystem() 0 4 1
A getExternalVariables() 0 11 2
A printExternalFilesStatus() 0 15 3
A getExternalFilesStatus() 0 11 2
A getGroups() 0 11 2
A postParse() 0 7 2
A isSystem() 0 12 2
A getDefaultEnvironmentsForGroups() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like Parser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Parser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Karma\Configuration;
6
7
use Gaufrette\Filesystem;
8
use Karma\Configuration\Parser\NullParser;
9
use Karma\Configuration\Parser\IncludeParser;
10
use Karma\Configuration\Parser\VariableParser;
11
use Karma\Configuration\Parser\ExternalParser;
12
use Psr\Log\NullLogger;
13
use Karma\Configuration\Parser\GroupParser;
14
15
class Parser implements FileParser
16
{
17
    use \Karma\Logging\LoggerAware;
18
19
    const
20
        INCLUDES = 'includes',
21
        VARIABLES = 'variables',
22
        EXTERNALS = 'externals',
23
        GROUPS = 'groups';
24
25
    private
26
        $parsers,
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
27
        $currentParser,
28
        $parsedFiles,
29
        $fs,
30
        $eol;
31
32 279
    public function __construct(Filesystem $fs)
33
    {
34 279
        $this->logger = new NullLogger();
35
36 279
        $this->parsers = array(
37 279
            self::VARIABLES => new VariableParser(),
38
        );
39
40 279
        $this->parsedFiles = array();
41 279
        $this->fs = $fs;
42 279
        $this->eol = "\n";
43 279
    }
44
45 49
    public function setEOL(string $eol): self
46
    {
47 49
        $this->eol = $eol;
48
49 49
        return $this;
50
    }
51
52 279
    public function enableIncludeSupport(): self
53
    {
54 279
        if(! isset($this->parsers[self::INCLUDES]))
55
        {
56 279
            $this->parsers[self::INCLUDES] = new IncludeParser();
57
        }
58
59 279
        return $this;
60
    }
61
62 279
    public function enableExternalSupport(): self
63
    {
64 279
        if(! isset($this->parsers[self::EXTERNALS]))
65
        {
66 279
            $this->parsers[self::EXTERNALS] = new ExternalParser(new Parser($this->fs));
67
        }
68
69 279
        return $this;
70
    }
71
72 269
    public function enableGroupSupport(): self
73
    {
74 269
        if(! isset($this->parsers[self::GROUPS]))
75
        {
76 269
            $this->parsers[self::GROUPS] = new GroupParser();
77
        }
78
79 269
        return $this;
80
    }
81
82 279
    public function parse(string $masterFilePath): array
83
    {
84
        try
85
        {
86 279
            $this->parseFromMasterFile($masterFilePath);
87
88 246
            $variables = $this->getVariables();
89 246
            $this->printExternalFilesStatus();
90
91 246
            $this->postParse();
92
93 244
            return $variables;
94
        }
95 36
        catch(\RuntimeException $e)
96
        {
97 36
            $this->error($e->getMessage());
98
99 36
            throw $e;
100
        }
101
    }
102
103 279
    private function parseFromMasterFile(string $masterFilePath): void
104
    {
105 279
        $files = array($masterFilePath);
106
107 279
        while(! empty($files))
108
        {
109 279
            foreach($files as $file)
110
            {
111 279
                $this->readFile($file);
112
            }
113
114 248
            if(isset($this->parsers[self::INCLUDES]))
115
            {
116 248
                $includeParser = $this->parsers[self::INCLUDES];
117 248
                $files = $includeParser->getCollectedFiles();
0 ignored issues
show
Bug introduced by
The method getCollectedFiles does only exist in Karma\Configuration\Parser\IncludeParser, but not in Karma\Configuration\Pars...n\Parser\VariableParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
118
            }
119
120
            // Avoid loop
121 248
            $files = array_diff($files, $this->parsedFiles);
122
        }
123 246
    }
124
125 279
    private function readFile(string $filePath): void
126
    {
127 279
        $lines = $this->extractLines($filePath);
128 279
        $this->changeCurrentFile($filePath);
129
130 279
        $this->currentParser = new NullParser();
131 279
        $currentLineNumber = 0;
132
133 279
        foreach($lines as $line)
134
        {
135 279
            $currentLineNumber++;
136
137 279
            if(empty($line))
138
            {
139 216
                continue;
140
            }
141
142 274
            $sectionName = $this->extractSectionName($line);
143 274
            if($sectionName !== null)
144
            {
145 272
                $this->switchSectionParser($sectionName);
146
147 271
                continue;
148
            }
149
150 273
            $this->currentParser->parse($line, $currentLineNumber);
151
        }
152
153 249
        $this->parsers[self::VARIABLES]->endOfFileCheck();
0 ignored issues
show
Bug introduced by
The method endOfFileCheck does only exist in Karma\Configuration\Parser\VariableParser, but not in Karma\Configuration\Pars...on\Parser\IncludeParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
154 248
    }
155
156 279
    private function extractLines(string $filePath): array
157
    {
158 279
        if(! $this->fs->has($filePath))
159
        {
160 1
            throw new \RuntimeException("$filePath does not exist");
161
        }
162
163 279
        $content = $this->fs->read($filePath);
164
165 279
        $lines = explode($this->eol, $content ?? '');
166 279
        $lines = $this->trimLines($lines);
167
168 279
        $this->parsedFiles[] = $filePath;
169
170 279
        if(empty($lines))
171
        {
172
            $this->warning("Empty file ($filePath)");
173
        }
174
175 279
        return $lines;
176
    }
177
178 279
    private function trimLines(array $lines): array
179
    {
180 279
        return array_map('trim', $lines);
181
    }
182
183 279
    private function changeCurrentFile(string $filePath): void
184
    {
185 279
        $this->info("Reading $filePath");
186
187 279
        foreach($this->parsers as $parser)
188
        {
189 279
            $parser->setCurrentFile($filePath);
190
        }
191 279
    }
192
193 274
    private function extractSectionName(string $line): ?string
194
    {
195 274
        $sectionName = null;
196
197
        // [.*]
198 274
        if(preg_match('~^\[(?P<groupName>[^\]]+)\]$~', $line, $matches))
199
        {
200 272
            $sectionName = trim(strtolower($matches['groupName']));
201
        }
202
203 274
        return $sectionName;
204
    }
205
206 272
    private function switchSectionParser(string $sectionName): void
207
    {
208 272
        if(! isset($this->parsers[$sectionName]))
209
        {
210 1
            throw new \RuntimeException('Unknown section name ' . $sectionName);
211
        }
212
213 271
        $this->currentParser = $this->parsers[$sectionName];
214 271
    }
215
216 246
    public function getVariables(): array
217
    {
218 246
        return $this->parsers[self::VARIABLES]->getVariables();
0 ignored issues
show
Bug introduced by
The method getVariables does only exist in Karma\Configuration\Parser\VariableParser, but not in Karma\Configuration\Pars...on\Parser\IncludeParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
219
    }
220
221 209
    public function getFileSystem(): Filesystem
222
    {
223 209
        return $this->fs;
224
    }
225
226 184
    public function getExternalVariables(): array
227
    {
228 184
        $variables = [];
229
230 184
        if(isset($this->parsers[self::EXTERNALS]))
231
        {
232 184
            $variables = $this->parsers[self::EXTERNALS]->getExternalVariables();
0 ignored issues
show
Bug introduced by
The method getExternalVariables does only exist in Karma\Configuration\Parser\ExternalParser, but not in Karma\Configuration\Pars...n\Parser\VariableParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
233
        }
234
235 184
        return $variables;
236
    }
237
238 246
    private function printExternalFilesStatus(): void
239
    {
240 246
        $files = $this->getExternalFilesStatus();
241
242 246
        foreach($files as $file => $status)
243
        {
244 209
            if($status['found'] === false)
245
            {
246 198
                $this->warning(sprintf(
247 198
                   'External file %s was not found',
248 209
                   $file
249
                ));
250
            }
251
        }
252 246
    }
253
254 246
    private function getExternalFilesStatus(): array
255
    {
256 246
        $files = [];
257
258 246
        if(isset($this->parsers[self::EXTERNALS]))
259
        {
260 246
            $files = $this->parsers[self::EXTERNALS]->getExternalFilesStatus();
0 ignored issues
show
Bug introduced by
The method getExternalFilesStatus does only exist in Karma\Configuration\Parser\ExternalParser, but not in Karma\Configuration\Pars...n\Parser\VariableParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
261
        }
262
263 246
        return $files;
264
    }
265
266 46
    public function getGroups(): array
267
    {
268 46
        $groups = [];
269
270 46
        if(isset($this->parsers[self::GROUPS]))
271
        {
272 38
            $groups = $this->parsers[self::GROUPS]->getCollectedGroups();
0 ignored issues
show
Bug introduced by
The method getCollectedGroups does only exist in Karma\Configuration\Parser\GroupParser, but not in Karma\Configuration\Pars...n\Parser\VariableParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
273
        }
274
275 46
        return $groups;
276
    }
277
278 246
    private function postParse(): void
279
    {
280 246
        foreach($this->parsers as $parser)
281
        {
282 246
            $parser->postParse();
283
        }
284 244
    }
285
286 4
    public function isSystem(string $variableName): bool
287
    {
288 4
        $system = false;
289
290 4
        $variables = $this->getVariables();
291 4
        if(isset($variables[$variableName]))
292
        {
293 4
            $system = $variables[$variableName]['system'];
294
        }
295
296 4
        return $system;
297
    }
298
299 41
    public function getDefaultEnvironmentsForGroups(): array
300
    {
301 41
         $defaultEnvironments = [];
302
303 41
        if(isset($this->parsers[self::GROUPS]))
304
        {
305 33
            $defaultEnvironments = $this->parsers[self::GROUPS]->getDefaultEnvironmentsForGroups();
0 ignored issues
show
Bug introduced by
The method getDefaultEnvironmentsForGroups does only exist in Karma\Configuration\Parser\GroupParser, but not in Karma\Configuration\Pars...n\Parser\VariableParser.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
306
        }
307
308 41
        return $defaultEnvironments;
309
    }
310
}
311