Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace Guzzle\Http\Message;
4
 
5
use Guzzle\Common\Collection;
6
use Guzzle\Common\Exception\InvalidArgumentException;
7
use Guzzle\Http\RedirectPlugin;
8
use Guzzle\Http\Url;
9
use Guzzle\Parser\ParserRegistry;
10
 
11
/**
12
 * Default HTTP request factory used to create the default {@see Request} and {@see EntityEnclosingRequest} objects.
13
 */
14
class RequestFactory implements RequestFactoryInterface
15
{
16
    /** @var RequestFactory Singleton instance of the default request factory */
17
    protected static $instance;
18
 
19
    /** @var array Hash of methods available to the class (provides fast isset() lookups) */
20
    protected $methods;
21
 
22
    /** @var string Class to instantiate for requests with no body */
23
    protected $requestClass = 'Guzzle\\Http\\Message\\Request';
24
 
25
    /** @var string Class to instantiate for requests with a body */
26
    protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';
27
 
28
    /**
29
     * Get a cached instance of the default request factory
30
     *
31
     * @return RequestFactory
32
     */
33
    public static function getInstance()
34
    {
35
        // @codeCoverageIgnoreStart
36
        if (!static::$instance) {
37
            static::$instance = new static();
38
        }
39
        // @codeCoverageIgnoreEnd
40
 
41
        return static::$instance;
42
    }
43
 
44
    public function __construct()
45
    {
46
        $this->methods = array_flip(get_class_methods(__CLASS__));
47
    }
48
 
49
    public function fromMessage($message)
50
    {
51
        $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message);
52
 
53
        if (!$parsed) {
54
            return false;
55
        }
56
 
57
        $request = $this->fromParts($parsed['method'], $parsed['request_url'],
58
            $parsed['headers'], $parsed['body'], $parsed['protocol'],
59
            $parsed['version']);
60
 
61
        // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST
62
        // requests. This factory method should accurately reflect the message, so here we are removing the Expect
63
        // header if one was not supplied in the message.
64
        if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) {
65
            $request->removeHeader('Expect');
66
        }
67
 
68
        return $request;
69
    }
70
 
71
    public function fromParts(
72
        $method,
73
        array $urlParts,
74
        $headers = null,
75
        $body = null,
76
        $protocol = 'HTTP',
77
        $protocolVersion = '1.1'
78
    ) {
79
        return $this->create($method, Url::buildUrl($urlParts), $headers, $body)
80
                    ->setProtocolVersion($protocolVersion);
81
    }
82
 
83
    public function create($method, $url, $headers = null, $body = null, array $options = array())
84
    {
85
        $method = strtoupper($method);
86
 
87
        if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') {
88
            // Handle non-entity-enclosing request methods
89
            $request = new $this->requestClass($method, $url, $headers);
90
            if ($body) {
91
                // The body is where the response body will be stored
92
                $type = gettype($body);
93
                if ($type == 'string' || $type == 'resource' || $type == 'object') {
94
                    $request->setResponseBody($body);
95
                }
96
            }
97
        } else {
98
            // Create an entity enclosing request by default
99
            $request = new $this->entityEnclosingRequestClass($method, $url, $headers);
100
            if ($body || $body === '0') {
101
                // Add POST fields and files to an entity enclosing request if an array is used
102
                if (is_array($body) || $body instanceof Collection) {
103
                    // Normalize PHP style cURL uploads with a leading '@' symbol
104
                    foreach ($body as $key => $value) {
105
                        if (is_string($value) && substr($value, 0, 1) == '@') {
106
                            $request->addPostFile($key, $value);
107
                            unset($body[$key]);
108
                        }
109
                    }
110
                    // Add the fields if they are still present and not all files
111
                    $request->addPostFields($body);
112
                } else {
113
                    // Add a raw entity body body to the request
114
                    $request->setBody($body, (string) $request->getHeader('Content-Type'));
115
                    if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') {
116
                        $request->removeHeader('Content-Length');
117
                    }
118
                }
119
            }
