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

Google_IO_Stream::executeRequest()   F

Complexity

Conditions 13
Paths 448

Size

Total Lines 116
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 69
nc 448
nop 1
dl 0
loc 116
rs 3.5085
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * Copyright 2013 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
/**
19
 * Http Streams based implementation of Google_IO.
20
 *
21
 * @author Stuart Langley <[email protected]>
22
 */
23
24
if (!class_exists('Google_Client')) {
25
  require_once dirname(__FILE__) . '/../autoload.php';
26
}
27
28
class Google_IO_Stream extends Google_IO_Abstract
29
{
30
  const TIMEOUT = "timeout";
31
  const ZLIB = "compress.zlib://";
32
  private $options = array();
33
  private $trappedErrorNumber;
34
  private $trappedErrorString;
35
36
  private static $DEFAULT_HTTP_CONTEXT = array(
37
    "follow_location" => 0,
38
    "ignore_errors" => 1,
39
  );
40
41
  private static $DEFAULT_SSL_CONTEXT = array(
42
    "verify_peer" => true,
43
  );
44
45 View Code Duplication
  public function __construct(Google_Client $client)
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...
46
  {
47
    if (!ini_get('allow_url_fopen')) {
48
      $error = 'The stream IO handler requires the allow_url_fopen runtime ' .
49
               'configuration to be enabled';
50
      $client->getLogger()->critical($error);
51
      throw new Google_IO_Exception($error);
52
    }
53
54
    parent::__construct($client);
55
  }
56
57
  /**
58
   * Execute an HTTP Request
59
   *
60
   * @param Google_Http_Request $request the http request to be executed
61
   * @return array containing response headers, body, and http code
62
   * @throws Google_IO_Exception on curl or IO error
63
   */
64
  public function executeRequest(Google_Http_Request $request)
65
  {
66
    $default_options = stream_context_get_options(stream_context_get_default());
67
68
    $requestHttpContext = array_key_exists('http', $default_options) ?
69
        $default_options['http'] : array();
70
71
    if ($request->getPostBody()) {
72
      $requestHttpContext["content"] = $request->getPostBody();
73
    }
74
75
    $requestHeaders = $request->getRequestHeaders();
76
    if ($requestHeaders && is_array($requestHeaders)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $requestHeaders of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
77
      $headers = "";
78
      foreach ($requestHeaders as $k => $v) {
79
        $headers .= "$k: $v\r\n";
80
      }
81
      $requestHttpContext["header"] = $headers;
82
    }
83
84
    $requestHttpContext["method"] = $request->getRequestMethod();
85
    $requestHttpContext["user_agent"] = $request->getUserAgent();
86
87
    $requestSslContext = array_key_exists('ssl', $default_options) ?
88
        $default_options['ssl'] : array();
89
90
    if (!array_key_exists("cafile", $requestSslContext)) {
91
      $requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem';
92
    }
93
94
    $options = array(
95
        "http" => array_merge(
96
            self::$DEFAULT_HTTP_CONTEXT,
97
            $requestHttpContext
98
        ),
99
        "ssl" => array_merge(
100
            self::$DEFAULT_SSL_CONTEXT,
101
            $requestSslContext
102
        )
103
    );
104
105
    $context = stream_context_create($options);
106
107
    $url = $request->getUrl();
108
109
    if ($request->canGzip()) {
110
      $url = self::ZLIB . $url;
111
    }
112
113
    $this->client->getLogger()->debug(
114
        'Stream request',
115
        array(
116
            'url' => $url,
117
            'method' => $request->getRequestMethod(),
118
            'headers' => $requestHeaders,
119
            'body' => $request->getPostBody()
120
        )
121
    );
122
123
    // We are trapping any thrown errors in this method only and
124
    // throwing an exception.
125
    $this->trappedErrorNumber = null;
126
    $this->trappedErrorString = null;
127
128
    // START - error trap.
129
    set_error_handler(array($this, 'trapError'));
130
    $fh = fopen($url, 'r', false, $context);
131
    restore_error_handler();
132
    // END - error trap.
133
134
    if ($this->trappedErrorNumber) {
135
      $error = sprintf(
136
          "HTTP Error: Unable to connect: '%s'",
137
          $this->trappedErrorString
138
      );
139
140
      $this->client->getLogger()->error('Stream ' . $error);
141
      throw new Google_IO_Exception($error, $this->trappedErrorNumber);
142
    }
143
144
    $response_data = false;
145
    $respHttpCode = self::UNKNOWN_CODE;
146
    if ($fh) {
147
      if (isset($this->options[self::TIMEOUT])) {
148
        stream_set_timeout($fh, $this->options[self::TIMEOUT]);
149
      }
150
151
      $response_data = stream_get_contents($fh);
152
      fclose($fh);
153
154
      $respHttpCode = $this->getHttpResponseCode($http_response_header);
155
    }
156
157
    if (false === $response_data) {
158
      $error = sprintf(
159
          "HTTP Error: Unable to connect: '%s'",
160
          $respHttpCode
161
      );
162
163
      $this->client->getLogger()->error('Stream ' . $error);
164
      throw new Google_IO_Exception($error, $respHttpCode);
165
    }
166
167
    $responseHeaders = $this->getHttpResponseHeaders($http_response_header);
168
169
    $this->client->getLogger()->debug(
170
        'Stream response',
171
        array(
172
            'code' => $respHttpCode,
173
            'headers' => $responseHeaders,
174
            'body' => $response_data,
175
        )
176
    );
177
178
    return array($response_data, $responseHeaders, $respHttpCode);
179
  }
180
181
  /**
182
   * Set options that update the transport implementation's behavior.
183
   * @param $options
184
   */
185
  public function setOptions($options)
186
  {
187
    $this->options = $options + $this->options;
188
  }
189
190
  /**
191
   * Method to handle errors, used for error handling around
192
   * stream connection methods.
193
   */
194
  public function trapError($errno, $errstr)
195
  {
196
    $this->trappedErrorNumber = $errno;
197
    $this->trappedErrorString = $errstr;
198
  }
199
200
  /**
201
   * Set the maximum request time in seconds.
202
   * @param $timeout in seconds
203
   */
204
  public function setTimeout($timeout)
205
  {
206
    $this->options[self::TIMEOUT] = $timeout;
207
  }
208
209
  /**
210
   * Get the maximum request time in seconds.
211
   * @return timeout in seconds
212
   */
213
  public function getTimeout()
214
  {
215
    return $this->options[self::TIMEOUT];
216
  }
217
218
  /**
219
   * Test for the presence of a cURL header processing bug
220
   *
221
   * {@inheritDoc}
222
   *
223
   * @return boolean
224
   */
225
  protected function needsQuirk()
226
  {
227
    return false;
228
  }
229
230
  protected function getHttpResponseCode($response_headers)
231
  {
232
    $header_count = count($response_headers);
233
234
    for ($i = 0; $i < $header_count; $i++) {
235
      $header = $response_headers[$i];
236
      if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) {
237
        $response = explode(' ', $header);
238
        return $response[1];
239
      }
240
    }
241
    return self::UNKNOWN_CODE;
242
  }
243
}
244