Test Failed
Push — master ( d99c6b...fb4ca3 )
by Stiofan
15:44
created

Google_Http_Request   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 475
Duplicated Lines 3.16 %

Coupling/Cohesion

Components 4
Dependencies 1

Importance

Changes 0
Metric Value
dl 15
loc 475
rs 3.0612
c 0
b 0
f 0
wmc 67
lcom 4
cbo 1

37 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A getBaseComponent() 0 4 1
A setBaseComponent() 0 4 1
A enableGzip() 0 6 1
A disableGzip() 0 11 3
A canGzip() 0 4 1
A getQueryParams() 0 4 1
A setQueryParam() 0 4 1
A getResponseHttpCode() 0 4 1
A setResponseHttpCode() 0 4 1
A getResponseHeaders() 0 4 1
A getResponseBody() 0 4 1
A setExpectedClass() 0 4 1
A getExpectedClass() 0 4 1
A enableExpectedRaw() 0 4 1
A disableExpectedRaw() 0 4 1
A getExpectedRaw() 0 4 1
A setResponseHeaders() 8 9 2
A getResponseHeader() 0 6 2
A setResponseBody() 0 4 1
A getUrl() 0 7 2
A getRequestMethod() 0 4 1
A getRequestHeaders() 0 4 1
A getRequestHeader() 0 6 2
A getPostBody() 0 4 1
C setUrl() 0 23 9
A setRequestMethod() 0 4 1
A setRequestHeaders() 7 8 2
A setPostBody() 0 4 1
A setUserAgent() 0 7 2
A getUserAgent() 0 4 1
A getCacheKey() 0 14 3
A getParsedCacheControl() 0 11 2
B toBatchString() 0 26 4
A parseQuery() 0 18 4
A buildQuery() 0 14 4
A maybeMoveParametersToBody() 0 13 3

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 Google_Http_Request 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 Google_Http_Request, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * Copyright 2010 Google Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
if (!class_exists('Google_Client')) {
19
  require_once dirname(__FILE__) . '/../autoload.php';
20
}
21
22
/**
23
 * HTTP Request to be executed by IO classes. Upon execution, the
24
 * responseHttpCode, responseHeaders and responseBody will be filled in.
25
 *
26
 * @author Chris Chabot <[email protected]>
27
 * @author Chirag Shah <[email protected]>
28
 *
29
 */
