GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#159)
by Roman
02:01
created

Server::getAliases()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 9
loc 9
ccs 3
cts 3
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace OpenStack\Compute\v2\Models;
4
5
use OpenStack\Common\Resource\Alias;
6
use OpenStack\Common\Resource\HasWaiterTrait;
7
use OpenStack\Common\Resource\Creatable;
8
use OpenStack\Common\Resource\Deletable;
9
use OpenStack\Common\Resource\Listable;
10
use OpenStack\Common\Resource\Retrievable;
11
use OpenStack\Common\Resource\Updateable;
12
use OpenStack\Common\Resource\OperatorResource;
13
use OpenStack\Common\Transport\Utils;
14
use OpenStack\BlockStorage\v2\Models\VolumeAttachment;
15
use OpenStack\Networking\v2\Models\InterfaceAttachment;
16
use OpenStack\Compute\v2\Enum;
17
use OpenStack\Networking\v2\Extensions\SecurityGroups\Models\SecurityGroup;
18
use Psr\Http\Message\ResponseInterface;
19
20
/**
21
 * @property \OpenStack\Compute\v2\Api $api
22
 */
23
class Server extends OperatorResource implements
24
    Creatable,
25
    Updateable,
26
    Deletable,
27
    Retrievable,
28
    Listable
