StaticPropertyAssignmentAnalyzer::analyze()   F
last analyzed

Complexity

Conditions 39
Paths 2934

Size

Total Lines 268

Duplication

Lines 143
Ratio 53.36 %

Importance

Changes 0
Metric Value
cc 39
nc 2934
nop 5
dl 143
loc 268
rs 0
c 0
b 0
f 0

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
namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
3
4
use PhpParser;
5
use PhpParser\Node\Expr\PropertyFetch;
6
use PhpParser\Node\Stmt\PropertyProperty;
7
use Psalm\Internal\Analyzer\ClassAnalyzer;
8
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
9
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
10
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
11
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
12
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\InstancePropertyFetchAnalyzer;
13
use Psalm\Internal\Analyzer\StatementsAnalyzer;
14
use Psalm\Internal\Analyzer\TypeAnalyzer;
15
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
16
use Psalm\CodeLocation;
17
use Psalm\Context;
18
use Psalm\Issue\DeprecatedProperty;
19
use Psalm\Issue\ImplicitToStringCast;
20
use Psalm\Issue\InaccessibleProperty;
21
use Psalm\Issue\InternalProperty;
22
use Psalm\Issue\InvalidPropertyAssignment;
23
use Psalm\Issue\InvalidPropertyAssignmentValue;
24
use Psalm\Issue\LoopInvalidation;
25
use Psalm\Issue\MixedAssignment;
26
use Psalm\Issue\MixedPropertyAssignment;
27
use Psalm\Issue\MixedPropertyTypeCoercion;
28
use Psalm\Issue\NoInterfaceProperties;
29
use Psalm\Issue\NullPropertyAssignment;
30
use Psalm\Issue\PossiblyFalsePropertyAssignmentValue;
31
use Psalm\Issue\PossiblyInvalidPropertyAssignment;
32
use Psalm\Issue\PossiblyInvalidPropertyAssignmentValue;
33
use Psalm\Issue\PossiblyNullPropertyAssignment;
34
use Psalm\Issue\PossiblyNullPropertyAssignmentValue;
35
use Psalm\Issue\PropertyTypeCoercion;
36
use Psalm\Issue\UndefinedClass;
37
use Psalm\Issue\UndefinedPropertyAssignment;
38
use Psalm\Issue\UndefinedMagicPropertyAssignment;
39
use Psalm\Issue\UndefinedThisPropertyAssignment;
40
use Psalm\IssueBuffer;
41
use Psalm\Type;
42
use Psalm\Type\Atomic\TNamedObject;
43
use Psalm\Type\Atomic\TNull;
44
use Psalm\Type\Atomic\TObject;
45
use function count;
46
use function in_array;
47
use function strtolower;
48
use function explode;
49
use Psalm\Internal\Taint\TaintNode;
50
51
/**
52
 * @internal
53
 */
