Completed
Pull Request — master (#1212)
by Asmir
11:43
created

Parser::visitArrayType()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 3
nop 0
dl 0
loc 16
ccs 0
cts 0
cp 0
crap 20
rs 9.9666
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
class Parser implements ParserInterface
10
{
11
    /**
12
     * @var Lexer
13
     */
14
    private $lexer;
15
16
    public function parse(string $string): array
17
    {
18
        $this->lexer = new Lexer();
19 448
        $this->lexer->setInput($string);
20
        $this->lexer->moveNext();
21 448
        return $this->visit();
22 448
    }
23 448
24
    /**
25 320
     * @return mixed
26
     */
27
    private function visit()
28 320
    {
29
        $this->lexer->moveNext();
30 314
31 6
        if (!$this->lexer->token) {
32 6
            throw new SyntaxError(
33
                'Syntax error, unexpected end of stream'
34
            );
35
        }
36
37
        if (Lexer::T_FLOAT === $this->lexer->token['type']) {
38
            return floatval($this->lexer->token['value']);
39
        } elseif (Lexer::T_INTEGER === $this->lexer->token['type']) {
40
            return intval($this->lexer->token['value']);
41
        } elseif (Lexer::T_NULL === $this->lexer->token['type']) {
42
            return null;
43
        } elseif (Lexer::T_STRING === $this->lexer->token['type']) {
44
            return $this->lexer->token['value'];
45
        } elseif (Lexer::T_IDENTIFIER === $this->lexer->token['type']) {
46
            if ($this->lexer->isNextToken(Lexer::T_TYPE_START)) {
47
                return $this->visitCompoundType();
48
            } elseif ($this->lexer->isNextToken(Lexer::T_ARRAY_START)) {
49
                return $this->visitArrayType();
50
            }
51
            return $this->visitSimpleType();
52
        } elseif (Lexer::T_ARRAY_START === $this->lexer->token['type']) {
53
            return $this->visitArrayType();
54
        }
55
56
        throw new SyntaxError(sprintf(
57
            'Syntax error, unexpected "%s" (%s)',
58
            $this->lexer->token['value'],
59
            $this->getConstant($this->lexer->token['type'])
60
        ));
61
    }
62
63
    /**
64
     * @return string|mixed[]
65
     */
66
    private function visitSimpleType()
67
    {
68
        $value = $this->lexer->token['value'];
69
        return ['name' => $value, 'params' => []];
70
    }
71
72
    private function visitCompoundType(): array
73
    {
74
        $name = $this->lexer->token['value'];
75
        $this->match(Lexer::T_TYPE_START);
76
77
        $params = [];
78
        if (!$this->lexer->isNextToken(Lexer::T_TYPE_END)) {
79
            while (true) {
80
                $params[] = $this->visit();
81
82
                if ($this->lexer->isNextToken(Lexer::T_TYPE_END)) {
83
                    break;
84
                }
85
                $this->match(Lexer::T_COMMA);
86
            }
87
        }
88
        $this->match(Lexer::T_TYPE_END);
89
        return [
90
            'name' => $name,
91
            'params' => $params,
92
        ];
93
    }
94
95
    private function visitArrayType(): array
96
    {
97
//        $this->match(Lexer::T_ARRAY_START);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
98
99
        $params = [];
100
        if (!$this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
101
            while (true) {
102
                $params[] = $this->visit();
103
                if ($this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
104
                    break;
105
                }
106
                $this->match(Lexer::T_COMMA);
107
            }
108
        }
109
        $this->match(Lexer::T_ARRAY_END);
110
        return $params;
111
    }
112
113
    private function match(int $token): void
114
    {
115
        if (!$this->lexer->lookahead) {
116
            throw new SyntaxError(
117
                sprintf('Syntax error, unexpected end of stream, expected %s', $this->getConstant($token))
118
            );
119
        }
120
121
        $lookaheadType = $this->lexer->lookahead['type'];
122
123
        // Short-circuit on first condition, usually types match
124
        if ($lookaheadType === $token) {
125
            $this->lexer->moveNext();
126
127
            return;
128
        }
129
130
        throw new SyntaxError(sprintf(
131
            'Syntax error, unexpected "%s" (%s), expected was %s',
132
            $this->lexer->lookahead['value'],
133
            $this->getConstant($this->lexer->lookahead['type']),
134
            $this->getConstant($token)
135
        ));
136
    }
137
138
    private function getConstant(int $value): string
139
    {
140
        $oClass = new \ReflectionClass(Lexer::class);
141
        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...
142
    }
143
}
144