Completed
Push — master ( 21430a...79e12b )
by Jeroen
02:36
created

CssFilterParser::parseFunctionString()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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