Completed
Push — master ( 795eef...f56d39 )
by Sergey
02:19
created

SphinxConfigurationParser::extractInheritanceName()   B

Complexity

Conditions 5
Paths 0

Size

Total Lines 16
Code Lines 12

Duplication

Lines 16
Ratio 100 %
Metric Value
dl 16
loc 16
rs 8.5454
cc 5
eloc 12
nc 0
nop 0
1
<?php
2
/**
3
 * @author: Viskov Sergey
4
 * @date: 3/10/16
5
 * @time: 1:33 PM
6
 */
7
8
namespace LTDBeget\sphinx\configurator\parser;
9
10
11
use BadMethodCallException;
12
use LTDBeget\ascii\AsciiChar;
13
use LTDBeget\sphinx\configurator\exceptions\SyntaxErrorException;
14
use LTDBeget\sphinx\enums\eSection;
15
use LTDBeget\stringstream\StringStream;
16
17
/**
18
 * Class SphinxConfigurationParser
19
 * @package LTDBeget\sphinx\configurator\parser
20
 */
21
final class SphinxConfigurationParser
22
{
23
    /**
24
     * parse and tokenize input string
25
     * @param string $plainData
26
     * @throws SyntaxErrorException
27
     * @throws BadMethodCallException
28
     * @return array
29
     */
30
    public static function parse(string $plainData) : array
31
    {
32
        return (new self($plainData))->tokenize()->tokens;
33
    }
34
35
    /**
36
     * SphinxConfigurationParser constructor.
37
     * @internal
38
     * @param string $string
39
     * @throws BadMethodCallException
40
     */
41
    private function __construct(string $string)
42
    {
43
        $this->originalString = $string;
0 ignored issues
show
Bug introduced by
The property originalString does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
44
        $string = $this->removeComments($string);
45
        $this->stream = new StringStream($string);
0 ignored issues
show
Bug introduced by
The property stream does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
46
    }
47
48
    /**
49
     * @internal
50
     * @param string $string
51
     * @return string
52
     */
53
    private function removeComments(string $string) : string
54
    {
55
        return preg_replace("/(^#| #|	#).*\n/im", "\n", $string);
56
    }
57
58
    /**
59
     * @internal
60
     * @return SphinxConfigurationParser
61
     * @throws SyntaxErrorException
62
     */
63
    private function tokenize() : SphinxConfigurationParser
64
    {
65
        do {
66
            $this->extractSection();
67
            $this->saveCurrentSection();
68
69
        } while (! $this->stream->isEnd());
70
71
        return $this;
72
    }
73
74
    /**
75
     * @internal
76
     * @throws SyntaxErrorException
77
     */
78
    private function extractSection()
79
    {
80
        $this->extractSectionType();
81
82
        switch ($this->currentSection["type"]) {
83
            case eSection::SOURCE():
84
            case eSection::INDEX():
85
                $this->extractSectionName();
86
87
                $this->extractInheritance();
88
                break;
89
            case eSection::INDEXER():
90
            case eSection::SEARCHD():
91
            case eSection::COMMON():
92
                break;
93
            default:
94
                throw new SyntaxErrorException($this->stream);
95
        }
96
97
        $this->extractOptions();
98
99
100
101
        $this->stream->ignoreWhitespace();
102
    }
103
104
    /**
105
     * @internal
106
     * @throws SyntaxErrorException
107
     */
108
    private function extractSectionType()
109
    {
110
        $this->stream->ignoreWhitespace();
111
        start:
112
        $char = $this->stream->currentAscii();
113
        $this->stream->next();
114
        if ($char->isLetter()) {
115
            $this->currentSection["type"] .= (string) $char;
116
            goto start;
117
        } elseif ($char->isWhiteSpace()) {
118
            return;
119
        } else {
120
            throw new SyntaxErrorException($this->stream);
121
        }
122
    }
123
124
    /**
125
     * @internal
126
     * @throws SyntaxErrorException
127
     */
128
    private function extractSectionName()
129
    {
130
        $this->stream->ignoreHorizontalSpace();
131
132
        start:
133
        $char = $this->stream->currentAscii();
134
        $this->stream->next();
135
136
        if ($char->isLetter() || $char->isDigit() || $char->is(AsciiChar::UNDERSCORE())) {
137
            $this->currentSection["name"] .= (string) $char;
138
            goto start;
139
        } elseif ($char->isWhiteSpace()) {
140
            return;
141
        } elseif ($char->is(AsciiChar::COLON())) {
142
            $this->stream->previous();
143
            return;
144
        } else {
145
            throw new SyntaxErrorException($this->stream);
146
        }
147
    }
148
149
    /**
150
     * @internal
151
     * @throws SyntaxErrorException
152
     */
153
    private function extractInheritance()
154
    {
155
        $this->stream->ignoreHorizontalSpace();
156
157
        $char = $this->stream->currentAscii();
158
159
        if($char->isVerticalSpace() || $char->is(AsciiChar::OPENING_BRACE())) {
160
            return;
161
        }
162
163
        if($char->is(AsciiChar::COLON())) {
164
            $this->stream->next();
165
            $this->extractInheritanceName();
166
        } else {
167
            throw new SyntaxErrorException($this->stream);
168
        }
169
    }
170
171
    /**
172
     * @internal
173
     * @throws SyntaxErrorException
174
     */
175 View Code Duplication
    private function extractInheritanceName()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177
        $this->stream->ignoreHorizontalSpace();
178
        start:
179
        $char = $this->stream->currentAscii();
180
        $this->stream->next();
181
182
        if ($char->isLetter() || $char->isDigit() || $char->is(AsciiChar::UNDERSCORE())) {
183
            $this->currentSection["inheritance"] .= (string) $char;
184
            goto start;
185
        } elseif ($char->isWhiteSpace()) {
186
            return;
187
        }  else {
188
            throw new SyntaxErrorException($this->stream);
189
        }
190
    }