120
        }
121
 
122
        if ($options) {
123
            $this->applyOptions($request, $options);
124
        }
125
 
126
        return $request;
127
    }
128
 
129
    /**
130
     * Clone a request while changing the method. Emulates the behavior of
131
     * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method.
132
     *
133
     * @param RequestInterface $request Request to clone
134
     * @param string           $method  Method to set
135
     *
136
     * @return RequestInterface
137
     */
138
    public function cloneRequestWithMethod(RequestInterface $request, $method)
139
    {
140
        // Create the request with the same client if possible
141
        if ($request->getClient()) {
142
            $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders());
143
        } else {
144
            $cloned = $this->create($method, $request->getUrl(), $request->getHeaders());
145
        }
146
 
147
        $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray());
148
        $cloned->setEventDispatcher(clone $request->getEventDispatcher());
149
        // Ensure that that the Content-Length header is not copied if changing to GET or HEAD
150
        if (!($cloned instanceof EntityEnclosingRequestInterface)) {
151
            $cloned->removeHeader('Content-Length');
152
        } elseif ($request instanceof EntityEnclosingRequestInterface) {
153
            $cloned->setBody($request->getBody());
154
        }
155
        $cloned->getParams()->replace($request->getParams()->toArray());
156
        $cloned->dispatch('request.clone', array('request' => $cloned));
157
 
158
        return $cloned;
159
    }
160
 
161
    public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE)
162
    {
163
        // Iterate over each key value pair and attempt to apply a config using function visitors
164
        foreach ($options as $key => $value) {
165
            $method = "visit_{$key}";
166
            if (isset($this->methods[$method])) {
167
                $this->{$method}($request, $value, $flags);
168
            }
169
        }
170
    }
171
 
172
    protected function visit_headers(RequestInterface $request, $value, $flags)
173
    {
174
        if (!is_array($value)) {
175
            throw new InvalidArgumentException('headers value must be an array');
176
        }
177
 
178
        if ($flags & self::OPTIONS_AS_DEFAULTS) {
179
            // Merge headers in but do not overwrite existing values
180
            foreach ($value as $key => $header) {
181
                if (!$request->hasHeader($key)) {
182
                    $request->setHeader($key, $header);
183
                }
184
            }
185
        } else {
186
            $request->addHeaders($value);
187
        }
188
    }
189
 
190
    protected function visit_body(RequestInterface $request, $value, $flags)
191
    {
192
        if ($request instanceof EntityEnclosingRequestInterface) {
193
            $request->setBody($value);
194
        } else {
195
            throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request');
196
        }
197
    }
198
 
199
    protected function visit_allow_redirects(RequestInterface $request, $value, $flags)
200
    {
201
        if ($value === false) {
202
            $request->getParams()->set(RedirectPlugin::DISABLE, true);
203
        }
204
    }
205
 
206
    protected function visit_auth(RequestInterface $request, $value, $flags)
207
    {
208
        if (!is_array($value)) {
209
            throw new InvalidArgumentException('auth value must be an array');
210
        }
211
 
212
        $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic');
213
    }
214
 
215
    protected function visit_query(RequestInterface $request, $value, $flags)
216
    {
217
        if (!is_array($value)) {
218
            throw new InvalidArgumentException('query value must be an array');
219
        }
220
 
221
        if ($flags & self::OPTIONS_AS_DEFAULTS) {
222
            // Merge query string values in but do not overwrite existing values
223
            $query = $request->getQuery();
224
            $query->overwriteWith(array_diff_key($value, $query->toArray()));
225
        } else {
226
            $request->getQuery()->overwriteWith($value);
227
        }
228
    }
229
 
230
    protected function visit_cookies(RequestInterface $request, $value, $flags)
