Completed
Push — master ( a5233f...937e4c )
by Gaetano
06:11
created

LocationManager   F

Complexity

Total Complexity 94

Size/Duplication

Total Lines 424
Duplicated Lines 16.98 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 75.53%

Importance

Changes 0
Metric Value
wmc 94
lcom 1
cbo 13
dl 72
loc 424
ccs 142
cts 188
cp 0.7553
rs 3.4814
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
C create() 0 60 13
A load() 12 12 3
D update() 0 82 23
A delete() 0 12 2
C matchLocations() 26 26 7
A matchLocationByKey() 0 4 1
D matchContents() 30 30 10
A getSortField() 0 10 2
A getSortOrder() 0 10 2
D setReferences() 4 94 30

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like LocationManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LocationManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use eZ\Publish\API\Repository\Values\Content\Location;
6
use Kaliop\eZMigrationBundle\API\Collection\ContentCollection;
7
use Kaliop\eZMigrationBundle\API\Collection\LocationCollection;
8
use Kaliop\eZMigrationBundle\Core\Matcher\ContentMatcher;
9
use Kaliop\eZMigrationBundle\Core\Matcher\LocationMatcher;
10
use Kaliop\eZMigrationBundle\Core\Helper\SortConverter;
11
12
class LocationManager extends RepositoryExecutor
13
{
14
    protected $supportedStepTypes = array('location');
15
    protected $supportedActions = array('create', 'load', 'update', 'delete');
16
17
    protected $contentMatcher;
18 20
    protected $locationMatcher;
19
    protected $sortConverter;
20 20
21 20
    public function __construct(ContentMatcher $contentMatcher, LocationMatcher $locationMatcher, SortConverter $sortConverter)
22 20
    {
23
        $this->contentMatcher = $contentMatcher;
24
        $this->locationMatcher = $locationMatcher;
25
        $this->sortConverter = $sortConverter;
26
    }
27 1
28
    /**
29 1
     * Method to handle the create operation of the migration instructions
30
     */
31 1
    protected function create()
32
    {
33
        $locationService = $this->repository->getLocationService();
34 1
35 1
        if (!isset($this->dsl['parent_location']) && !isset($this->dsl['parent_location_id'])) {
36 1
            throw new \Exception('Missing parent location id. This is required to create the new location.');
37 1
        }
38 1
39 1
        // support legacy tag: parent_location_id
40 1
        if (!isset($this->dsl['parent_location']) && isset($this->dsl['parent_location_id'])) {
41 1
            $parentLocationIds = $this->dsl['parent_location_id'];
42
        } else {
43 1
            $parentLocationIds = $this->dsl['parent_location'];
44
        }
45 1
46 1
        if (!is_array($parentLocationIds)) {
47 1
            $parentLocationIds = array($parentLocationIds);
48
        }
49 1
50 1
        // resolve references and remote ids
51
        foreach ($parentLocationIds as $id => $parentLocationId) {
52 1
            $parentLocationId = $this->referenceResolver->resolveReference($parentLocationId);
53
            $parentLocationIds[$id] = $this->matchLocationByKey($parentLocationId)->id;
54 1
        }
55 1
56 1
        $contentCollection = $this->matchContents('create');
57
58 1
        $locations = null;
59 1
        foreach ($contentCollection as $content) {
0 ignored issues
show
Bug introduced by
The expression $contentCollection of type object<Kaliop\eZMigratio...ContentCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
60
            $contentInfo = $content->contentInfo;
61 1
62 1
            foreach ($parentLocationIds as $parentLocationId) {
63 1
                $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocationId);
64
65 1
                if (isset($this->dsl['is_hidden'])) {
66 1
                    $locationCreateStruct->hidden = $this->dsl['is_hidden'];
67
                }
68
69
                if (isset($this->dsl['priority'])) {
70
                    $locationCreateStruct->priority = $this->dsl['priority'];
71
                }
72
73
                if (isset($this->dsl['sort_order'])) {
74
                    $locationCreateStruct->sortOrder = $this->getSortOrder($this->dsl['sort_order']);
75 1
                }
76
77 1
                if (isset($this->dsl['sort_field'])) {
78
                    $locationCreateStruct->sortField = $this->getSortField($this->dsl['sort_field']);
79 1
                }
80
81 1
                $locations[] = $locationService->createLocation($contentInfo, $locationCreateStruct);
82
            }
83
        }
84
85 1
        $locationCollection = new LocationCollection($locations);
0 ignored issues
show
Documentation introduced by
$locations is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
86
87
        $this->setReferences($locationCollection);
88
89 1
        return $locationCollection;
90
    }
