CssFilterParser::parse()   C
last analyzed

Complexity

Conditions 16
Paths 20

Size

Total Lines 47
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
cc 16
eloc 32
c 3
b 2
f 0
nc 20
nop 1
dl 0
loc 47
rs 5.5666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Jerodev\Diglett;
4
5
use ErrorException;
6
use Jerodev\Diglett\CssFilters\ICssFilter;
7
use Jerodev\Diglett\Models\ParsedSelector;
8
9
class CssFilterParser
10
{
11
    /**
12
     *  The css filters to parse.
13
     *
14
     *  @var array
15
     */
16
    private $cssFilters;
17
18
    /**
19
     *  Create CssFilterParser and set the chosen css filters.
20
     *
21
     * @param array $cssFilters An array of css filters to use
22
     *
23
     * @throws ErrorException
24
     */
25
    public function __construct(array $cssFilters = [])
26
    {
27
        $this->cssFilters = [];
28
        $this->addCssFilters([
29
            CssFilters\ContainsTextFilter::class,
30
            CssFilters\ExactTextFilter::class,
31
            CssFilters\FirstFilter::class,
32
            CssFilters\LastFilter::class,
33
            CssFilters\NextFilter::class,
34
            CssFilters\NthFilter::class,
35
            CssFilters\PrevFilter::class,
36
            CssFilters\RegexTextFilter::class,
37
        ]);
38
39
        if (!empty($cssFilters)) {
40
            $this->addCssFilters($cssFilters);
41
        }
42
    }
43
44
    /**
45
     *  Add extra css filters.
46
     *
47
     * @param array|string $cssFilter
48
     *
49
     * @throws ErrorException
50
     */
51
    public function addCssFilters($cssFilter): void
52
    {
53
        if (is_array($cssFilter)) {
54
            foreach ($cssFilter as $filter) {
55
                $this->addCssFilters($filter);
56
            }
57
        } else {
58
            if (!class_exists($cssFilter) || !in_array(ICssFilter::class, class_implements($cssFilter))) {
59
                throw new ErrorException("`$cssFilter` does not implement ICssFilter.");
60
            }
61
62
            $this->cssFilters[$cssFilter::getFunctionName()] = $cssFilter;
63
        }
64
    }
65
66
    /**
67
     * Parse a string to an array containing selectors and special functions.
68
     *
69
     * @param string $line The filter to parser.
70
     *
71
     * @throws ErrorException
72
     *
73
     * @return array
74
     */
75
    public function parse(string $line): array
76
    {
77
        $line = trim($line);
78
79
        $parts = [];
80
        $selector = null;
81
        $functions = [];
82
        $quoted = false;
83
        for ($i = 0; $i < strlen($line); $i++) {
84
            $char = $line[$i];
85
            if (empty(trim($char)) && empty(trim($selector))) {
86
                continue;
87
            }
88
            if ($char === '"' || $char === '\'') {
89
                $quoted = !$quoted;
0 ignored issues
show
introduced by
The condition $quoted is always false.
Loading history...
90
            }
91
92
            if ($char !== ':' || $quoted) {
93
                $selector .= $char;
94
            } else {
95
                do {
96
                    $brackets = 0;
97
                    $functionLine = '';
98
                    for (; ++$i < strlen($line);) {
99
                        $char = $line[$i];
100
                        $functionLine .= $char;
101
                        if ($char === '(') {
102
                            $brackets++;
103
                        } elseif ($char === ')' && --$brackets === 0) {
104
                            break;
105
                        }
106
                    }
107
108
                    $functions[] = $this->parseFunctionString($functionLine);
109
                } while (++$i < strlen($line) && $line[$i] === ':');
110
111
                $parts[] = new ParsedSelector($selector, $functions);
112
                $selector = null;
113
                $functions = [];
114
            }
115
        }
116
117
        if (!empty(trim($selector)) || !empty($functions)) {
118
            $parts[] = new ParsedSelector($selector, $functions);
119
        }
120
121
        return $parts;
122
    }
123
124
    /**
125
     * Parse a string to a CssFilter object.
126
     *
127
     * @param string $line The part of the selector presenting the filter function.
128
     *
129
     * @throws ErrorException
130
     *
131
     * @return ICssFilter
132
     */
133
    private function parseFunctionString(string $line): ICssFilter
134
    {
135
        $functionName = strstr($line, '(', true);
136
        $arguments = explode(',', substr(strstr($line, '('), 1, -1));
137
138
        if (!array_key_exists($functionName, $this->cssFilters)) {
139
            throw new ErrorException("The ICssFilter `$functionName` does not exist.");
140
        }
141
142
        return new $this->cssFilters[$functionName]($arguments);
143
    }
144
}
145