Parser::match()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 19
ccs 0
cts 0
cp 0
crap 12
rs 9.8666
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
 * @phpstan-import-type TypeArray from Type
13
 */
14
final class Parser implements ParserInterface
15
{
16
    /**
17
     * @var Lexer
18
     */
19 448
    private $lexer;
20
21 448
    /**
22 448
     * @var bool
23 448
     */
24
    private $root = true;
25 320
26
    public function parse(string $string): array
27
    {
28 320
        $this->lexer = new Lexer();
29
        $this->lexer->setInput($string);
30 314
        $this->lexer->moveNext();
31 6
32 6
        return $this->visit();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->visit() could return the type double|integer|null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
33
    }
34
35
    /**
36
     * @return mixed
37
     */
38
    private function visit()
39
    {
40
        $this->lexer->moveNext();
41
42
        if (!$this->lexer->token) {
43
            throw new SyntaxError(
44
                'Syntax error, unexpected end of stream',
45
            );
46
        }
47
48
        if (Lexer::T_FLOAT === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
49
            return floatval($this->lexer->token->value);
50
        } elseif (Lexer::T_INTEGER === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
51
            return intval($this->lexer->token->value);
52
        } elseif (Lexer::T_NULL === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
53
            return null;
54
        } elseif (Lexer::T_STRING === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
55
            return $this->lexer->token->value;
56
        } elseif (Lexer::T_IDENTIFIER === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
57
            if ($this->lexer->isNextToken(Lexer::T_TYPE_START)) {
58
                return $this->visitCompoundType();
59
            } elseif ($this->lexer->isNextToken(Lexer::T_ARRAY_START)) {
60
                return $this->visitArrayType();
61
            }
62
63
            return $this->visitSimpleType();
64
        } elseif (!$this->root && Lexer::T_ARRAY_START === $this->lexer->token->type) {
0 ignored issues
show
introduced by
The condition JMS\Serializer\Type\Lexe...his->lexer->token->type is always false.
Loading history...
65
            return $this->visitArrayType();
66
        }
67
68
        throw new SyntaxError(sprintf(
69
            'Syntax error, unexpected "%s" (%s)',
70
            $this->lexer->token->value,
71
            $this->getConstant($this->lexer->token->type),
0 ignored issues
show
Bug introduced by
$this->lexer->token->type of type Doctrine\Common\Lexer\T|null is incompatible with the type integer expected by parameter $value of JMS\Serializer\Type\Parser::getConstant(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

71
            $this->getConstant(/** @scrutinizer ignore-type */ $this->lexer->token->type),
Loading history...
72
        ));
73
    }
74
75
    /**
76
     * @return TypeArray
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\Type\TypeArray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
77
     */
78
    private function visitSimpleType(): array
79
    {
80
        $value = $this->lexer->token->value;
81
82
        return ['name' => $value, 'params' => []];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('name' => $...e, 'params' => array()) returns the type array<string,Doctrine\Common\Lexer\V|array> which is incompatible with the documented return type JMS\Serializer\Type\TypeArray.
Loading history...
83
    }
84
85
    /**
86
     * @return TypeArray
87
     */
88
    private function visitCompoundType(): array
89
    {
90
        $this->root = false;
91
        $name = $this->lexer->token->value;
92
        $this->match(Lexer::T_TYPE_START);
93
94
        $params = [];
95
        if (!$this->lexer->isNextToken(Lexer::T_TYPE_END)) {
0 ignored issues
show
Bug introduced by
JMS\Serializer\Type\Lexer::T_TYPE_END of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $type of Doctrine\Common\Lexer\AbstractLexer::isNextToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

95
        if (!$this->lexer->isNextToken(/** @scrutinizer ignore-type */ Lexer::T_TYPE_END)) {
Loading history...
96
            while (true) {
97
                $params[] = $this->visit();
98
99
                if ($this->lexer->isNextToken(Lexer::T_TYPE_END)) {
100
                    break;
101
                }
102
103
                $this->match(Lexer::T_COMMA);
104
            }
105
        }
106
107
        $this->match(Lexer::T_TYPE_END);
108
109
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('name' => $...e, 'params' => $params) returns the type array<string,Doctrine\Common\Lexer\V|array> which is incompatible with the documented return type JMS\Serializer\Type\TypeArray.
Loading history...
110
            'name' => $name,
111
            'params' => $params,
112
        ];
113
    }
114
115
    private function visitArrayType(): array
116
    {
117
        /*
118
         * Here we should call $this->match(Lexer::T_ARRAY_START); to make it clean
119
         * but the token has already been consumed by moveNext() in visit()
120
         */
121
122
        $params = [];
123
        if (!$this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
0 ignored issues
show
Bug introduced by
JMS\Serializer\Type\Lexer::T_ARRAY_END of type integer is incompatible with the type Doctrine\Common\Lexer\T expected by parameter $type of Doctrine\Common\Lexer\AbstractLexer::isNextToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
        if (!$this->lexer->isNextToken(/** @scrutinizer ignore-type */ Lexer::T_ARRAY_END)) {
Loading history...
124
            while (true) {
125
                $params[] = $this->visit();
126
                if ($this->lexer->isNextToken(Lexer::T_ARRAY_END)) {
127
                    break;
128
                }
129
130
                $this->match(Lexer::T_COMMA);
131
            }
132
        }
133
134
        $this->match(Lexer::T_ARRAY_END);
135
136
        return $params;
137
    }
138
139
    private function match(int $token): void
140
    {
141
        if (!$this->lexer->lookahead) {
142
            throw new SyntaxError(
143
                sprintf('Syntax error, unexpected end of stream, expected %s', $this->getConstant($token)),
144
            );
145
        }
146
147
        if ($this->lexer->lookahead->type === $token) {
0 ignored issues
show
introduced by
The condition $this->lexer->lookahead->type === $token is always false.
Loading history...
148
            $this->lexer->moveNext();
149
150
            return;
151
        }
152
153
        throw new SyntaxError(sprintf(
154
            'Syntax error, unexpected "%s" (%s), expected was %s',
155
            $this->lexer->lookahead->value,
156
            $this->getConstant($this->lexer->lookahead->type),
0 ignored issues
show
Bug introduced by
$this->lexer->lookahead->type of type Doctrine\Common\Lexer\T|null is incompatible with the type integer expected by parameter $value of JMS\Serializer\Type\Parser::getConstant(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
            $this->getConstant(/** @scrutinizer ignore-type */ $this->lexer->lookahead->type),
Loading history...
157
            $this->getConstant($token),
158
        ));
159
    }
160
161
    private function getConstant(int $value): string
162
    {
163
        $oClass = new \ReflectionClass(Lexer::class);
164
165
        return array_search($value, $oClass->getConstants());
166
    }
167
}
168