91
92 View Code Duplication
    protected function load()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
93
    {
94
        $locationCollection = $this->matchLocations('load');
95
96
        if (count($locationCollection) > 1 && isset($this->dsl['references'])) {
97
            throw new \Exception("Can not execute Location load because multiple locations match, and a references section is specified in the dsl. References can be set when only 1 location matches");
98 1
        }
99
100
        $this->setReferences($locationCollection);
0 ignored issues
show
Bug introduced by
It seems like $locationCollection defined by $this->matchLocations('load') on line 94 can be null; however, Kaliop\eZMigrationBundle...anager::setReferences() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
101 1
102 1
        return $locationCollection;
103 1
    }
104 1
105 1
    /**
106 1
     * Updates information for a location like priority, sort field and sort order.
107
     * Updates the visibility of the location when needed.
108 1
     * Can move a location and its children to a new parent location or swap two locations.
109 1
     *
110 1
     * @todo add support for flexible matchers
111
     */
112 1
    protected function update()
113
    {
114
        $locationService = $this->repository->getLocationService();
115
116 1
        $locationCollection = $this->matchLocations('update');
117 1
118 1
        if (count($locationCollection) > 1 && isset($this->dsl['references'])) {
119
            throw new \Exception("Can not execute Location update because multiple locations match, and a references section is specified in the dsl. References can be set when only 1 location matches");
120 1
        }
121
122
        if (count($locationCollection) > 1 && isset($this->dsl['swap_with_location'])) {
123
            throw new \Exception("Can not execute Location update because multiple locations match, and a swap_with_location is specified in the dsl.");
124 1
        }
125 1
126
        // support legacy tag: parent_location_id
127
        if (isset($this->dsl['swap_with_location']) && (isset($this->dsl['parent_location']) || isset($this->dsl['parent_location_id']))) {
128 1
            throw new \Exception('Cannot move location to a new parent and swap location with another location at the same time.');
129 1
        }
130 1
131 1
        foreach ($locationCollection as $key => $location) {
0 ignored issues
show
Bug introduced by
The expression $locationCollection of type object<Kaliop\eZMigratio...ocationCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
132 1
133
            if (isset($this->dsl['priority'])
134 1
                || isset($this->dsl['sort_field'])
135
                || isset($this->dsl['sort_order'])
136
                || isset($this->dsl['remote_id'])
137 1
            ) {
138
                $locationUpdateStruct = $locationService->newLocationUpdateStruct();
139 1
140 1
                    if (isset($this->dsl['priority'])) {
141 1
                        $locationUpdateStruct->priority = $this->dsl['priority'];
142 1
                    }
143 1
144 1
                    if (isset($this->dsl['sort_field'])) {
145 1
                        $locationUpdateStruct->sortField = $this->getSortField($this->dsl['sort_field'], $location->sortField);
146
                    }
147
148
                    if (isset($this->dsl['sort_order'])) {
149
                        $locationUpdateStruct->sortOrder = $this->getSortOrder($this->dsl['sort_order'], $location->sortOrder);
150
                    }
151
152
                    if (isset($this->dsl['remote_id'])) {
153
                        $locationUpdateStruct->remoteId = $this->dsl['remote_id'];
154
                    }
155
156 1
                $location = $locationService->updateLocation($location, $locationUpdateStruct);
157 1
            }
158 1
159
            // Check if visibility needs to be updated
160
            if (isset($this->dsl['is_hidden'])) {
161
                if ($this->dsl['is_hidden']) {
162
                    $location = $locationService->hideLocation($location);
163
                } else {
164
                    $location = $locationService->unhideLocation($location);
165
                }
166
            }
167
168
            // Move or swap location
169
            if (isset($this->dsl['parent_location']) || isset($this->dsl['parent_location_id'])) {
170
                // Move the location and all its children to a new parent
171
                $parentLocationId = isset($this->dsl['parent_location']) ? $this->dsl['parent_location'] : $this->dsl['parent_location_id'];
172
                $parentLocationId = $this->referenceResolver->resolveReference($parentLocationId);
173
174
                $newParentLocation = $locationService->loadLocation($parentLocationId);
175
176
                $locationService->moveSubtree($location, $newParentLocation);
177
            } elseif (isset($this->dsl['swap_with_location'])) {
178
                // Swap locations
179
                $swapLocationId = $this->dsl['swap_with_location'];
180
                $swapLocationId = $this->referenceResolver->resolveReference($swapLocationId);
181
182 1
                $locationToSwap = $this->matchLocationByKey($swapLocationId);
183
184 1
                $locationService->swapLocation($location, $locationToSwap);
185 1
            }
186
187
            $locationCollection[$key] = $location;
188
        }
189 1
190 1
        $this->setReferences($locationCollection);
0 ignored issues
show
Bug introduced by
It seems like $locationCollection defined by $this->matchLocations('update') on line 116 can be null; however, Kaliop\eZMigrationBundle...anager::setReferences() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
191 1
192
        return $locationCollection;
193 1
    }