54
class StaticPropertyAssignmentAnalyzer
55
{
56
    /**
57
     * @param   StatementsAnalyzer                         $statements_analyzer
58
     * @param   PhpParser\Node\Expr\StaticPropertyFetch   $stmt
59
     * @param   PhpParser\Node\Expr|null                  $assignment_value
60
     * @param   Type\Union                                $assignment_value_type
61
     * @param   Context                                   $context
62
     *
63
     * @return  false|null
64
     */
65
    public static function analyze(
66
        StatementsAnalyzer $statements_analyzer,
67
        PhpParser\Node\Expr\StaticPropertyFetch $stmt,
68
        $assignment_value,
69
        Type\Union $assignment_value_type,
70
        Context $context
71
    ) {
72
        $var_id = ExpressionIdentifier::getArrayVarId(
73
            $stmt,
74
            $context->self,
75
            $statements_analyzer
76
        );
77
78
        $fq_class_name = (string) $statements_analyzer->node_data->getType($stmt->class);
79
80
        $codebase = $statements_analyzer->getCodebase();
81
82
        $prop_name = $stmt->name;
83
84
        if (!$prop_name instanceof PhpParser\Node\Identifier) {
85
            if (ExpressionAnalyzer::analyze($statements_analyzer, $prop_name, $context) === false) {
86
                return false;
87
            }
88
89
            if ($fq_class_name && !$context->ignore_variable_property) {
90
                $codebase->analyzer->addMixedMemberName(
91
                    strtolower($fq_class_name) . '::$',
92
                    $context->calling_method_id ?: $statements_analyzer->getFileName()
93
                );
94
            }
95
96
            return;
97
        }
98
99
        $property_id = $fq_class_name . '::$' . $prop_name;
100
101 View Code Duplication
        if (!$codebase->properties->propertyExists($property_id, false, $statements_analyzer, $context)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
102
            if (IssueBuffer::accepts(
103
                new UndefinedPropertyAssignment(
104
                    'Static property ' . $property_id . ' is not defined',
105
                    new CodeLocation($statements_analyzer->getSource(), $stmt),
106
                    $property_id
107
                ),
108
                $statements_analyzer->getSuppressedIssues()
109
            )) {
110
                // fall through
111
            }
112
113
            return;
114
        }
115
116 View Code Duplication
        if (ClassLikeAnalyzer::checkPropertyVisibility(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117
            $property_id,
118
            $context,
119
            $statements_analyzer,
120
            new CodeLocation($statements_analyzer->getSource(), $stmt),
121
            $statements_analyzer->getSuppressedIssues()
122
        ) === false) {
123
            return false;
124
        }
125
126
        $declaring_property_class = (string) $codebase->properties->getDeclaringClassForProperty(
127
            $fq_class_name . '::$' . $prop_name->name,
128
            false
129
        );
130
131
        $declaring_property_id = strtolower((string) $declaring_property_class) . '::$' . $prop_name;
132
133 View Code Duplication
        if ($codebase->alter_code && $stmt->class instanceof PhpParser\Node\Name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
            $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
135
                $codebase,
136
                $statements_analyzer,
137
                $stmt->class,
138
                $fq_class_name,
139
                $context->calling_method_id
140
            );
141
142
            if (!$moved_class) {
143
                foreach ($codebase->property_transforms as $original_pattern => $transformation) {
144
                    if ($declaring_property_id === $original_pattern) {
145
                        list($old_declaring_fq_class_name) = explode('::$', $declaring_property_id);
146
                        list($new_fq_class_name, $new_property_name) = explode('::$', $transformation);
147
148
                        $file_manipulations = [];
149
150
                        if (strtolower($new_fq_class_name) !== strtolower($old_declaring_fq_class_name)) {
151
                            $file_manipulations[] = new \Psalm\FileManipulation(
152
                                (int) $stmt->class->getAttribute('startFilePos'),
153
                                (int) $stmt->class->getAttribute('endFilePos') + 1,
154
                                Type::getStringFromFQCLN(
155
                                    $new_fq_class_name,
156
                                    $statements_analyzer->getNamespace(),
157
                                    $statements_analyzer->getAliasedClassesFlipped(),
158
                                    null
159
                                )
160
                            );
161
                        }
162
163
                        $file_manipulations[] = new \Psalm\FileManipulation(
164
                            (int) $stmt->name->getAttribute('startFilePos'),
165
                            (int) $stmt->name->getAttribute('endFilePos') + 1,
166
                            '$' . $new_property_name
167
                        );
168
169
                        FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
170
                    }
171
                }
172
            }
173
        }
174
175
        $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
176
177
        if ($var_id) {
178
            $context->vars_in_scope[$var_id] = $assignment_value_type;
179
        }
180
181
        $class_property_type = $codebase->properties->getPropertyType(
182
            $property_id,
183
            true,
184
            $statements_analyzer,
185
            $context
186
        );
187
188
        if (!$class_property_type) {
189
            $class_property_type = Type::getMixed();
190
191
            $source_analyzer = $statements_analyzer->getSource()->getSource();
192
193
            $prop_name_name = $prop_name->name;
194
195 View Code Duplication
            if ($source_analyzer instanceof ClassAnalyzer
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
                && $fq_class_name === $source_analyzer->getFQCLN()
197
            ) {
198
                if (isset($source_analyzer->inferred_property_types[$prop_name_name])) {
199
                    $source_analyzer->inferred_property_types[$prop_name_name] = Type::combineUnionTypes(
200
                        $assignment_value_type,
201
                        $source_analyzer->inferred_property_types[$prop_name_name]
202
                    );
203
                } else {
204
                    $source_analyzer->inferred_property_types[$prop_name_name] = $assignment_value_type;
205
                }
206
            }
207
        } else {
208
            $class_property_type = clone $class_property_type;
209
        }
210
211
        if ($assignment_value_type->hasMixed()) {
212
            return null;
213
        }
214
215
        if ($class_property_type->hasMixed()) {
216
            return null;
217
        }
218
219
        $class_property_type = \Psalm\Internal\Type\TypeExpander::expandUnion(
220
            $codebase,
221
            $class_property_type,
222
            $fq_class_name,
223
            $fq_class_name,
224
            $class_storage->parent_class
225
        );
226
227
        $union_comparison_results = new \Psalm\Internal\Analyzer\TypeComparisonResult();
228
229
        $type_match_found = TypeAnalyzer::isContainedBy(
230
            $codebase,
231
            $assignment_value_type,
232
            $class_property_type,
233
            true,
234
            true,
235
            $union_comparison_results
236
        );
237
238 View Code Duplication
        if ($union_comparison_results->type_coerced) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
            if ($union_comparison_results->type_coerced_from_mixed) {
240
                if (IssueBuffer::accepts(
241
                    new MixedPropertyTypeCoercion(
242
                        $var_id . ' expects \'' . $class_property_type->getId() . '\', '
243
                            . ' parent type `' . $assignment_value_type->getId() . '` provided',
244
                        new CodeLocation(
245
                            $statements_analyzer->getSource(),
246
                            $assignment_value ?: $stmt,
247
                            $context->include_location
248
                        ),
249
                        $property_id
250
                    ),
251
                    $statements_analyzer->getSuppressedIssues()
252
                )) {
253
                    // keep soldiering on
254
                }
255
            } else {
256
                if (IssueBuffer::accepts(
257
                    new PropertyTypeCoercion(
258
                        $var_id . ' expects \'' . $class_property_type->getId() . '\', '
259
                            . ' parent type \'' . $assignment_value_type->getId() . '\' provided',
260
                        new CodeLocation(
261
                            $statements_analyzer->getSource(),
262
                            $assignment_value ?: $stmt,
263
                            $context->include_location
264
                        ),
265
                        $property_id
266
                    ),
267
                    $statements_analyzer->getSuppressedIssues()
268
                )) {
269
                    // keep soldiering on
270
                }
271
            }
272
        }
