Passed
Push — master ( 534b0c...8bd354 )
by Matt
05:44
created

DropletService::setFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
namespace Billow;
3
use Billow\Droplets\DropletFactory;
4
use Billow\Droplets\DropletFactoryInterface;
5
use Billow\Actions\ActionInterface;
6
use Billow\Droplets\DropletInterface;
7
use Billow\Exceptions\ProvisionException;
8
use Billow\Exceptions\DropletException;
9
use GuzzleHttp\Exception\RequestException;
10
11
/**
12
 * @author Matt Frost<[email protected]>
13
 * @package Billow
14
 * @license http://opensource.org/licenses/MIT MIT
15
 * @method setClient set the HTTP Client
16
 * @method getClient retrieve or create an HTTP Client
17
 * @method create create a droplet 
18
 * @method retrieve retrieve a droplet by id
19
 * @method retrieveAll retrieve a paginated list of droplets
20
 * @method performAction perform a predefined action on a droplet
21
 */
22
class DropletService
23
{
24
    /**
25
     * Droplet Factory
26
     *
27
     * @var \Billow\Droplets\DropletFactoryInterface
28
     */
29
    private $factory;
30
31
    /**
32
     * Client to make http requests
33
     *
34
     * @var \Billow\ClientInterface
35
     */
36
    private $client;
37
38
    /**
39
     * Method to set factory
40
     *
41
     * @param \Billow\Droplets\DropletFactoryInterface $factory
42
     */
43
    public function setFactory(DropletFactoryInterface $factory)
44
    {
45
        $this->factory = $factory;
46
    }
47
48
    /**
49
     * Method to retrieve or instantiate a Droplet Factory
50
     *
51
     * @return \Billow\Droplets\DropletFactoryInterface
52
     */
53
    public function getFactory()
54
    {
55
        if (!is_null($this->factory)) {
56
            return $this->factory;
57
        }
58
        return new DropletFactory();
59
    }
60
61
    /**
62
     * Method to set client
63
     *
64
     * @param \Billow\ClientInterface $client
65
     */
66
    public function setClient(ClientInterface $client)
67
    {
68
        $this->client = $client;
69
    }
70
71
    /**
72
     * Method to retrieve set client or generate
73
     * a new client if one is not set
74
     *
75
     * @return \Billow\ClientInterface
76
     */
77
    public function getClient()
78
    {
79
        if ($this->client === null) {
80
            $this->client = new Client();
81
        }
82
        return $this->client;
83
    }
84
85
    /**
86
     * Method to create a new Digital Ocean Droplet
87
     *
88
     * @param Array $dropletRequest
89
     * @param Array $headers
90
     * @return \GuzzleHttp\Message\ResponseInterface
91
     * @throws \Billow\Exceptions\ProvisionException
92
     */
93
    public function create(Array $dropletRequest, Array $headers =[])
94
    {
95
        $headers = $this->prepareHeaders($headers);
96
97
        $dropletJSON = json_encode($dropletRequest);
98
        $params = ['headers' => $headers, 'body' => $dropletJSON];
99
100
        $client = $this->getClient();
101
        try {
102
            $response = $client->post('droplets', $params);
103
            return $response;
104
        } catch (RequestException $e) {
105
            $message = 'Failed to provision new droplet';
106
            $code = 0;
107
            if ($e->hasResponse()) {
108
                $response = $e->getResponse();
109
                $message = $response->getBody();
110
                $code = $response->getStatusCode();
111
            }
112
            throw new ProvisionException($message, $code, $e);
113
        }
114
    }
115
116
    /**
117
     * Method to retrieve a droplet by id
118
     *
119
     * @param int $dropletId
120
     * @param Array $headers
121
     * @return \Billow\Droplets\Droplet
122
     * @throws \Billow\Exceptions\DropletException
123
     */
124
    public function retrieve($dropletId, Array $headers = [])
125
    {
126
        $headers = $this->prepareHeaders($headers);
127
        $params = ['headers' => $headers];
128
        $client = $this->getClient();
129
        try {
130
            $response = $client->get('droplets/' . $dropletId, $params);
131
            $factory = $this->getFactory();
132
            $dropletArray = json_decode($response->getBody(), true);
133
            return $factory->getDroplet($dropletArray['droplet']);
134
        } catch (RequestException $e) {
135
            $message = 'Retrieval of droplet failed';
136
            $code = 0;
137
            if ($e->hasResponse()) {
138
                $response = $e->getResponse();
139
                $message = $response->getBody();
140
                $code = $response->getStatusCode();
141
            }
142
            throw new DropletException($message, $code, $e);
143
        }
144
    }
145
146
    /**
147
     * Method to retrieve a paginated list of droplets
148
     *
149
     * @param Array headers
150
     * @param int per_page
151
     * @param int page
152
     * @return Array
153
     */
154
    public function retrieveAll(Array $headers = [], $per_page = 25, $page = 1)
155
    {
156
        $headers = $this->prepareHeaders($headers);
157
        $params = ['headers' => $headers];
158
        $client = $this->getClient();
159
        $endpoint = 'droplets?page='. $page .'&per_page=' . $per_page;
160
        $dropletArray = [];
161
        $dropletArray['droplets'] = [];
162
        $dropletArray['meta'] = [];
163
        $dropletArray['links'] = [];
164
        try {
165
            $response = $client->get($endpoint, $params);
166
            $factory = $this->getFactory();
167
            $responseArray = json_decode($response->getBody(), true);
168
            foreach ($responseArray['droplets'] as $droplet) {
169
                $dropletArray['droplets'][] = $factory->getDroplet($droplet);
170
            }
171
            $dropletArray['meta'] = $responseArray['meta'];
172
            $dropletArray['links'] = $responseArray['links'];
173
            return $dropletArray;
174
        } catch(RequestException $e) {
175
            $message = 'Retrieval of droplets failed';
176
            $code = 0;
177
            if ($e->hasResponse()) {
178
                $response = $e->getResponse();
179
                $message = $response->getBody();
180
                $code = $response->getStatusCode();
181
            }
182
            throw new DropletException($message, $code, $e);
183
        }
184
    }
185
186
    /**
187
     * Method to perform an action on a Digital Ocean Droplet
188
     *
189
     * @param \Billow\Droplets\DropletInterface
190
     * @param \Billow\Actions\ActionInterface $action
191
     * @param Array $headers
192
     * @return \GuzzleHttp\Message\ResponseInterface
193
     */
194
    public function performAction(DropletInterface $droplet, ActionInterface $action, Array $headers = [])
195
    {
196
        $headers = $this->prepareHeaders($headers);
197
        $dropletValues = $droplet->toArray();   
198
        $id = $dropletValues['id'];
199
        $action->setId($id);
200
        $request = $action->getRequest($headers, $action->getBody());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Billow\Actions\ActionInterface as the method getBody() does only exist in the following implementations of said interface: Billow\Actions\ChangeKernel, Billow\Actions\CreateSnapshot, Billow\Actions\DisableBackups, Billow\Actions\EnableBackups, Billow\Actions\EnableIPv6, Billow\Actions\EnablePrivateNetworking, Billow\Actions\PasswordReset, Billow\Actions\PowerCycle, Billow\Actions\PowerOff, Billow\Actions\PowerOn, Billow\Actions\Reboot, Billow\Actions\Rebuild, Billow\Actions\Rename, Billow\Actions\Resize, Billow\Actions\Restore, Billow\Actions\ShutDown, Billow\Actions\Upgrade.

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...
201
        $client = $this->getClient();
202
        $response = $client->send($request);
203
        return $response;       
204
    }
205
206
    /**
207
     * Method to set the default Content-type if one is not sent
208
     *
209
     * @param Array Headers
210
     * @return Array
211
     */ 
212
    private function prepareHeaders(array $headers)
213
    {
214
        if (!isset($headers['Content-type']) && !isset($headers['Content-Type'])) {
215
            $headers['Content-type'] = 'application/json';
216
        } 
217
        return $headers;
218
    }
219
}
220