231
    {
232
        if (!is_array($value)) {
233
            throw new InvalidArgumentException('cookies value must be an array');
234
        }
235
 
236
        foreach ($value as $name => $v) {
237
            $request->addCookie($name, $v);
238
        }
239
    }
240
 
241
    protected function visit_events(RequestInterface $request, $value, $flags)
242
    {
243
        if (!is_array($value)) {
244
            throw new InvalidArgumentException('events value must be an array');
245
        }
246
 
247
        foreach ($value as $name => $method) {
248
            if (is_array($method)) {
249
                $request->getEventDispatcher()->addListener($name, $method[0], $method[1]);
250
            } else {
251
                $request->getEventDispatcher()->addListener($name, $method);
252
            }
253
        }
254
    }
255
 
256
    protected function visit_plugins(RequestInterface $request, $value, $flags)
257
    {
258
        if (!is_array($value)) {
259
            throw new InvalidArgumentException('plugins value must be an array');
260
        }
261
 
262
        foreach ($value as $plugin) {
263
            $request->addSubscriber($plugin);
264
        }
265
    }
266
 
267
    protected function visit_exceptions(RequestInterface $request, $value, $flags)
268
    {
269
        if ($value === false || $value === 0) {
270
            $dispatcher = $request->getEventDispatcher();
271
            foreach ($dispatcher->getListeners('request.error') as $listener) {
272
                if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') {
273
                    $dispatcher->removeListener('request.error', $listener);
274
                    break;
275
                }
276
            }
277
        }
278
    }
279
 
280
    protected function visit_save_to(RequestInterface $request, $value, $flags)
281
    {
282
        $request->setResponseBody($value);
283
    }
284
 
285
    protected function visit_params(RequestInterface $request, $value, $flags)
286
    {
287
        if (!is_array($value)) {
288
            throw new InvalidArgumentException('params value must be an array');
289
        }
290
 
291
        $request->getParams()->overwriteWith($value);
292
    }
293
 
294
    protected function visit_timeout(RequestInterface $request, $value, $flags)
295
    {
296
        if (defined('CURLOPT_TIMEOUT_MS')) {
297
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000);
298
        } else {
299
            $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value);
300
        }
301
    }
302
 
303
    protected function visit_connect_timeout(RequestInterface $request, $value, $flags)
304
    {
305
        if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
306
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000);
307
        } else {
308
            $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value);
309
        }
310
    }
311
 
312
    protected function visit_debug(RequestInterface $request, $value, $flags)
313
    {
314
        if ($value) {
315
            $request->getCurlOptions()->set(CURLOPT_VERBOSE, true);
316
        }
317
    }
318
 
319
    protected function visit_verify(RequestInterface $request, $value, $flags)
320
    {
321
        $curl = $request->getCurlOptions();
322
        if ($value === true || is_string($value)) {
323
            $curl[CURLOPT_SSL_VERIFYHOST] = 2;
324
            $curl[CURLOPT_SSL_VERIFYPEER] = true;
325
            if ($value !== true) {
326
                $curl[CURLOPT_CAINFO] = $value;
327
            }
328
        } elseif ($value === false) {
329
            unset($curl[CURLOPT_CAINFO]);
330
            $curl[CURLOPT_SSL_VERIFYHOST] = 0;
331
            $curl[CURLOPT_SSL_VERIFYPEER] = false;
332
        }
333
    }
334
 
335
    protected function visit_proxy(RequestInterface $request, $value, $flags)
336
    {
337
        $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags);
338
    }
339
 
340
    protected function visit_cert(RequestInterface $request, $value, $flags)
341
    {
342
        if (is_array($value)) {
343
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]);
344
            $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]);
345
        } else {
346
            $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value);
347
        }
348
    }
349
 
350
    protected function visit_ssl_key(RequestInterface $request, $value, $flags)
351
    {
352
        if (is_array($value)) {
353
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]);
354
            $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]);
355
        } else {
356
            $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value);
357
        }
358
    }
359
}