GetModules::process()   B
last analyzed

Complexity

Conditions 8
Paths 16

Size

Total Lines 73
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 40
c 1
b 0
f 0
nc 16
nop 0
dl 0
loc 73
ccs 40
cts 40
cp 1
crap 8
rs 8.0355

How to fix   Long Method   

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 kalanis\kw_modules\Parser;
4
5
6
use kalanis\kw_modules\Interfaces\IMdTranslations;
7
use kalanis\kw_modules\ModuleException;
8
use kalanis\kw_modules\Support;
9
use kalanis\kw_modules\Traits\TMdLang;
10
11
12
/**
13
 * Class GetModules
14
 * @package kalanis\kw_modules\Parser
15
 * Parse source template to get submodules
16
 *
17
 * No params:
18
 * In: {MODULE/}
19
 *
20
 * Params passed:
21
 * In: {MODULE}param1=value1&param2=value2&...{/MODULE}
22
 * In: {OUR-MODULE}param1=value1&param2=value2&...{/OUR-MODULE}
23
 * In: {MODULE--SUBPART}param1=value1&param2=value2&...{/MODULE--SUBPART}
24
 *
25
 * Disabled ones - will be skipped:
26
 * In: {-MODULE/}
27
 * In: {!MODULE/}
28
 * In: {-MODULE}param1=value1&param2=value2&...{/MODULE-}
29
 *
30
 * Out: available modules to process
31
 *
32
 * After getting modules in that content it's necessary to run them through loaders and replace the content back in source
33
 * This also use hashed params - so each instance with the same params will be returned just once
34
 *
35
 * Intentionally without multibyte calls (mb_*)
36
 *
37
 * todo: in this version you cannot make the module which will pass its result as param for another module
38
 */
