Passed
Pull Request — master (#315)
by Théo
02:34
created

Dump::dumpAnnotations()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
dl 0
loc 42
rs 8.9137
c 0
b 0
f 0
cc 6
nc 2
nop 1
1
<?php
2
3
/**
4
 * Hoa
5
 *
6
 *
7
 * @license
8
 *
9
 * New BSD License
10
 *
11
 * Copyright © 2007-2017, Hoa community. All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions are met:
15
 *     * Redistributions of source code must retain the above copyright
16
 *       notice, this list of conditions and the following disclaimer.
17
 *     * Redistributions in binary form must reproduce the above copyright
18
 *       notice, this list of conditions and the following disclaimer in the
19
 *       documentation and/or other materials provided with the distribution.
20
 *     * Neither the name of the Hoa nor the names of its contributors may be
21
 *       used to endorse or promote products derived from this software without
22
 *       specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 */
36
37
namespace KevinGH\Box\Annotation;
38
39
use function array_map;
40
use function array_shift;
41
use function array_values;
42
use Assert\Assertion;
43
use Hoa\Compiler\Llk\TreeNode;
44
use Hoa\Visitor;
45
use function implode;
46
use function in_array;
47
use InvalidArgumentException;
48
use function sprintf;
49
50
/**
51
 * Class \Hoa\Compiler\Visitor\Dump.
52
 *
53
 * Dump AST produced by LL(k) compiler.
54
 *
55
 * @copyright  Copyright © 2007-2017 Hoa community
56
 * @license    New BSD License
57
 */
58
class Dump
59
{
60
    /**
61
     * @return  string[]
62
     */
63
    public function dumpAnnotations(TreeNode $node): array
64
    {
65
        if ('#annotations' !== $node->getId()) {
66
            return [];
67
        }
68
69
        return array_map(
70
            function (TreeNode $node): string {
71
                return $this->transformDataToString($node);
72
            },
73
            $node->getChildren()
74
        );
75
76
        ++self::$_i;
0 ignored issues
show
Unused Code introduced by
++self::_i is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
77
78
        $out  = str_repeat('>  ', self::$_i) . $node->getId();
79
80
        if (null !== $value = $node->getValue()) {
81
            $out .=
82
                '(' .
83
                ('default' !== $value['namespace']
84
                    ? $value['namespace'] . ':'
85
                    : '') .
86
                $value['token'] . ', ' .
87
                $value['value'] . ')';
88
        }
89
90
        $data = $node->getData();
91
92
        if (!empty($data)) {
93
            $out .= ' ' . $this->dumpData($data);
94
        }
95
96
        $out .= "\n";
97
98
        foreach ($node->getChildren() as $child) {
99
            $out .= $child->accept($this, $handle, $eldnah);
100
        }
101
102
        --self::$_i;
103
104
        return $out;
105
    }
106
107
    private function transformDataToString(TreeNode $node): string
108
    {
109
        if ('token' === $node->getId()) {
110
            if (in_array($node->getValueToken(), ['identifier', 'simple_identifier'], true)) {
111
                return $node->getValueValue();
112
            }
113
114
            if ('string' === $node->getValueToken()) {
115
                return sprintf('"%s"', $node->getValueValue());
116
            }
117
118
            if ('valued_identifier' === $node->getValueToken()) {
119
                return sprintf('%s()', $node->getValueValue());
120
            }
121
122
            throw new InvalidArgumentException(
123
                sprintf(
124
                    'Unknown token type "%s"',
125
                    $node->getValueToken()
126
                )
127
            );
128
        }
129
130
        if ('#parameters' === $node->getId()) {
131
            $transformedChildren = array_map(
132
                function (TreeNode $parameter): string {
133
                    return $this->transformDataToString($parameter);
134
                },
135
                $node->getChildren()
136
            );
137
138
            return implode(',', $transformedChildren);
139
        }
140
141
        if ('#named_parameter' === $node->getId()) {
142
            Assertion::same($node->getChildrenNumber(), 2);
143
144
            $name = $node->getChild(0);
145
            $parameter = $node->getChild(1);
146
147
            return sprintf(
148
                '%s=%s',
149
                $this->transformDataToString($name),
150
                $this->transformDataToString($parameter)
151
            );
152
        }
153
154
        if ('#value' === $node->getId()) {
155
            Assertion::same($node->getChildrenNumber(), 1);
156
157
            return $this->transformDataToString($node->getChild(0));
158
        }
159
160
        if ('#string' === $node->getId()) {
161
            Assertion::same($node->getChildrenNumber(), 1);
162
163
            return $this->transformDataToString($node->getChild(0));
164
        }
165
166
        if ('#list' === $node->getId()) {
167
            $transformedChildren = array_map(
168
                function (TreeNode $parameter): string {
169
                    return $this->transformDataToString($parameter);
170
                },
171
                $node->getChildren()
172
            );
173
174
            return sprintf(
175
                '{%s}',
176
                implode(
177
                    ',',
178
                    $transformedChildren
179
                )
180
            );
181
        }
182
183
        if ('#annotation' === $node->getId()) {
184
            Assertion::greaterOrEqualThan($node->getChildrenNumber(), 1);
185
186
            $children = $node->getChildren();
187
188
            /** @var TreeNode $token */
189
            $token = array_shift($children);
190
            $parameters = array_values($children);
191
192
            if ('simple_identifier' === $token->getValueToken()) {
193
                Assertion::count($parameters, 0);
194
195
                return '@'.$token->getValueValue();
196
            }
197
198
            if ('valued_identifier' === $token->getValueToken()) {
199
                $transformedChildren = array_map(
200
                    function (TreeNode $parameter): string {
201
                        return $this->transformDataToString($parameter);
202
                    },
203
                    $parameters
204
                );
205
206
                return sprintf(
207
                    '@%s(%s)',
208
                    $token->getValueValue(),
209
                    implode(
210
                        '',
211
                        $transformedChildren
212
                    )
213
                );
214
            }
215
        }
216
217
218
219
        $x = '';
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Unused Code introduced by
The assignment to $x is dead and can be removed.
Loading history...
220
    }
221
222
    private function transformNamedParameterToString(TreeNode $node): string
0 ignored issues
show
Unused Code introduced by
The parameter $node is not used and could be removed. ( Ignorable by Annotation )

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

222
    private function transformNamedParameterToString(/** @scrutinizer ignore-unused */ TreeNode $node): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The method transformNamedParameterToString() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
223
    {
224
225
    }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
226
227
    /**
228
     * Dump data.
229
     *
230
     * @param   mixed  $data    Data.
231
     * @return  string
232
     */
233
    protected function dumpData($data)
234
    {
235
        $out = null;
236
237
        if (!is_array($data)) {
238
            return $data;
239
        }
240
241
        foreach ($data as $key => $value) {
242
            $out .= '[' . $key . ' => ' . $this->dumpData($value) . ']';
243
        }
244
245
        return $out;
246
    }
247
}
248