29
{
30
    use HasWaiterTrait;
31
32
    /** @var string */
33
    public $id;
34
35
    /** @var string */
36
    public $ipv4;
37
38
    /** @var string */
39
    public $ipv6;
40
41
    /** @var array */
42
    public $addresses;
43
44
    /** @var \DateTimeImmutable */
45
    public $created;
46
47
    /** @var \DateTimeImmutable */
48
    public $updated;
49
50
    /** @var Flavor */
51
    public $flavor;
52
53
    /** @var string */
54
    public $hostId;
55
56
    /** @var string */
57
    public $hypervisorHostname;
58
59
    /** @var Image */
60
    public $image;
61
62
    /** @var array */
63
    public $links;
64
65
    /** @var array */
66
    public $metadata;
67
68
    /** @var string */
69
    public $name;
70
71
    /** @var string */
72
    public $progress;
73
74
    /** @var string */
75
    public $status;
76
77
    /** @var string */
78
    public $tenantId;
79
80
    /** @var string */
81
    public $userId;
82
83
    /** @var string */
84
    public $adminPass;
85
86
    /** @var string */
87
    public $taskState;
88
89
    /** @var string */
90
    public $powerState;
91
92
    /** @var string */
93
    public $vmState;
94
95
    /** @var Fault */
96
    public $fault;
97
98
    protected $resourceKey = 'server';
99
    protected $resourcesKey = 'servers';
100
    protected $markerKey = 'id';
101 2
102
    protected $aliases = [
103 2
        'block_device_mapping_v2'             => 'blockDeviceMapping',
104 2
        'accessIPv4'                          => 'ipv4',
105
        'accessIPv6'                          => 'ipv6',
106
        'tenant_id'                           => 'tenantId',
107
        'user_id'                             => 'userId',
108
        'security_groups'                     => 'securityGroups',
109
        'OS-EXT-STS:task_state'               => 'taskState',
110 1
        'OS-EXT-STS:power_state'              => 'powerState',
111
        'OS-EXT-STS:vm_state'                 => 'vmState',
112 1
        'OS-EXT-SRV-ATTR:hypervisor_hostname' => 'hypervisorHostname',
113
    ];
114 1
115
    /**
116
     * @inheritdoc
117
     */
118 View Code Duplication
    protected function getAliases(): array
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...
119
    {
120 1
        return parent::getAliases() + [
121
            'image'   => new Alias('image', Image::class),
122 1
            'flavor'  => new Alias('flavor', Flavor::class),
123 1
            'created' => new Alias('created', \DateTimeImmutable::class),
124
            'updated' => new Alias('updated', \DateTimeImmutable::class)
125
        ];
126
    }
127
128 1
    /**
129
     * {@inheritDoc}
130 1
     *
131
     * @param array $userOptions {@see \OpenStack\Compute\v2\Api::postServer}
132 1
     */
133
    public function create(array $userOptions): Creatable
134
    {
135
        if (!isset($userOptions['imageId']) && !isset($userOptions['blockDeviceMapping'][0]['uuid'])) {
136
            throw new \RuntimeException('imageId or blockDeviceMapping.uuid must be set.');
137
        }
138
139
        $response = $this->execute($this->api->postServer(), $userOptions);
140 1
        return $this->populateFromResponse($response);
141
    }
142 1
143 1
    /**
144
     * {@inheritDoc}
145 1
     */
146 1
    public function update()
147
    {
148
        $response = $this->execute($this->api->putServer(), $this->getAttrs(['id', 'name', 'ipv4', 'ipv6']));
149
        $this->populateFromResponse($response);
150
    }
151
152
    /**
153 2
     * {@inheritDoc}
154
     */
155 2
    public function delete()
156 1
    {
157
        $this->execute($this->api->deleteServer(), $this->getAttrs(['id']));
158
    }
159 1
160 1
    /**
161 1
     * {@inheritDoc}
162 1
     */
163 1
    public function retrieve()
164
    {
165
        $response = $this->execute($this->api->getServer(), $this->getAttrs(['id']));
166
        $this->populateFromResponse($response);
167
    }
168
169
    /**
170 1
     * Changes the root password for a server.
171
     *
172 1
     * @param string $newPassword The new root password
173 1
     */
174
    public function changePassword(string $newPassword)
175 1
    {
176 1
        $this->execute($this->api->changeServerPassword(), [
177
            'id'       => $this->id,
178
            'password' => $newPassword,
179
        ]);
180
    }
181
182
    /**
183
     * Reboots the server.
184 1
     *
185
     * @param string $type The type of reboot that will be performed. Either SOFT or HARD is supported.
186 1
     */
187 1
    public function reboot(string $type = Enum::REBOOT_SOFT)
188 1
    {
189 1
        if (!in_array($type, ['SOFT', 'HARD'])) {
190
            throw new \RuntimeException('Reboot type must either be SOFT or HARD');
191 1
        }
192 1
193
        $this->execute($this->api->rebootServer(), [
194
            'id'   => $this->id,
195
            'type' => $type,
196
        ]);
197 1
    }
198
199 1
    /**
200 1
     * Starts server
201
     */
202
    public function start()
203
    {
204
        $this->execute($this->api->startServer(), [
205 1
            'id' => $this->id,
206
            'os-start' => null
207 1
        ]);
208 1
    }
209
210
    /**
211
     * Stops server
212
     */
213
    public function stop()
214
    {
215 1
        $this->execute($this->api->stopServer(), [
216
            'id' => $this->id,
217 1
            'os-stop' => null
218 1
        ]);
219 1
    }
220
221
    /**
222
     * Rebuilds the server.
223
     *
224
     * @param array $options {@see \OpenStack\Compute\v2\Api::rebuildServer}
225
     */
226
    public function rebuild(array $options)
227
    {
228 2
        $options['id'] = $this->id;
229
        $response = $this->execute($this->api->rebuildServer(), $options);
230 2
231
        $this->populateFromResponse($response);
232 2
    }
233 2
234 2
    /**
235
     * Resizes the server to a new flavor. Once this operation is complete and server has transitioned
236
     * to an active state, you will either need to call {@see confirmResize()} or {@see revertResize()}.
237
     *
238
     * @param string $flavorId The UUID of the new flavor your server will be based on.
239
     */
240
    public function resize(string $flavorId)
241
    {
242 1
        $response = $this->execute($this->api->resizeServer(), [
243
            'id'       => $this->id,
244 1
            'flavorId' => $flavorId,
245 1
        ]);
246
247
        $this->populateFromResponse($response);
248
    }
249
250
    /**
251
     * Confirms a previous resize operation.
252
     */
253
    public function confirmResize()
254
    {
255
        $this->execute($this->api->confirmServerResize(), ['confirmResize' => null, 'id' => $this->id]);
256 1
    }
257
258 1
    /**
259 1
     * Reverts a previous resize operation.
260
     */
261
    public function revertResize()
262
    {
263
        $this->execute($this->api->revertServerResize(), ['revertResize' => null, 'id' => $this->id]);
264
    }
265
266
    /**
267
     * Gets a VNC console for a server.
268
     *
269
     * @param  string $type The type of VNC console: novnc|xvpvnc.
270
     *                      Defaults to novnc.
271 1
     *
272
     * @return array
273 1
     */
274 1
    public function getVncConsole($type = Enum::CONSOLE_NOVNC): array
275
    {
276
        $response = $this->execute($this->api->getVncConsole(), ['id' => $this->id, 'type' => $type]);
277
        return Utils::jsonDecode($response)['console'];
278
    }
279
280
    /**
281
     * Gets a RDP console for a server.
282
     *
283
     * @param  string $type The type of VNC console: rdp-html5 (default).
284 1
     *
285
     * @return array
286 1
     */
287 1
    public function getRDPConsole($type = Enum::CONSOLE_RDP_HTML5): array
288
    {
289
        $response = $this->execute($this->api->getRDPConsole(), ['id' => $this->id, 'type' => $type]);
290
        return Utils::jsonDecode($response)['console'];
291
    }
292
293
    /**
294
     * Gets a Spice console for a server.
295 1
     *
296
     * @param  string $type The type of VNC console: spice-html5.
297 1
     *
298 1
     * @return array
299
     */
300
    public function getSpiceConsole($type = Enum::CONSOLE_SPICE_HTML5): array
301
    {
302
        $response = $this->execute($this->api->getSpiceConsole(), ['id' => $this->id, 'type' => $type]);
303
        return Utils::jsonDecode($response)['console'];
304
    }
305
306
    /**
307
     * Gets a serial console for a server.
308
     *
309
     * @param  string $type The type of VNC console: serial.
310
     *
311
     * @return array
312
     */
313
    public function getSerialConsole($type = Enum::CONSOLE_SERIAL): array
314
    {
315
        $response = $this->execute($this->api->getSerialConsole(), ['id' => $this->id, 'type' => $type]);
316
        return Utils::jsonDecode($response)['console'];
317
    }
318
319
    /**
320
     * Creates an image for the current server.
321
     *
322
     * @param array $options {@see \OpenStack\Compute\v2\Api::createServerImage}
323
     */
324
    public function createImage(array $options)
325
    {
326
        $options['id'] = $this->id;
327
        $this->execute($this->api->createServerImage(), $options);
328
    }
329
330
    /**
331
     * Iterates over all the IP addresses for this server.
332
     *
333
     * @param array $options {@see \OpenStack\Compute\v2\Api::getAddressesByNetwork}
334
     *
335
     * @return array An array containing to two keys: "public" and "private"
336
     */
337
    public function listAddresses(array $options = []): array
338
    {
339
        $options['id'] = $this->id;
340
341
        $data = (isset($options['networkLabel'])) ? $this->api->getAddressesByNetwork() : $this->api->getAddresses();
342
        $response = $this->execute($data, $options);
343
        return Utils::jsonDecode($response)['addresses'];
344
    }
345
346
    /**
347
     * Returns Generator for InterfaceAttachment
348
     *
349
     * @return \Generator
350
     */
351
    public function listInterfaceAttachments(array $options = []): \Generator
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

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

Loading history...
352
    {
353
        return $this->model(InterfaceAttachment::class)->enumerate($this->api->getInterfaceAttachments(), ['id' => $this->id]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OpenStack\Common\Resource\ResourceInterface as the method enumerate() does only exist in the following implementations of said interface: OpenStack\BlockStorage\v2\Models\QuotaSet, OpenStack\BlockStorage\v2\Models\Snapshot, OpenStack\BlockStorage\v2\Models\Volume, OpenStack\BlockStorage\v2\Models\VolumeAttachment, OpenStack\BlockStorage\v2\Models\VolumeType, OpenStack\Common\Resource\OperatorResource, OpenStack\Compute\v2\Models\AvailabilityZone, OpenStack\Compute\v2\Models\Flavor, OpenStack\Compute\v2\Models\Host, OpenStack\Compute\v2\Models\Hypervisor, OpenStack\Compute\v2\Models\HypervisorStatistic, OpenStack\Compute\v2\Models\Image, OpenStack\Compute\v2\Models\Keypair, OpenStack\Compute\v2\Models\QuotaSet, OpenStack\Compute\v2\Models\Server, OpenStack\Identity\v2\Models\Catalog, OpenStack\Identity\v2\Models\Endpoint, OpenStack\Identity\v2\Models\Entry, OpenStack\Identity\v2\Models\Tenant, OpenStack\Identity\v2\Models\Token, OpenStack\Identity\v3\Models\Assignment, OpenStack\Identity\v3\Models\Catalog, OpenStack\Identity\v3\Models\Credential, OpenStack\Identity\v3\Models\Domain, OpenStack\Identity\v3\Models\Endpoint, OpenStack\Identity\v3\Models\Group, OpenStack\Identity\v3\Models\Policy, OpenStack\Identity\v3\Models\Project, OpenStack\Identity\v3\Models\Role, OpenStack\Identity\v3\Models\Service, OpenStack\Identity\v3\Models\Token, OpenStack\Identity\v3\Models\User, OpenStack\Images\v2\Models\Image, OpenStack\Images\v2\Models\Member, OpenStack\Metric\v1\Gnocchi\Models\Metric, OpenStack\Metric\v1\Gnocchi\Models\Resource, OpenStack\Metric\v1\Gnocchi\Models\ResourceType, OpenStack\Networking\v2\...ayer3\Models\FloatingIp, OpenStack\Networking\v2\...ns\Layer3\Models\Router, OpenStack\Networking\v2\...ps\Models\SecurityGroup, OpenStack\Networking\v2\...odels\SecurityGroupRule, OpenStack\Networking\v2\Models\InterfaceAttachment, OpenStack\Networking\v2\Models\LoadBalancer, OpenStack\Networking\v2\...adBalancerHealthMonitor, OpenStack\Networking\v2\...ls\LoadBalancerListener, OpenStack\Networking\v2\Models\LoadBalancerMember, OpenStack\Networking\v2\Models\LoadBalancerPool, OpenStack\Networking\v2\Models\LoadBalancerStat, OpenStack\Networking\v2\Models\LoadBalancerStatus, OpenStack\Networking\v2\Models\Network, OpenStack\Networking\v2\Models\Port, OpenStack\Networking\v2\Models\Quota, OpenStack\Networking\v2\Models\Subnet, OpenStack\ObjectStore\v1\Models\Account, OpenStack\ObjectStore\v1\Models\Container, OpenStack\ObjectStore\v1\Models\Object.

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...
354
    }
355
356
    /**
357
     * Gets an interface attachment.
358
     *
359
     * @param string $portId The unique ID of the port.
360
     * @return InterfaceAttachment
361
     */
362 View Code Duplication
    public function getInterfaceAttachment(string $portId): InterfaceAttachment
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...
363
    {
364
        $response = $this->execute($this->api->getInterfaceAttachment(), [
365
            'id'     => $this->id,
366
            'portId' => $portId
367
        ]);
368
369
        return $this->model(InterfaceAttachment::class)->populateFromResponse($response);
370
    }
371
372
    /**
373
     * Creates an interface attachment.
374
     *
375
     * @param array $userOptions {@see \OpenStack\Compute\v2\Api::postInterfaceAttachment}
376
     * @return InterfaceAttachment
377
     */
378
    public function createInterfaceAttachment(array $userOptions): InterfaceAttachment
379
    {
380
        if (!isset($userOptions['networkId']) && !isset($userOptions['portId'])) {
381
            throw new \RuntimeException('networkId or portId must be set.');
382
        }
383
384
        $response = $this->execute($this->api->postInterfaceAttachment(), array_merge($userOptions, ['id' => $this->id]));
385
        return $this->model(InterfaceAttachment::class)->populateFromResponse($response);
386
    }
387
388
    /**
389
     * Detaches an interface attachment.
390
     *
391
     * @param string $portId
392
     */
393
    public function detachInterface(string $portId)
394
    {
395
        $this->execute($this->api->deleteInterfaceAttachment(), [
396
            'id' => $this->id,
397
            'portId' => $portId,
398
        ]);
399
    }
400
401
    /**
402
     * Retrieves metadata from the API.
403
     *
404
     * @return array
405
     */
406
    public function getMetadata(): array
407
    {
408
        $response = $this->execute($this->api->getServerMetadata(), ['id' => $this->id]);
409
        return $this->parseMetadata($response);
410
    }
411
412
    /**
413
     * Resets all the metadata for this server with the values provided. All existing metadata keys
414
     * will either be replaced or removed.
415
     *
416
     * @param array $metadata {@see \OpenStack\Compute\v2\Api::putServerMetadata}
417
     */
418
    public function resetMetadata(array $metadata)
419
    {
420
        $response = $this->execute($this->api->putServerMetadata(), ['id' => $this->id, 'metadata' => $metadata]);
421
        $this->metadata = $this->parseMetadata($response);
422
    }
423
424
    /**
425
     * Merges the existing metadata for the server with the values provided. Any existing keys
426
     * referenced in the user options will be replaced with the user's new values. All other
427
     * existing keys will remain unaffected.
428
     *
429
     * @param array $metadata {@see \OpenStack\Compute\v2\Api::postServerMetadata}
430
     *
431
     * @return array
432
     */
433
    public function mergeMetadata(array $metadata)
434
    {
435
        $response = $this->execute($this->api->postServerMetadata(), ['id' => $this->id, 'metadata' => $metadata]);
436
        $this->metadata = $this->parseMetadata($response);
437
    }
438
439
    /**
440
     * Retrieve the value for a specific metadata key.
441
     *
442
     * @param string $key {@see \OpenStack\Compute\v2\Api::getServerMetadataKey}
443
     *
444
     * @return mixed
445
     */
446 View Code Duplication
    public function getMetadataItem(string $key)
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...
447
    {
448
        $response = $this->execute($this->api->getServerMetadataKey(), ['id' => $this->id, 'key' => $key]);
449
        $value = $this->parseMetadata($response)[$key];
450
        $this->metadata[$key] = $value;
451
        return $value;
452
    }
453
454
    /**
455
     * Remove a specific metadata key.
456
     *
457
     * @param string $key {@see \OpenStack\Compute\v2\Api::deleteServerMetadataKey}
458
     */
459 View Code Duplication
    public function deleteMetadataItem(string $key)
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...
460
    {
461
        if (isset($this->metadata[$key])) {
462
            unset($this->metadata[$key]);
463
        }
464
465
        $this->execute($this->api->deleteServerMetadataKey(), ['id' => $this->id, 'key' => $key]);
466
    }
467
468
469
    /**
470
     * Add security group to a server (addSecurityGroup action)
471
     *
472
     * @param array $options {@see \OpenStack\Compute\v2\Api::postSecurityGroup}
473
     *
474
     * @return SecurityGroup
475
     */
476 View Code Duplication
    public function addSecurityGroup(array $options) : SecurityGroup
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...
477
    {
478
        $options['id'] = $this->id;
479
480
        $response = $this->execute($this->api->postSecurityGroup(), $options);
481
482
        return $this->model(SecurityGroup::class)->populateFromResponse($response);
483
    }
484
485
    /**
486
     * Add security group to a server (addSecurityGroup action)
487
     *
488
     * @param array $options {@see \OpenStack\Compute\v2\Api::deleteSecurityGroup}
489
     */
490
    public function removeSecurityGroup(array $options)
491
    {
492
        $options['id'] = $this->id;
493
        $this->execute($this->api->deleteSecurityGroup(), $options);
494
    }
495
496
    public function parseMetadata(ResponseInterface $response): array
497
    {
498
        return Utils::jsonDecode($response)['metadata'];
499
    }
500
501
    /**
502
     * Returns Generator for SecurityGroups
503
     *
504
     * @return \Generator
505
     */
506
    public function listSecurityGroups(): \Generator
507
    {
508
        return $this->model(SecurityGroup::class)->enumerate($this->api->getSecurityGroups(), ['id' => $this->id]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OpenStack\Common\Resource\ResourceInterface as the method enumerate() does only exist in the following implementations of said interface: OpenStack\BlockStorage\v2\Models\QuotaSet, OpenStack\BlockStorage\v2\Models\Snapshot, OpenStack\BlockStorage\v2\Models\Volume, OpenStack\BlockStorage\v2\Models\VolumeAttachment, OpenStack\BlockStorage\v2\Models\VolumeType, OpenStack\Common\Resource\OperatorResource, OpenStack\Compute\v2\Models\AvailabilityZone, OpenStack\Compute\v2\Models\Flavor, OpenStack\Compute\v2\Models\Host, OpenStack\Compute\v2\Models\Hypervisor, OpenStack\Compute\v2\Models\HypervisorStatistic, OpenStack\Compute\v2\Models\Image, OpenStack\Compute\v2\Models\Keypair, OpenStack\Compute\v2\Models\QuotaSet, OpenStack\Compute\v2\Models\Server, OpenStack\Identity\v2\Models\Catalog, OpenStack\Identity\v2\Models\Endpoint, OpenStack\Identity\v2\Models\Entry, OpenStack\Identity\v2\Models\Tenant, OpenStack\Identity\v2\Models\Token, OpenStack\Identity\v3\Models\Assignment, OpenStack\Identity\v3\Models\Catalog, OpenStack\Identity\v3\Models\Credential, OpenStack\Identity\v3\Models\Domain, OpenStack\Identity\v3\Models\Endpoint, OpenStack\Identity\v3\Models\Group, OpenStack\Identity\v3\Models\Policy, OpenStack\Identity\v3\Models\Project, OpenStack\Identity\v3\Models\Role, OpenStack\Identity\v3\Models\Service, OpenStack\Identity\v3\Models\Token, OpenStack\Identity\v3\Models\User, OpenStack\Images\v2\Models\Image, OpenStack\Images\v2\Models\Member, OpenStack\Metric\v1\Gnocchi\Models\Metric, OpenStack\Metric\v1\Gnocchi\Models\Resource, OpenStack\Metric\v1\Gnocchi\Models\ResourceType, OpenStack\Networking\v2\...ayer3\Models\FloatingIp, OpenStack\Networking\v2\...ns\Layer3\Models\Router, OpenStack\Networking\v2\...ps\Models\SecurityGroup, OpenStack\Networking\v2\...odels\SecurityGroupRule, OpenStack\Networking\v2\Models\InterfaceAttachment, OpenStack\Networking\v2\Models\LoadBalancer, OpenStack\Networking\v2\...adBalancerHealthMonitor, OpenStack\Networking\v2\...ls\LoadBalancerListener, OpenStack\Networking\v2\Models\LoadBalancerMember, OpenStack\Networking\v2\Models\LoadBalancerPool, OpenStack\Networking\v2\Models\LoadBalancerStat, OpenStack\Networking\v2\Models\LoadBalancerStatus, OpenStack\Networking\v2\Models\Network, OpenStack\Networking\v2\Models\Port, OpenStack\Networking\v2\Models\Quota, OpenStack\Networking\v2\Models\Subnet, OpenStack\ObjectStore\v1\Models\Account, OpenStack\ObjectStore\v1\Models\Container, OpenStack\ObjectStore\v1\Models\Object.

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...
509
    }
510
511
512
    /**
513
     * Returns Generator for VolumeAttachment
514
     *
515
     * @return \Generator
516
     */
517
    public function listVolumeAttachments(): \Generator
518
    {
519
        return $this->model(VolumeAttachment::class)->enumerate($this->api->getVolumeAttachments(), ['id' => $this->id]);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface OpenStack\Common\Resource\ResourceInterface as the method enumerate() does only exist in the following implementations of said interface: OpenStack\BlockStorage\v2\Models\QuotaSet, OpenStack\BlockStorage\v2\Models\Snapshot, OpenStack\BlockStorage\v2\Models\Volume, OpenStack\BlockStorage\v2\Models\VolumeAttachment, OpenStack\BlockStorage\v2\Models\VolumeType, OpenStack\Common\Resource\OperatorResource, OpenStack\Compute\v2\Models\AvailabilityZone, OpenStack\Compute\v2\Models\Flavor, OpenStack\Compute\v2\Models\Host, OpenStack\Compute\v2\Models\Hypervisor, OpenStack\Compute\v2\Models\HypervisorStatistic, OpenStack\Compute\v2\Models\Image, OpenStack\Compute\v2\Models\Keypair, OpenStack\Compute\v2\Models\QuotaSet, OpenStack\Compute\v2\Models\Server, OpenStack\Identity\v2\Models\Catalog, OpenStack\Identity\v2\Models\Endpoint, OpenStack\Identity\v2\Models\Entry, OpenStack\Identity\v2\Models\Tenant, OpenStack\Identity\v2\Models\Token, OpenStack\Identity\v3\Models\Assignment, OpenStack\Identity\v3\Models\Catalog, OpenStack\Identity\v3\Models\Credential, OpenStack\Identity\v3\Models\Domain, OpenStack\Identity\v3\Models\Endpoint, OpenStack\Identity\v3\Models\Group, OpenStack\Identity\v3\Models\Policy, OpenStack\Identity\v3\Models\Project, OpenStack\Identity\v3\Models\Role, OpenStack\Identity\v3\Models\Service, OpenStack\Identity\v3\Models\Token, OpenStack\Identity\v3\Models\User, OpenStack\Images\v2\Models\Image, OpenStack\Images\v2\Models\Member, OpenStack\Metric\v1\Gnocchi\Models\Metric, OpenStack\Metric\v1\Gnocchi\Models\Resource, OpenStack\Metric\v1\Gnocchi\Models\ResourceType, OpenStack\Networking\v2\...ayer3\Models\FloatingIp, OpenStack\Networking\v2\...ns\Layer3\Models\Router, OpenStack\Networking\v2\...ps\Models\SecurityGroup, OpenStack\Networking\v2\...odels\SecurityGroupRule, OpenStack\Networking\v2\Models\InterfaceAttachment, OpenStack\Networking\v2\Models\LoadBalancer, OpenStack\Networking\v2\...adBalancerHealthMonitor, OpenStack\Networking\v2\...ls\LoadBalancerListener, OpenStack\Networking\v2\Models\LoadBalancerMember, OpenStack\Networking\v2\Models\LoadBalancerPool, OpenStack\Networking\v2\Models\LoadBalancerStat, OpenStack\Networking\v2\Models\LoadBalancerStatus, OpenStack\Networking\v2\Models\Network, OpenStack\Networking\v2\Models\Port, OpenStack\Networking\v2\Models\Quota, OpenStack\Networking\v2\Models\Subnet, OpenStack\ObjectStore\v1\Models\Account, OpenStack\ObjectStore\v1\Models\Container, OpenStack\ObjectStore\v1\Models\Object.

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...
520
    }
521
522
    /**
523
     * Attach a volume and returns volume that was attached
524
     *
525
     * @param $volumeId
526
     *
527
     * @return VolumeAttachment
528
     */
529
    public function attachVolume(string $volumeId): VolumeAttachment
530
    {
531
        $response =  $this->execute($this->api->postVolumeAttachments(), ['id' => $this->id, 'volumeId' => $volumeId]);
532
533
        return $this->model(VolumeAttachment::class)->populateFromResponse($response);
534
    }
535
536
    /**
537
     * Detach a volume
538
     *
539
     * @param $attachmentId
540
     *
541
     * @return void
542
     */
543
    public function detachVolume(string $attachmentId)
544
    {
545
        $this->execute($this->api->deleteVolumeAttachments(), ['id' => $this->id, 'attachmentId' => $attachmentId]);
546
    }
547
}
548