StaticPropertyAssignmentAnalyzer   A
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 280
Duplicated Lines 51.07 %

Coupling/Cohesion

Components 1
Dependencies 30

Importance

Changes 0
Metric Value
dl 143
loc 280
rs 9.28
c 0
b 0
f 0
wmc 39
lcom 1
cbo 30

1 Method

Rating   Name   Duplication   Size   Complexity  
F analyze() 143 268 39

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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