Completed
Push — master ( 4dcf1f...9ebefd )
by Maxim
02:52
created

Lexer::isLineEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is a part of "Axessors" library.
4
 *
5
 * @author <[email protected]>
6
 * @license GPL
7
 */
8
9
namespace NoOne4rever\Axessors;
10
11
use NoOne4rever\Axessors\Exceptions\ParseError;
12
13
/**
14
 * Class Lexer.
15
 *
16
 * A general implementation of Axessors tokenizer.
17
 *
18
 * @package NoOne4rever\Axessors
19
 */
20
abstract class Lexer
21
{
22
    /** @var \ReflectionClass class' reflection */
23
    protected $reflection;
24
    /** @var resource file with PHP-code */
25
    protected $source;
26
    /** @var int the first line of class declaration */
27
    protected $startLine;
28
    /** @var int the last line of class declaration */
29
    protected $endLine;
30
    /** @var string current line */
31
    protected $currentLine;
32
    /** @var int number of the current symbol */
33
    protected $currentSym = 0;
34
35
    /** @var int number of the current line */
36
    private $lineNumber = 0;
37
38
    /**
39
     * Lexer constructor.
40
     *
41
     * @param \ReflectionClass $reflection class' reflection
42
     */
43 5
    public function __construct(\ReflectionClass $reflection)
44
    {
45 5
        $this->reflection = $reflection;
46 5
        $this->openSource();
47 5
        $this->skipFirstLines();
48 5
    }
49
50
    /**
51
     * Splits given code into array of tokens.
52
     *
53
     * @param string $code
54
     * @param string[] $expectations tokens' patterns
55
     * @param int[] $requiredItems necessary tokens
56
     * @return string[] found tokens
57
     * @throws ParseError if an important token not found in Axessors comment
58
     */
59 5
    protected function parse(string $code, array $expectations, array $requiredItems): array
60
    {
61 5
        $this->currentSym = 0;
62 5
        $code = trim($code);
63 5
        $result = [];
64 5
        foreach ($expectations as $index => $pattern) {
65 5
            $this->skipWhitespace($code);
66 5
            preg_match($pattern, substr($code, $this->currentSym), $token);
67 5
            if (empty($token)) {
68 5
                if (in_array($index, $requiredItems)) {
69 5
                    throw new ParseError("token with pattern \"$pattern\" not found while parsing {$this->reflection->getFileName()}:{$this->lineNumber}");
70
                }
71
            } else {
72 5
                $result[$index] = $token[0];
73 5
                $this->currentSym += strlen($token[0]);
74
            }
75
        }
76 5
        return $result;
77
    }
78
79
    /**
80
     * Skips whitespace symbols in code.
81
     *
82
     * @param string $code line of code
83
     */
84 5
    private function skipWhitespace(string $code): void
85
    {
86 5
        preg_match('{^\s+}', substr($code, $this->currentSym), $whitespace);
87 5
        if (!empty($whitespace)) {
88 5
            $this->currentSym += strlen($whitespace[0]);
89
        }
90 5
    }
91
92
    /**
93
     * Opens source with class declaration.
94
     */
95 5
    protected function openSource(): void
96
    {
97 5
        $this->source = fopen($this->reflection->getFileName(), 'rb');
98 5
        $this->startLine = $this->reflection->getStartLine();
99 5
        $this->endLine = $this->reflection->getEndLine();
100 5
    }
101
102
    /**
103
     * Reads a line from source.
104
     */
105 5
    protected function readLine(): void
106
    {
107 5
        $this->currentLine = fgets($this->source);
108 5
        ++$this->lineNumber;
109 5
    }
110
111
    /**
112
     * Checks if the line is empty.
113
     *
114
     * @return bool result of the checkout
115
     */
116 5
    protected function isLineEmpty(): bool
117
    {
118 5
        return $this->currentLine !== false;
119
    }
120
121
    /**
122
     * Skips lines before class declaration.
123
     */
124 5
    protected function skipFirstLines(): void
125
    {
126 5
        for ($i = 1; $i < $this->startLine; ++$i) {
127 5
            $this->readLine();
128
        }
129 5
    }
130
}
131