30
class Google_Http_Request
31
{
32
  const GZIP_UA = " (gzip)";
33
34
  private $batchHeaders = array(
35
    'Content-Type' => 'application/http',
36
    'Content-Transfer-Encoding' => 'binary',
37
    'MIME-Version' => '1.0',
38
  );
39
40
  protected $queryParams;
41
  protected $requestMethod;
42
  protected $requestHeaders;
43
  protected $baseComponent = null;
44
  protected $path;
45
  protected $postBody;
46
  protected $userAgent;
47
  protected $canGzip = null;
48
49
  protected $responseHttpCode;
50
  protected $responseHeaders;
51
  protected $responseBody;
52
  
53
  protected $expectedClass;
54
  protected $expectedRaw = false;
55
56
  public $accessKey;
57
58
  public function __construct(
59
      $url,
60
      $method = 'GET',
61
      $headers = array(),
62
      $postBody = null
63
  ) {
64
    $this->setUrl($url);
65
    $this->setRequestMethod($method);
66
    $this->setRequestHeaders($headers);
67
    $this->setPostBody($postBody);
68
  }
69
70
  /**
71
   * Misc function that returns the base url component of the $url
72
   * used by the OAuth signing class to calculate the base string
73
   * @return string The base url component of the $url.
74
   */
75
  public function getBaseComponent()
76
  {
77
    return $this->baseComponent;
78
  }
79
  
80
  /**
81
   * Set the base URL that path and query parameters will be added to.
82
   * @param $baseComponent string
83
   */
84
  public function setBaseComponent($baseComponent)
85
  {
86
    $this->baseComponent = rtrim($baseComponent, '/');
87
  }
88
  
89
  /**
90
   * Enable support for gzipped responses with this request.
91
   */
92
  public function enableGzip()
93
  {
94
    $this->setRequestHeaders(array("Accept-Encoding" => "gzip"));
95
    $this->canGzip = true;
96
    $this->setUserAgent($this->userAgent);
97
  }
98
  
99
  /**
100
   * Disable support for gzip responses with this request.
101
   */
102
  public function disableGzip()
103
  {
104
    if (
105
        isset($this->requestHeaders['accept-encoding']) &&
106
        $this->requestHeaders['accept-encoding'] == "gzip"
107
    ) {
108
      unset($this->requestHeaders['accept-encoding']);
109
    }
110
    $this->canGzip = false;
111
    $this->userAgent = str_replace(self::GZIP_UA, "", $this->userAgent);
112
  }
113
  
114
  /**
115
   * Can this request accept a gzip response?
116
   * @return bool
117
   */
118
  public function canGzip()
119
  {
120
    return $this->canGzip;
121
  }
122
123
  /**
124
   * Misc function that returns an array of the query parameters of the current
125
   * url used by the OAuth signing class to calculate the signature
126
   * @return array Query parameters in the query string.
127
   */
128
  public function getQueryParams()
129
  {
130
    return $this->queryParams;
131
  }
132
133
  /**
134
   * Set a new query parameter.
135
   * @param $key - string to set, does not need to be URL encoded
136
   * @param $value - string to set, does not need to be URL encoded
137
   */
138
  public function setQueryParam($key, $value)
139
  {
140
    $this->queryParams[$key] = $value;
141
  }
142
143
  /**
144
   * @return string HTTP Response Code.
145
   */
146
  public function getResponseHttpCode()
147
  {
148
    return (int) $this->responseHttpCode;
149
  }
150
151
  /**
152
   * @param int $responseHttpCode HTTP Response Code.
153
   */
154
  public function setResponseHttpCode($responseHttpCode)
155
  {
156
    $this->responseHttpCode = $responseHttpCode;
157
  }
158
159
  /**
160
   * @return $responseHeaders (array) HTTP Response Headers.
0 ignored issues
show
Documentation introduced by
The doc-type $responseHeaders could not be parsed: Unknown type name "$responseHeaders" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
161
   */
162
  public function getResponseHeaders()
163
  {
164
    return $this->responseHeaders;
165
  }
166
167
  /**
168
   * @return string HTTP Response Body
169
   */
170
  public function getResponseBody()
171
  {
172
    return $this->responseBody;
173
  }
174
  
175
  /**
176
   * Set the class the response to this request should expect.
177
   *
178
   * @param $class string the class name
179
   */
180
  public function setExpectedClass($class)
181
  {
182
    $this->expectedClass = $class;
183
  }
184
  
185
  /**
186
   * Retrieve the expected class the response should expect.
187
   * @return string class name
188
   */
189
  public function getExpectedClass()
190
  {
191
    return $this->expectedClass;
192
  }
193
194
  /**
195
   * Enable expected raw response
196
   */
197
  public function enableExpectedRaw()
198
  {
199
    $this->expectedRaw = true;
200
  }
201
202
  /**
203
   * Disable expected raw response
204
   */
205
  public function disableExpectedRaw()
206
  {
207
    $this->expectedRaw = false;
208
  }
209
210
  /**
211
   * Expected raw response or not.
212
   * @return boolean expected raw response
213
   */
214
  public function getExpectedRaw()
215
  {
216
    return $this->expectedRaw;
217
  }
218
219
  /**
220
   * @param array $headers The HTTP response headers
221
   * to be normalized.
222
   */
223 View Code Duplication
  public function setResponseHeaders($headers)
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...
224
  {
225
    $headers = Google_Utils::normalize($headers);
226
    if ($this->responseHeaders) {
227
      $headers = array_merge($this->responseHeaders, $headers);
228
    }
229
230
    $this->responseHeaders = $headers;
231
  }
232
233
  /**
234
   * @param string $key
235
   * @return array|boolean Returns the requested HTTP header or
236
   * false if unavailable.
237
   */
238
  public function getResponseHeader($key)
239
  {
240
    return isset($this->responseHeaders[$key])
241
        ? $this->responseHeaders[$key]
242
        : false;
243
  }
244
245
  /**
246
   * @param string $responseBody The HTTP response body.
247
   */
248
  public function setResponseBody($responseBody)
249
  {
250
    $this->responseBody = $responseBody;
251
  }
252
253
  /**
254
   * @return string $url The request URL.
255
   */
256
  public function getUrl()
257
  {
258
    return $this->baseComponent . $this->path .
259
        (count($this->queryParams) ?
260
            "?" . $this->buildQuery($this->queryParams) :
261
            '');
262
  }
263
264
  /**
265
   * @return string $method HTTP Request Method.
266
   */
267
  public function getRequestMethod()
268
  {
269
    return $this->requestMethod;
270
  }
271
272
  /**
273
   * @return array $headers HTTP Request Headers.
274
   */
275
  public function getRequestHeaders()
276
  {
277
    return $this->requestHeaders;
278
  }
279
280
  /**
281
   * @param string $key
282
   * @return array|boolean Returns the requested HTTP header or
283
   * false if unavailable.
284
   */
285
  public function getRequestHeader($key)
286
  {
287
    return isset($this->requestHeaders[$key])
288
        ? $this->requestHeaders[$key]
289
        : false;
290
  }
291
292
  /**
293
   * @return string $postBody HTTP Request Body.
294
   */
295
  public function getPostBody()
296
  {
297
    return $this->postBody;
298
  }
299
300
  /**
301
   * @param string $url the url to set
302
   */
303
  public function setUrl($url)
304
  {
305
    if (substr($url, 0, 4) != 'http') {
306
      // Force the path become relative.
307
      if (substr($url, 0, 1) !== '/') {
308
        $url = '/' . $url;
309
      }
310
    }
311
    $parts = parse_url($url);
312
    if (isset($parts['host'])) {
313
      $this->baseComponent = sprintf(
314
          "%s%s%s",
315
          isset($parts['scheme']) ? $parts['scheme'] . "://" : '',
316
          isset($parts['host']) ? $parts['host'] : '',
317
          isset($parts['port']) ? ":" . $parts['port'] : ''
318
      );
319
    }
320
    $this->path = isset($parts['path']) ? $parts['path'] : '';
321
    $this->queryParams = array();
322
    if (isset($parts['query'])) {
323
      $this->queryParams = $this->parseQuery($parts['query']);
324
    }
325
  }
326
327
  /**
328
   * @param string $method Set he HTTP Method and normalize
329
   * it to upper-case, as required by HTTP.
330
   *
331
   */
332
  public function setRequestMethod($method)
333
  {
334
    $this->requestMethod = strtoupper($method);
335
  }
336
337
  /**
338
   * @param array $headers The HTTP request headers
339
   * to be set and normalized.
340
   */
341 View Code Duplication
  public function setRequestHeaders($headers)
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...
342
  {
343
    $headers = Google_Utils::normalize($headers);
344
    if ($this->requestHeaders) {
345
      $headers = array_merge($this->requestHeaders, $headers);
346
    }
347
    $this->requestHeaders = $headers;
348
  }
349
350
  /**
351
   * @param string $postBody the postBody to set
352
   */
353
  public function setPostBody($postBody)
354
  {
355
    $this->postBody = $postBody;
356
  }
357
358
  /**
359
   * Set the User-Agent Header.
360
   * @param string $userAgent The User-Agent.
361
   */
362
  public function setUserAgent($userAgent)
363
  {
364
    $this->userAgent = $userAgent;
365
    if ($this->canGzip) {
366
      $this->userAgent = $userAgent . self::GZIP_UA;
367
    }
368
  }
369
370
  /**
371
   * @return string The User-Agent.
372
   */
373
  public function getUserAgent()
374
  {
375
    return $this->userAgent;
376
  }
377
378
  /**
379
   * Returns a cache key depending on if this was an OAuth signed request
380
   * in which case it will use the non-signed url and access key to make this
381
   * cache key unique per authenticated user, else use the plain request url
382
   * @return string The md5 hash of the request cache key.
383
   */
384
  public function getCacheKey()
385
  {
386
    $key = $this->getUrl();
387
388
    if (isset($this->accessKey)) {
389
      $key .= $this->accessKey;
390
    }
391
392
    if (isset($this->requestHeaders['authorization'])) {
393
      $key .= $this->requestHeaders['authorization'];
394
    }
395
396
    return md5($key);
397
  }
398
399
  public function getParsedCacheControl()
400
  {
401
    $parsed = array();
402
    $rawCacheControl = $this->getResponseHeader('cache-control');
403
    if ($rawCacheControl) {
404
      $rawCacheControl = str_replace(', ', '&', $rawCacheControl);
405
      parse_str($rawCacheControl, $parsed);
406
    }
407
408
    return $parsed;
409
  }
410
411
  /**
412
   * @param string $id
413
   * @return string A string representation of the HTTP Request.
414
   */
415
  public function toBatchString($id)
416
  {
417
    $str = '';
418
    $path = parse_url($this->getUrl(), PHP_URL_PATH) . "?" .
419
        http_build_query($this->queryParams);
420
    $str .= $this->getRequestMethod() . ' ' . $path . " HTTP/1.1\n";
421
422
    foreach ($this->getRequestHeaders() as $key => $val) {
423
      $str .= $key . ': ' . $val . "\n";
424
    }
425
426
    if ($this->getPostBody()) {
427
      $str .= "\n";
428
      $str .= $this->getPostBody();
429
    }
430
    
431
    $headers = '';
432
    foreach ($this->batchHeaders as $key => $val) {
433
      $headers .= $key . ': ' . $val . "\n";
434
    }
435
436
    $headers .= "Content-ID: $id\n";
437
    $str = $headers . "\n" . $str;
438
439
    return $str;
440
  }
441
  
442
  /**
443
   * Our own version of parse_str that allows for multiple variables
444
   * with the same name.
445
   * @param $string - the query string to parse
446
   */
447
  private function parseQuery($string)
448
  {
449
    $return = array();
450
    $parts = explode("&", $string);
451
    foreach ($parts as $part) {
452
      list($key, $value) = explode('=', $part, 2);
453
      $value = urldecode($value);
454
      if (isset($return[$key])) {
455
        if (!is_array($return[$key])) {
456
          $return[$key] = array($return[$key]);
457
        }
458
        $return[$key][] = $value;
459
      } else {
460
        $return[$key] = $value;
461
      }
462
    }
463
    return $return;
464
  }
465
  
466
  /**
467
   * A version of build query that allows for multiple
468
   * duplicate keys.
469
   * @param $parts array of key value pairs
470
   */
471
  private function buildQuery($parts)
472
  {
473
    $return = array();
474
    foreach ($parts as $key => $value) {
475
      if (is_array($value)) {
476
        foreach ($value as $v) {
477
          $return[] = urlencode($key) . "=" . urlencode($v);
478
        }
479
      } else {
480
        $return[] = urlencode($key) . "=" . urlencode($value);
481
      }
482
    }
483
    return implode('&', $return);
484
  }
485
  
486
  /**
487
   * If we're POSTing and have no body to send, we can send the query
488
   * parameters in there, which avoids length issues with longer query
489
   * params.
490
   */
491
  public function maybeMoveParametersToBody()
492
  {
493
    if ($this->getRequestMethod() == "POST" && empty($this->postBody)) {
494
      $this->setRequestHeaders(
495
          array(
496
            "content-type" =>
497
                "application/x-www-form-urlencoded; charset=UTF-8"
498
          )
499
      );
500
      $this->setPostBody($this->buildQuery($this->queryParams));
501
      $this->queryParams = array();
502
    }
503
  }
504
}
505