Passed
Pull Request — master (#1212)
by Asmir
12:10
created

Parser::visitSimpleType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Type;
6
7
use JMS\Serializer\Type\Exception\SyntaxError;
8
9
/**
10
 * @internal
11
 */
12
final class Parser implements ParserInterface
13
{
14
    /**
15
     * @var Lexer
16
     */
17
    private $lexer;
18
19 448
    /**
20
     * @var bool
21 448
     */
22 448
    private $root = true;
23 448
24
    public function parse(string $string): array
25 320
    {
26
        $this->lexer = new Lexer();
27
        $this->lexer->setInput($string);
28 320
        $this->lexer->moveNext();
29
        return $this->visit();
30 314
    }
31 6
32 6
    /**
33
     * @return mixed
34
     */
35
    private function visit()
36
    {
37
        $this->lexer->moveNext();
38
39
        if (!$this->lexer->token) {
40
            throw new SyntaxError(
41
                'Syntax error, unexpected end of stream'
42
            );
43
        }
44
45
        if (Lexer::T_FLOAT === $this->lexer->token['type']) {
46
            return floatval($this->lexer->token['value']);
47
        } elseif (Lexer::T_INTEGER === $this->lexer->token['type']) {
48
            return intval($this->lexer->token['value']);
49
        } elseif (Lexer::T_NULL === $this->lexer->token['type']) {
50
            return null;
51
        } elseif (Lexer::T_STRING === $this->lexer->token['type']) {
52
            return $this->lexer->token['value'];
53
        } elseif (Lexer::T_IDENTIFIER === $this->lexer->token['type']) {
54
            if ($this->lexer->isNextToken(Lexer::T_TYPE_START)) {
55
                return $this->visitCompoundType();
56
            } elseif ($this->lexer->isNextToken(Lexer::T_ARRAY_START)) {
57
                return $this->visitArrayType();
58
            }
59
            return $this->visitSimpleType();
60
        } elseif (!$this->root && Lexer::T_ARRAY_START === $this->lexer->token['type']) {
61
            return $this->visitArrayType();
62
        }
63
64
        throw new SyntaxError(sprintf(
65
            'Syntax error, unexpected "%s" (%s)',
66
            $this->lexer->token['value'],
67
            $this->getConstant($this->lexer->token['type'])
68
        ));
69
    }
70
71
    /**
72
     * @return string|mixed[]
73
     */
74
    private function visitSimpleType()
75
    {
76
        $value = $this->lexer->token['value'];
77
        return ['name' => $value, 'params' => []];
78
    }
79
80
    private function visitCompoundType(): array
81
    {
82
        $this->root = false;
83
        $name = $this->lexer->token['value'];
84
        $this->match(Lexer::T_TYPE_START);
85
86
        $params = [];
87
        if (!$this->lexer->isNextToken(Lexer::T_TYPE_END)) {
88
            while (true) {
89
                $params[] = $this->visit();
90
91
                if ($this->lexer->isNextToken(Lexer::T_TYPE_END)) {
92
                    break;
93
                }
94
                $this->match(Lexer::T_COMMA);
95
            }
96
        }
97
        $this->match(Lexer::T_TYPE_END);
98
        return [
99
            'name' => $name,
100
            'params' => $params,
101
        ];
102
    }
103
104
    private function visitArrayType(): array
105
    {
106
        /*
107
         * Here we should call $this->match(Lexer::T_ARRAY_START); to make it clean
108
         * but the token has already been consumed by moveNext() in visit()
109
         */
110
111
        $params = [];
112
        if (!$this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
113
            while (true) {
114
                $params[] = $this->visit();
115
                if ($this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
116
                    break;
117
                }
118
                $this->match(Lexer::T_COMMA);
119
            }
120
        }
121
        $this->match(Lexer::T_ARRAY_END);
122
        return $params;
123
    }
124
125
    private function match(int $token): void
126
    {
127
        if (!$this->lexer->lookahead) {
128
            throw new SyntaxError(
129
                sprintf('Syntax error, unexpected end of stream, expected %s', $this->getConstant($token))
130
            );
131
        }
132
133
        if ($this->lexer->lookahead['type'] === $token) {
134
            $this->lexer->moveNext();
135
136
            return;
137
        }
138
139
        throw new SyntaxError(sprintf(
140
            'Syntax error, unexpected "%s" (%s), expected was %s',
141
            $this->lexer->lookahead['value'],
142
            $this->getConstant($this->lexer->lookahead['type']),
143
            $this->getConstant($token)
144
        ));
145
    }
146
147
    private function getConstant(int $value): string
148
    {
149
        $oClass = new \ReflectionClass(Lexer::class);
150
        return array_search($value, $oClass->getConstants());
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_search($val...oClass->getConstants()) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
151
    }
152
}
153