39
class GetModules
40
{
41
    use TMdLang;
42
43
    protected string $content = '';
44
    /** @var array<string, Record> */
45
    protected array $foundModules = [];
46
47 24
    public function __construct(?IMdTranslations $lang = null)
48
    {
49 24
        $this->setMdLang($lang);
50 24
    }
51
52 10
    public function setContent(string $content): self
53
    {
54 10
        $this->content = $content;
55 10
        return $this;
56
    }
57
58
    /**
59
     * @throws ModuleException
60
     * @return $this
61
     */
62 10
    public function process(): self
63
    {
64 10
        $this->foundModules = [];
65
        // regexp got all available module tags on page, strpos their positions and then we van get the params
66 10
        if (!preg_match_all('#{([a-zA-Z_!/-]+)}#mi', $this->content,  $matches)) {
67 2
            return $this;
68
        };
69
70 8
        $use = $this->rePair($matches);
71
72
        // matches got everything - even solo, ending and disabled
73
        // so filter them
74 8
        $use = array_filter($use, [$this, 'filterUsable']);
75
76
        // then add real positions in main string to usable tags
77
        // due option that tags will be more than once, it must get foreach and start skip
78
        /** @var Positioned[] $positions */
79 8
        $positions = [];
80 8
        $start = 0;
81 8
        foreach ($use as $found) {
82 8
            $pos = strpos($this->content, $found->getInner(), $start);
83 8
            if (false !== $pos) {
84 8
                $positions[] = new Positioned($found->getBraced(), $found->getInner(), $pos - 1);
85
                // skip part with already found
86 8
                $start = $pos + strlen($found->getInner());
87
            }
88
        }
89
90
//print_r(['pos', $positions]);
91
        // then solos can go permanently out
92 8
        array_map([$this, 'filteredSoloIntoRecord'], array_filter($positions, [$this, 'filterSolo']));
93
        /** @var Positioned[] $rest */
94 8
        $rest = array_values(array_filter($positions, [$this, 'filterNoSolo'])); // remove holes in indexes
95
96 8
        $stack = new \SplStack();
97 8
        $len = count($rest);
98 8
        for ($i=0; $i<$len; $i++) {
99 6
            if (!$this->filterEndingTag($rest[$i])) {
100 5
                $stack[] = $rest[$i];
101
            } else {
102 6
                if ($stack->isEmpty()) {
103 1
                    throw new ModuleException($this->getMdLang()->mdNoOpeningTag());
104
                }
105
                // check if it's ending for that tag
106
                /** @var Positioned $top */
107 5
                $top = $stack->top();
108 5
                $clear = Support::clearModuleName($rest[$i]->getInner());
109 5
                if ($top->getInner() != $clear) {
110
                    // todo: now just lookup for top one, in future try to look deeper if the correct match is somewhere there
111 1
                    throw new ModuleException($this->getMdLang()->mdNoEndingTag($top->getInner()));
112
                }
113 5
                $stack->pop();
114
115
                // between start and end are the necessary params
116 5
                $paramStart = $top->getPos() + strlen($top->getBraced());
117 5
                $paramLen = $rest[$i]->getPos() - $paramStart;
118 5
                $changeLen = $rest[$i]->getPos() + strlen($rest[$i]->getBraced()) - $top->getPos();
119
120 5
                $parts = Support::modulePathFromTemplate(Support::clearModuleName($top->getInner()));
121 5
                $readInternals = substr($this->content, $paramStart, $paramLen);
122 5
                $toChange = substr($this->content, $top->getPos(), $changeLen);
123
//print_r(['proc', $parts, $readParams, $toChange]);
124 5
                $rec = new Record();
125 5
                $rec->setContent($readInternals);
126 5
                $rec->setModuleName(strval(reset($parts)));
127 5
                $rec->setModulePath($parts);
128 5
                $rec->setContentToChange($toChange);
129
130 5
                $this->foundModules[$this->makeHash($rec)] = $rec;
131
            }
132
        }
133
134 6
        return $this;
135
    }
136
137
    /**
138
     * @param array<int, array<int, string>> $in
139
     * @return Positioned[]
140
     * To let data remove just by one index
141
     */
142 8
    protected function rePair(array $in): array
143
    {
144 8
        $pairs = [];
145 8
        $match = (array) reset($in);
146 8
        foreach ($match as $index => $value) {
147 8
            $pairs[] = new Positioned($in[0][$index], $in[1][$index]);
148
        }
149 8
        return $pairs;
150
    }
151
152 8
    public function filterUsable(Positioned $entry): bool
153
    {
154 8
        $firstLetter = mb_substr($entry->getInner(), 0, 1);
155 8
        $lastLetter = mb_substr($entry->getInner(), -1, 1);
156 8
        return '-' != $firstLetter && '!' != $firstLetter && '-' != $lastLetter;
157
    }
158
159 8
    public function filterSolo(Positioned $entry): bool
160
    {
161 8
        $lastLetter = mb_substr($entry->getInner(), -1, 1);
162 8
        return '/' == $lastLetter;
163
    }
164
165 8
    public function filterNoSolo(Positioned $entry): bool
166
    {
167 8
        return !$this->filterSolo($entry);
168
    }
169
170 6
    public function filterEndingTag(Positioned $entry): bool
171
    {
172 6
        $word = $entry->getInner();
173 6
        return '/' == $word[0];
174
    }
175
176 2
    protected function filteredSoloIntoRecord(Positioned $entry): Record
177
    {
178 2
        $parts = Support::modulePathFromTemplate(Support::clearModuleName($entry->getInner()));
179 2
        $rec = new Record();
180 2
        $rec->setModuleName(strval(reset($parts)));
181 2
        $rec->setModulePath($parts);
182 2
        $rec->setContentToChange($entry->getBraced());
183 2
        $this->foundModules[$this->makeHash($rec)] = $rec;
184 2
        return $rec;
185
    }
186
187 7
    protected function makeHash(Record $record): string
188
    {
189 7
        return md5(sprintf('%s-%s', implode('::', $record->getModulePath()), $record->getContent()));
190
    }
191
192
    /**
193
     * @return Record[]
194
     */
195 8
    public function getFoundModules(): array
196
    {
197 8
        return array_values($this->foundModules);
198
    }
199
}
200