191
192
    /**
193
     * @internal
194
     * @throws SyntaxErrorException
195
     */
196
    private function extractOptions()
197
    {
198
        $this->stream->ignoreWhitespace();
199
200
        if ($this->stream->currentAscii()->is(AsciiChar::OPENING_BRACE())) {
201
            $this->stream->next();
202
203
            start:
204
            $this->stream->ignoreWhitespace();
205
            if ($this->stream->currentAscii()->is(AsciiChar::CLOSING_BRACE())) {
206
                $this->stream->next();
207
                return;
208
            }
209
            $this->extractOption();
210
            goto start;
211
        } else {
212
            throw new SyntaxErrorException($this->stream);
213
        }
214
    }
215
216
    /**
217
     * @internal
218
     * @throws SyntaxErrorException
219
     */
220
    private function extractOption()
221
    {
222
        $this->extractOptionName();
223
        $this->extractOptionValue();
224
        $this->saveCurrentOption();
225
    }
226
227
    /**
228
     * @internal
229
     * @throws SyntaxErrorException
230
     */
231 View Code Duplication
    private function extractOptionName()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
    {
233
        $this->stream->ignoreWhitespace();
234
235
        start:
236
        $char = $this->stream->currentAscii();
237
        $this->stream->next();
238
239
        if ($char->isLetter() || $char->isDigit() || $char->is(AsciiChar::UNDERSCORE())) {
240
            $this->currentOption["name"] .= (string) $char;
241
            goto start;
242
        } elseif ($char->isHorizontalSpace()) {
243
            return;
244
        } else {
245
            throw new SyntaxErrorException($this->stream);
246
        }
247
    }
248
249
    /**
250
     * @internal
251
     * @throws SyntaxErrorException
252
     */
253
    private function extractOptionValue()
254
    {
255
        $this->stream->ignoreHorizontalSpace();
256
257
        $char = $this->stream->currentAscii();
258
        $this->stream->next();
259
260
        if (! $char->is(AsciiChar::EQUALS())) {
261
            throw new SyntaxErrorException($this->stream);
262
        }
263
264
        $this->stream->ignoreHorizontalSpace();
265
266
        start:
267
        $char = $this->stream->currentAscii();
268
        $this->stream->next();
269
270
        if ($char->isPrintableChar()) {
271
            if ($char->is(AsciiChar::BACKSLASH())) { // if possibility of multi-line
272
                $char = $this->stream->currentAscii();
273
274
                if ($char->isVerticalSpace()) { // multi-line opened
275
                    $this->currentOption["value"] .= (string) AsciiChar::SPACE();
276
                    $this->stream->next(); // ignore end line
277
                    $this->stream->ignoreHorizontalSpace();
278
                    goto start;
279
                } else { // backslash as mean symbol
280
                    $this->currentOption["value"] .= (string) AsciiChar::BACKSLASH();
281
                    goto start;
282
                }
283
            } else {
284
                $this->currentOption["value"] .= (string) $char;
285
                goto start;
286
            }
287
        } elseif ($char->isVerticalSpace()) {
288
            return;
289
        } else {
290
            throw new SyntaxErrorException($this->stream);
291
        }
292
    }
293
294
    /**
295
     * @internal
296
     */
297
    private function saveCurrentSection()
298
    {
299
        $this->currentSection = array_filter($this->currentSection);
300
        $this->tokens[]       = $this->currentSection;
301
        $this->currentSection = $this->getEmptySectionData();
302
    }
303
304
    /**
305
     * @internal
306
     */
307
    private function saveCurrentOption()
308
    {
309
        $this->currentSection["options"][] = $this->currentOption;
310
        $this->currentOption               = $this->getEmptyOptionData();
311
    }
312
313
    /**
314
     * @internal
315
     * @return array
316
     */
317
    private function getEmptySectionData() : array
318
    {
319
        return [
320
            "type"        => "",
321
            "name"        => "",
322
            "inheritance" => "",
323
            "options"     => []
324
        ];
325
    }
326
327
    /**
328
     * @internal
329
     * @return array
330
     */
331
    private function getEmptyOptionData() : array
332
    {
333
        return [
334
            "name"  => "",
335
            "value" => ""
336
        ];
337
    }
338
339
    /**
340
     * Result of tokenize input string
341
     * @var array
342
     */
343
    private $tokens = [];
344
345
    /**
346
     * temporary storage of tokens for one section
347
     * @var array
348
     */
349
    private $currentSection = [
350
        "type"        => "",
351
        "name"        => "",
352
        "inheritance" => "",
353
        "options"     => []
354
    ];
355
    /**
356
     * temporary storage of tokens for one option
357
     * @var array
358
     */
359
    private $currentOption = [
360
        "name"  => "",
361
        "value" => ""
362
    ];
363
}