Completed
Pull Request — master (#20)
by Dave
06:27
created

RawGuzzleContext::setRequestOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * Behat Guzzle Extension
4
 *
5
 * PHP version 5
6
 *
7
 * @package Behat\GuzzleExtension
8
 * @author  Dave Nash <[email protected]>
9
 * @license http://opensource.org/licenses/MIT The MIT License
10
 * @version GIT: $Id$
11
 * @link    https://github.com/teaandcode/behat-guzzle-extension GuzzleExtension
12
 */
13
14
namespace Behat\GuzzleExtension\Context;
15
16
use Guzzle\Common\Exception\RuntimeException;
17
use Guzzle\Http\Exception\BadResponseException;
18
use Guzzle\Http\Exception\ClientErrorResponseException;
19
use Guzzle\Http\Message\Response;
20
use Guzzle\Service\Client;
21
22
/**
23
 * Raw Guzzle context for Behat BDD tool
24
 * Provides raw Guzzle integration (without step definitions) and web assertions
25
 *
26
 * @package Behat\GuzzleExtension\Context
27
 * @author  Dave Nash <[email protected]>
28
 * @license http://opensource.org/licenses/MIT The MIT License
29
 * @version Release: @package_version@
30
 * @link    https://github.com/teaandcode/behat-guzzle-extension GuzzleExtension
31
 */
32
class RawGuzzleContext implements GuzzleAwareContext
33
{
34
    /**
35
     * @var string
36
     */
37
    const GUZZLE_EXTENSION_NAME = 'Behat Guzzle Extension';
38
39
    /**
40
     * @var string
41
     */
42
    const GUZZLE_EXTENSION_VERSION = '0.4.1';
43
44
    /**
45
     * @var Client
46
     *
47
     * @access private
48
     */
49
    private $client;
50
51
    /**
52
     * @var array
53
     *
54
     * @access private
55
     */
56
    private $parameters;
57
58
    /**
59
     * @var Response
60
     *
61
     * @access private
62
     */
63
    private $response;
64
65
    /**
66
     * @var mixed
67
     *
68
     * @access private
69
     */
70
    private $result;
71
72
    /**
73
     * Execute command
74
     *
75
     * @param string $command Command to execute
76
     * @param array  $data    Data to send
77
     *
78
     * @access protected
79
     * @return void
80
     */
81 13
    public function executeCommand($command, array $data = array())
82
    {
83 13
        $this->getGuzzleClient()->setUserAgent(
84 13
            self::GUZZLE_EXTENSION_NAME . '/' . self::GUZZLE_EXTENSION_VERSION
85 13
        );
86
87 13
        $command = $this->getGuzzleClient()->getCommand($command, $data);
88
89
        try {
90 13
            $result = $this->getGuzzleClient()->execute($command);
91 13
        } catch (BadResponseException $e) {
92 1
            $this->response = $e->getResponse();
93
94
            try {
95 1
                $this->result = $e->getResponse()->json();
96 1
            } catch (RuntimeException $e) {
97
                try {
98
                    $this->result = $e->getResponse()->xml();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Guzzle\Common\Exception\RuntimeException as the method getResponse() does only exist in the following sub-classes of Guzzle\Common\Exception\RuntimeException: Guzzle\Http\Exception\BadResponseException, Guzzle\Http\Exception\ClientErrorResponseException, Guzzle\Http\Exception\ServerErrorResponseException, Guzzle\Http\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
99
                } catch (RuntimeException $e) {
100
                    continue;
101
                }
102
            }
103
104 1
            return;
105
        }
106
107 12
        $this->response = $command->getResponse();
108 12
        $this->result = $result;
109 12
    }
110
111
    /**
112
     * Returns Client instance
113
     *
114
     * @access public
115
     * @return Client
116
     */
117 16
    public function getGuzzleClient()
118
    {
119 16
        if ($this->client === null) {
120 1
            throw new \RuntimeException(
121
                'Guzzle client instance has not been set on Guzzle context ' .
122 1
                'class.' . chr(10) . 'Have you enabled the Guzzle Extension?'
123 1
            );
124
        }
125
126 15
        return $this->client;
127
    }
128
129
    /**
130
     * Sets Client instance
131
     *
132
     * @param Client $client Guzzle client
133
     *
134
     * @access public
135
     * @return void
136
     */
137 15
    public function setGuzzleClient(Client $client)
138
    {
139 15
        $this->client = $client;
140 15
    }
141
142
    /**
143
     * Add Guzzle header
144
     *
145
     * @param string $field Field name
146
     * @param string $value Header value
147
     *
148
     * @access public
149
     * @return void
150
     */
151 2
    public function addGuzzleHeader($field, $value)
152
    {
153 2
        $this->updateHeader($field, $value);
154 2
    }
155
156
    /**
157
     * Remove Guzzle header
158
     *
159
     * @param string $field Field name
160
     *
161
     * @access public
162
     * @return void
163
     */
164 1
    public function removeGuzzleHeader($field)
165
    {
166 1
        $this->updateHeader($field);
167 1
    }
168
169
    /**
170
     * Returns specific Guzzle parameter
171
     *
172
     * @param string $name
173
     *
174
     * @access public
175
     * @return mixed
176
     */
177 1
    public function getGuzzleParameter($name)
178
    {
179 1
        if (isset($this->parameters[$name])) {
180 1
            return $this->parameters[$name];
181
        }
182 1
    }
183
184
    /**
185
     * Applies the given parameter to the Guzzle configuration. Consider that
186
     * all parameters get reset for each feature context
187
     *
188
     * @param string $name  The key of the parameter
189
     * @param string $value The value of the parameter
190
     *
191
     * @access public
192
     * @return void
193
     */
194 1
    public function setGuzzleParameter($name, $value)
195
    {
196 1
        $this->parameters[$name] = $value;
197 1
    }
198
199
    /**
200
     * Returns the parameters provided for Guzzle
201
     *
202
     * @access public
203
     * @return array
204
     */
205 1
    public function getGuzzleParameters()
206
    {
207 1
        return $this->parameters;
208
    }
209
210
    /**
211
     * Sets parameters provided for Guzzle
212
     *
213
     * @param array $parameters
214
     *
215
     * @access public
216
     * @return void
217
     */
218 1
    public function setGuzzleParameters(array $parameters)
219
    {
220 1
        $this->parameters = $parameters;
221 1
    }
222
223
    /**
224
     * Returns Response instance
225
     *
226
     * @access public
227
     * @return Response
228
     */
229 5
    public function getGuzzleResponse()
230
    {
231 5
        return $this->response;
232
    }
233
234
    /**
235
     * Returns result
236
     *
237
     * @access public
238
     * @return mixed
239
     */
240 8
    public function getGuzzleResult()
241
    {
242 8
        return $this->result;
243
    }
244
245
    /**
246
     * Compare array values
247
     *
248
     * @param array $input   Array with values to compare
249
     * @param array $control Array with correct values
250
     *
251
     * @access protected
252
     * @return void
253
     */
254 7
    protected function compareArrays(array $input, array $control)
255
    {
256 7
        foreach ($control as $field => $expected) {
257 7
            if (!array_key_exists($field, $input)) {
258 2
                throw new ClientErrorResponseException(
259 2
                    sprintf(
260
                        'Expected value %s is missing from array ' .
261 2
                        'of actual values at position %s',
262 2
                        is_array($expected) ?
263 2
                        json_encode($expected) :
264 2
                        $expected,
265
                        $field
266 2
                    )
267 2
                );
268
            }
269 7
            $this->compareValues($input[$field], $expected);
270 6
        }
271 5
    }
272
273
    /**
274
     * Compare array values
275
     *
276
     * @param mixed $input   Input value
277
     * @param mixed $control Correct control value
278
     *
279
     * @throws Exception When two field values do not match
280
     *
281
     * @access protected
282
     * @return void
283
     */
284 8
    protected function compareValues($input, $control)
285
    {
286 8
        if (is_array($input) && is_array($control)) {
287 7
            $this->compareArrays($input, $control);
288 5
        } else {
289 8
            if ($input != $control) {
290 1
                throw new ClientErrorResponseException(
291 1
                    'Actual value ' . $input . ' does not match expected ' .
292 1
                    'value ' . $control
293 1
                );
294
            }
295
        }
296 7
    }
297
298
    /**
299
     * Get request options
300
     *
301
     * @access private
302
     * @return array
303
     */
304 2
    private function getRequestOptions()
305
    {
306 2
        $options = $this->getGuzzleClient()
307 2
            ->getConfig()
308 2
            ->get(Client::REQUEST_OPTIONS);
309
310 2
        if (!is_array($options)) {
311 2
            $options = array();
312 2
        }
313
314 2
        return $options;
315
    }
316
317
    /**
318
     * Set request options
319
     *
320
     * @param array $options Request options
321
     *
322
     * @access private
323
     * @return void
324
     */
325 2
    private function setRequestOptions(array $options)
326
    {
327 2
        $config = $this->getGuzzleClient()->getConfig();
328 2
        $config->set(Client::REQUEST_OPTIONS, $options);
329
330 2
        $this->getGuzzleClient()->setConfig($config);
331 2
    }
332
333
    /**
334
     * Update header
335
     *
336
     * Adds, updates or removes header (if no value is provided)
337
     *
338
     * @param string $field Field name
339
     * @param mixed  $value Header value
340
     *
341
     * @access private
342
     * @return void
343
     */
344 2
    private function updateHeader($field, $value = null)
345
    {
346 2
        $options = $this->getRequestOptions();
347
348 2
        if (!isset($options['headers'])) {
349 2
            $options['headers'] = array();
350 2
        }
351
352 2
        $options['headers'][$field] = $value;
353
354 2
        $this->setRequestOptions($options);
355 2
    }
356
}
357