194
195
    /**
196 1
     * Delete locations
197 1
     *
198
     * @todo add support for flexible matchers
199
     */
200
    protected function delete()
201
    {
202
        $locationService = $this->repository->getLocationService();
203
204 1
        $locationCollection = $this->matchLocations('delete');
205 1
206 1
        foreach ($locationCollection as $location) {
0 ignored issues
show
Bug introduced by
The expression $locationCollection of type object<Kaliop\eZMigratio...ocationCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
207 1
            $locationService->deleteLocation($location);
208 1
        }
209
210 1
        return $locationCollection;
211
    }
212
213
    /**
214
     * @param string $action
215
     * @return LocationCollection
216
     * @throws \Exception
217
     */
218 View Code Duplication
    protected function matchLocations($action)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
219
    {
220 1
        if (!isset($this->dsl['location_id']) && !isset($this->dsl['match'])) {
221
            throw new \Exception("The ID or a Match Condition is required to $action a location.");
222 1
        }
223
224
        // Backwards compat
225
        if (!isset($this->dsl['match'])) {
226
            $this->dsl['match'] = array('location_id' => $this->dsl['location_id']);
227 1
        }
228
229
        $match = $this->dsl['match'];
230
231
        // convert the references passed in the match
232
        foreach ($match as $condition => $values) {
233
            if (is_array($values)) {
234
                foreach ($values as $position => $value) {
235 1
                    $match[$condition][$position] = $this->referenceResolver->resolveReference($value);
236
                }
237
            } else {
238 1
                $match[$condition] = $this->referenceResolver->resolveReference($values);
239 1
            }
240
        }
241
242
        return $this->locationMatcher->match($match);
243
    }
244
245
    /**
246 1
     * @param int|string|array $locationKey
247 1
     * @return Location
248 1
     */
249
    public function matchLocationByKey($locationKey)
250 1
    {
251
        return $this->locationMatcher->matchOneByKey($locationKey);
252 1
    }
253
254
    /**
255 1
     * NB: weirdly enough, it returns contents, not locations
256 1
     *
257 1
     * @param string $action
258
     * @return ContentCollection
259 1
     * @throws \Exception
260
     */
261 View Code Duplication
    protected function matchContents($action)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
262
    {
263 1
        if (!isset($this->dsl['object_id']) && !isset($this->dsl['remote_id']) && !isset($this->dsl['match'])) {
264 1
            throw new \Exception("The ID or remote ID of an object or a Match Condition is required to $action a new location.");
265
        }
266 1
267
        // Backwards compat
268 1
        if (!isset($this->dsl['match'])) {
269 1
            if (isset($this->dsl['object_id'])) {
270
                $this->dsl['match'] = array('content_id' => $this->dsl['object_id']);
271 1
            } elseif (isset($this->dsl['remote_id'])) {
272
                $this->dsl['match'] = array('content_remote_id' => $this->dsl['remote_id']);
273
            }
274
        }
275
276
        $match = $this->dsl['match'];
277
278
        // convert the references passed in the match
279
        foreach ($match as $condition => $values) {
280
            if (is_array($values)) {
281
                foreach ($values as $position => $value) {
282
                    $match[$condition][$position] = $this->referenceResolver->resolveReference($value);
283
                }
284 1
            } else {
285 1
                $match[$condition] = $this->referenceResolver->resolveReference($values);
286 1
            }
287 1
        }
288 1
289 1
        return $this->contentMatcher->matchContent($match);
290
    }
291 1
292 1
    /**
293 1
     * @param $newValue
294 1
     * @param null $currentValue
295 1
     * @return int|null
296
     *
297 1
     * * @todo make protected
298
     */
299 1
    public function getSortField($newValue, $currentValue = null)
300
    {
301
        $sortField = $currentValue;
302
303
        if ($newValue !== null) {
304
            $sortField = $this->sortConverter->hash2SortField($newValue);
305
        }
306
307
        return $sortField;
308
    }