273
274
        if ($union_comparison_results->to_string_cast) {
275
            if (IssueBuffer::accepts(
276
                new ImplicitToStringCast(
277
                    $var_id . ' expects \'' . $class_property_type . '\', '
278
                        . '\'' . $assignment_value_type . '\' provided with a __toString method',
279
                    new CodeLocation(
280
                        $statements_analyzer->getSource(),
281
                        $assignment_value ?: $stmt,
282
                        $context->include_location
283
                    )
284
                ),
285
                $statements_analyzer->getSuppressedIssues()
286
            )) {
287
                // fall through
288
            }
289
        }
290
291
        if (!$type_match_found && !$union_comparison_results->type_coerced) {
292
            if (TypeAnalyzer::canBeContainedBy($codebase, $assignment_value_type, $class_property_type)) {
293 View Code Duplication
                if (IssueBuffer::accepts(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
                    new PossiblyInvalidPropertyAssignmentValue(
295
                        $var_id . ' with declared type \''
296
                            . $class_property_type->getId() . '\' cannot be assigned type \''
297
                            . $assignment_value_type->getId() . '\'',
298
                        new CodeLocation(
299
                            $statements_analyzer->getSource(),
300
                            $assignment_value ?: $stmt
301
                        ),
302
                        $property_id
303
                    ),
304
                    $statements_analyzer->getSuppressedIssues()
305
                )) {
306
                    return false;
307
                }
308 View Code Duplication
            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
                if (IssueBuffer::accepts(
310
                    new InvalidPropertyAssignmentValue(
311
                        $var_id . ' with declared type \'' . $class_property_type->getId()
312
                            . '\' cannot be assigned type \''
313
                            . $assignment_value_type->getId() . '\'',
314
                        new CodeLocation(
315
                            $statements_analyzer->getSource(),
316
                            $assignment_value ?: $stmt
317
                        ),
318
                        $property_id
319
                    ),
320
                    $statements_analyzer->getSuppressedIssues()
321
                )) {
322
                    return false;
323
                }
324
            }
325
        }
326
327
        if ($var_id) {
328
            $context->vars_in_scope[$var_id] = $assignment_value_type;
329
        }
330
331
        return null;
332
    }
333
}
334