Passed
Pull Request — 3.x (#900)
by Anders
04:33
created

PHPParserVersion84::parsePropertyHook()   B

Complexity

Conditions 11
Paths 97

Size

Total Lines 57
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 11.0225

Importance

Changes 0
Metric Value
eloc 34
dl 0
loc 57
ccs 33
cts 35
cp 0.9429
rs 7.3166
c 0
b 0
f 0
cc 11
nc 97
nop 2
crap 11.0225

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of PDepend.
5
 *
6
 * PHP Version 5
7
 *
8
 * Copyright (c) 2025 Oliver Eglseder <[email protected]>.
9
 * All rights reserved.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 *
15
 *   * Redistributions of source code must retain the above copyright
16
 *     notice, this list of conditions and the following disclaimer.
17
 *
18
 *   * Redistributions in binary form must reproduce the above copyright
19
 *     notice, this list of conditions and the following disclaimer in
20
 *     the documentation and/or other materials provided with the
21
 *     distribution.
22
 *
23
 *   * Neither the name of Manuel Pichler nor the names of his
24
 *     contributors may be used to endorse or promote products derived
25
 *     from this software without specific prior written permission.
26
 *
27
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
31
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
 * POSSIBILITY OF SUCH DAMAGE.
39
 *
40
 * @copyright 2025 Oliver Eglseder <[email protected]>. All rights reserved.
41
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42
 *
43
 * @since 3.0
44
 */
45
46
namespace PDepend\Source\Language\PHP;
47
48
use PDepend\Source\AST\AbstractASTClassOrInterface;
49
use PDepend\Source\AST\AbstractASTNode;
50
use PDepend\Source\AST\ASTFieldDeclaration;
51
use PDepend\Source\AST\ASTFormalParameter;
52
use PDepend\Source\AST\ASTNode;
53
use PDepend\Source\AST\State;
54
use PDepend\Source\Parser\UnexpectedTokenException;
55
use PDepend\Source\Tokenizer\Token;
56
use PDepend\Source\Tokenizer\Tokens;
57
58
/**
59
 * Concrete parser implementation that supports features up to PHP version 8.4
60
 *
61
 * @copyright 2025 Oliver Eglseder <[email protected]>. All rights reserved.
62
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
63
 *
64
 * @since 3.0
65
 */
66
abstract class PHPParserVersion84 extends PHPParserVersion83
67
{
68
    /**
69
     * This method will be called when the base parser cannot handle an expression
70
     * in the base version. In this method you can implement version specific
71
     * expressions.
72
     *
73
     * @throws UnexpectedTokenException
74
     */
75
    protected function parseOptionalExpressionForVersion(): ?ASTNode
76
    {
77
        return $this->parseExpressionVersion84()
78
            ?: parent::parseOptionalExpressionForVersion();
79
    }
80
81
    /**
82
     * In this method we implement parsing of PHP 8.4 specific expressions.
83
     */
84
    protected function parseExpressionVersion84(): ?ASTNode
85
    {
86
        $this->consumeComments();
87
        $nextTokenType = $this->tokenizer->peek();
88
89
        if ($nextTokenType === Tokens::T_OBJECT_OPERATOR) {
90
            $token = $this->consumeToken($nextTokenType);
91
92
            $expr = $this->builder->buildAstExpression($token->image);
93
            $expr->configureLinesAndColumns(
94
                $token->startLine,
95
                $token->endLine,
96
                $token->startColumn,
97
                $token->endColumn
98
            );
99
100
            return $expr;
101
        }
102
103
        return null;
104
    }
105
106 3
    protected function parseConstructFormalParameterModifiers(): int
107
    {
108
        /** @var array<int, int> */
109 3
        static $states = [
110 3
            Tokens::T_PUBLIC => State::IS_PUBLIC,
111 3
            Tokens::T_PROTECTED => State::IS_PROTECTED,
112 3
            Tokens::T_PRIVATE => State::IS_PRIVATE,
113 3
            Tokens::T_PRIVATE_SET => State::IS_PRIVATE_SET,
114 3
            Tokens::T_PROTECTED_SET => State::IS_PROTECTED_SET,
115 3
            Tokens::T_PUBLIC_SET => State::IS_PUBLIC,
116 3
        ];
117
118 3
        $modifier = $this->checkReadonlyToken();
119 3
        $token = $this->tokenizer->peek();
120
121 3
        if (isset($states[$token])) {
122 3
            $modifier |= $states[$token];
123 3
            $next = $this->tokenizer->next();
124
            assert($next instanceof Token);
125 3
            $this->tokenStack->add($next);
126
127 3
            $token = $this->tokenizer->peek();
128
129 3
            if (isset($states[$token])) {
130 2
                $modifier |= $states[$token];
131 2
                $next = $this->tokenizer->next();
132
                assert($next instanceof Token);
133 2
                $this->tokenStack->add($next);
134
            }
135
        }
136
137 3
        return $modifier | $this->checkReadonlyToken();
138
    }
139
140 9
    protected function parseUnknownDeclaration(int $tokenType, int $modifiers): AbstractASTNode
141
    {
142
        // Handle Asymmetric Property Visibility
143 9
        if (in_array($tokenType, [Tokens::T_PRIVATE_SET, Tokens::T_PROTECTED_SET, Tokens::T_PUBLIC_SET], true)) {
144
            switch ($tokenType) {
145
                case Tokens::T_PRIVATE_SET:
146 1
                    $modifiers |= State::IS_PRIVATE_SET;
147
148 1
                    break;
149
150
                case Tokens::T_PROTECTED_SET:
151 1
                    $modifiers |= State::IS_PROTECTED_SET;
152
153 1
                    break;
154
155
                case Tokens::T_PUBLIC_SET:
156
                    $modifiers |= State::IS_PUBLIC;
157
158
                    break;
159
            }
160
161 2
            $this->consumeToken($tokenType);
162 2
            $this->consumeComments();
163
164 2
            $tokenType = $this->tokenizer->peek();
165
        }
166
167 9
        return parent::parseUnknownDeclaration($tokenType, $modifiers);
168
    }
169
170 2
    protected function parseUnknownTypeBody(
171
        int $tokenType,
172
        AbstractASTClassOrInterface $classOrInterface,
173
        int $modifiers
174
    ): void {
175 2
        if (in_array($tokenType, [Tokens::T_PRIVATE_SET, Tokens::T_PROTECTED_SET, Tokens::T_PUBLIC_SET], true)) {
176 1
            $methodOrProperty = $this->parseUnknownDeclaration($tokenType, $modifiers);
177 1
            $classOrInterface->addChild($methodOrProperty);
178 1
            $this->reset();
179
180 1
            return;
181
        }
182
183 1
        parent::parseUnknownTypeBody($tokenType, $classOrInterface, $modifiers);
184
    }
185
186 41
    protected function isStaticValueTerminator(int $tokenType): bool
187
    {
188 41
        return $tokenType === Tokens::T_CURLY_BRACE_OPEN
189 41
            || parent::isStaticValueTerminator($tokenType);
190
    }
191
192 1
    protected function isStaticValueVersionSpecificTerminator(int $tokenType): bool
193
    {
194 1
        return $tokenType === Tokens::T_CURLY_BRACE_OPEN
195 1
            || parent::isStaticValueVersionSpecificTerminator($tokenType);
196
    }
197
198 32
    protected function parseFieldTermination(int $tokenType, ASTFieldDeclaration $declaration): void
199
    {
200 32
        if ($tokenType === Tokens::T_SEMICOLON) {
201 23
            $this->consumeToken(Tokens::T_SEMICOLON);
202
203 23
            return;
204
        }
205
206 10
        $this->parsePropertyHook($tokenType, $declaration);
207
    }
208
209 3
    protected function parsePromotedParameterExtensions(ASTFormalParameter $parameter): void
210
    {
211 3
        $tokenType = $this->tokenizer->peek();
212 3
        if ($tokenType === Tokens::T_CURLY_BRACE_OPEN) {
213 1
            $this->parsePropertyHook($tokenType, $parameter);
214
        }
215
    }
216
217 11
    private function parsePropertyHook(int $tokenType, ASTFieldDeclaration|ASTFormalParameter $declaration): void
0 ignored issues
show
Unused Code introduced by
The parameter $tokenType 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

217
    private function parsePropertyHook(/** @scrutinizer ignore-unused */ int $tokenType, ASTFieldDeclaration|ASTFormalParameter $declaration): void

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...
218
    {
219 11
        $this->consumeToken(Tokens::T_CURLY_BRACE_OPEN);
220 11
        $tokenType = $this->tokenizer->peek();
221
222 11
        while ($tokenType === Tokens::T_STRING || $tokenType === Tokens::T_FINAL) {
223 11
            $modifiers = State::IS_PUBLIC;
224
225 11
            if ($tokenType === Tokens::T_FINAL) {
226 1
                $modifiers |= State::IS_FINAL;
227 1
                $this->consumeToken(Tokens::T_FINAL);
228
            }
229
230 11
            $token = $this->tokenizer->currentToken();
231
            assert($token instanceof Token);
232 11
            $hook = $this->builder->buildPropertyHook($token->image);
233
234 11
            if ($declaration->isProtected()) {
235
                $modifiers = ($modifiers & ~State::IS_PUBLIC & ~State::IS_PRIVATE) | State::IS_PROTECTED;
236 11
            } elseif ($declaration->isPrivate()) {
237
                $modifiers = ($modifiers & ~State::IS_PUBLIC & ~State::IS_PROTECTED) | State::IS_PRIVATE;
238
            }
239 11
            if ($token->image === 'set') {
240 10
                if ($declaration->isProtectedSet()) {
241 1
                    $modifiers = ($modifiers & ~State::IS_PUBLIC & ~State::IS_PRIVATE) | State::IS_PROTECTED;
242 9
                } elseif ($declaration->isPrivateSet()) {
243 1
                    $modifiers = ($modifiers & ~State::IS_PUBLIC & ~State::IS_PROTECTED) | State::IS_PRIVATE;
244
                }
245
            }
246
247 11
            $hook->setModifiers($modifiers);
248
249 11
            $this->consumeToken(Tokens::T_STRING);
250
251 11
            $tokenType = $this->tokenizer->peek();
252
253 11
            if ($tokenType === Tokens::T_PARENTHESIS_OPEN) {
254 1
                $hook->addChild($this->parseFormalParameters($hook));
255
            }
256
257 11
            if ($tokenType === Tokens::T_CURLY_BRACE_OPEN) {
258 8
                $hook->addChild($this->parseScope());
259
            } else {
260 3
                $hook->addChild(
261 3
                    $this->buildReturnStatement(
262 3
                        $this->consumeToken(Tokens::T_DOUBLE_ARROW)
263 3
                    )
264 3
                );
265 3
                $this->consumeToken(Tokens::T_SEMICOLON);
266
            }
267
268 11
            $declaration->addChild($hook);
269
270 11
            $tokenType = $this->tokenizer->peek();
271
        }
272
273 11
        $this->consumeToken(Tokens::T_CURLY_BRACE_CLOSE);
274
    }
275
}
276