309
310
    /**
311 1
     * Get the sort order based on the current value and the value in the DSL definition.
312
     *
313 1
     * @see \eZ\Publish\API\Repository\Values\Content\Location::SORT_ORDER_*
314 1
     *
315
     * @param int $newValue
316
     * @param int $currentValue
317 1
     * @return int|null
318 1
     *
319
     * @todo make protected
320
     */
321 1
    public function getSortOrder($newValue, $currentValue = null)
322 1
    {
323
        $sortOrder = $currentValue;
324 1
325 1
        if ($newValue !== null) {
326 1
            $sortOrder = $this->sortConverter->hash2SortOrder($newValue);
327 1
        }
328 1
329 1
        return $sortOrder;
330 1
    }
331 1
332 1
    /**
333 1
     * Sets references to object attributes
334
     *
335
     * The Location Manager currently supports setting references to location id.
336 1
     *
337
     * @throws \InvalidArgumentException When trying to set a reference to an unsupported attribute.
338 1
     * @param \eZ\Publish\API\Repository\Values\Content\Location|LocationCollection $location
339 1
     * @return boolean
340
     */
341 1
    protected function setReferences($location)
342
    {
343
        if (!array_key_exists('references', $this->dsl)) {
344
            return false;
345
        }
346
347
        if ($location instanceof LocationCollection) {
348
            if (count($location) > 1) {
349
                throw new \InvalidArgumentException('Location Manager does not support setting references for creating/updating of multiple locations');
350
            }
351
            $location = reset($location);
352
        }
353
354
        foreach ($this->dsl['references'] as $reference) {
355
            switch ($reference['attribute']) {
356
                case 'location_id':
357
                case 'id':
358
                    $value = $location->id;
359
                    break;
360
                case 'remote_id':
361
                case 'location_remote_id':
362
                    $value = $location->remoteId;
363
                    break;
364
                case 'always_available':
365
                    $value = $location->contentInfo->alwaysAvailable;
366
                    break;
367
                case 'content_id':
368
                    $value = $location->contentId;
369
                    break;
370
                case 'content_type_id':
371
                    $value = $location->contentInfo->contentTypeId;
372
                    break;
373 View Code Duplication
                case 'content_type_identifier':
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...
374
                    $contentTypeService = $this->repository->getContentTypeService();
375
                    $value = $contentTypeService->loadContentType($location->contentInfo->contentTypeId)->identifier;
376
                    break;
377
                case 'current_version':
378
                case 'current_version_no':
379
                    $value = $location->contentInfo->currentVersionNo;
380
                    break;
381
                case 'depth':
382
                    $value = $location->depth;
383
                    break;
384
                case 'is_hidden':
385
                    $value = $location->hidden;
386
                    break;
387
                case 'main_location_id':
388
                    $value = $location->contentInfo->mainLocationId;
389
                    break;
390
                case 'main_language_code':
391
                    $value = $location->contentInfo->mainLanguageCode;
392
                    break;
393
                case 'modification_date':
394
                    $value = $location->contentInfo->modificationDate->getTimestamp();
395
                    break;
396
                case 'name':
397
                    $value = $location->contentInfo->name;
398
                    break;
399
                case 'owner_id':
400
                    $value = $location->contentInfo->ownerId;
401
                    break;
402
                case 'parent_location_id':
403
                    $value = $location->parentLocationId;
404
                    break;
405
                case 'path':
406
                    $value = $location->pathString;
407
                    break;
408
                case 'position':
409
                    $value = $location->position;
410
                    break;
411
                case 'priority':
412
                    $value = $location->priority;
413
                    break;
414
                case 'publication_date':
415
                    $value = $location->contentInfo->publishedDate->getTimestamp();
416
                    break;
417
                case 'section_id':
418
                    $value = $location->contentInfo->sectionId;
419
                    break;
420
                case 'sort_field':
421
                    $value = $this->sortConverter->sortField2Hash($location->sortField);
422
                    break;
423
                case 'sort_order':
424
                    $value = $this->sortConverter->sortOrder2Hash($location->sortOrder);
425
                    break;
426
                default:
427
                    throw new \InvalidArgumentException('Location Manager does not support setting references for attribute ' . $reference['attribute']);
428
            }
429
430
            $this->referenceResolver->addReference($reference['identifier'], $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kaliop\eZMigrationBundle...erenceResolverInterface as the method addReference() does only exist in the following implementations of said interface: Kaliop\eZMigrationBundle...ver\ChainPrefixResolver, Kaliop\eZMigrationBundle...ver\ChainRegexpResolver, Kaliop\eZMigrationBundle...eResolver\ChainResolver, Kaliop\eZMigrationBundle...CustomReferenceResolver.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
431
        }
432
433
        return true;
434
    }
435
}
436