Completed
Push — master ( 85e358...cf3219 )
by Rakesh
04:24 queued 10s
created

Zend_Http_Client::_prepareHeaders()   F

Complexity

Conditions 21
Paths 1944

Size

Total Lines 73
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 21
eloc 34
c 1
b 0
f 1
nc 1944
nop 0
dl 0
loc 73
rs 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
/**
4
 * Zend Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file LICENSE.txt.
10
 * It is also available through the world-wide-web at this URL:
11
 * http://framework.zend.com/license/new-bsd
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @category   Zend
17
 * @package    Zend_Http
18
 * @subpackage Client
19
 * @version    $Id$
20
 * @copyright  Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
21
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
22
 */
23
24
/**
25
 * @see Zend_Loader
26
 */
27
require_once 'Zend/Loader.php';
28
29
30
/**
31
 * @see Zend_Uri
32
 */
33
require_once 'Zend/Uri.php';
34
35
36
/**
37
 * @see Zend_Http_Client_Adapter_Interface
38
 */
39
require_once 'Zend/Http/Client/Adapter/Interface.php';
40
41
42
/**
43
 * @see Zend_Http_Response
44
 */
45
require_once 'Zend/Http/Response.php';
46
47
/**
48
 * @see Zend_Http_Response_Stream
49
 */
50
require_once 'Zend/Http/Response/Stream.php';
51
52
/**
53
 * Zend_Http_Client is an implementation of an HTTP client in PHP. The client
54
 * supports basic features like sending different HTTP requests and handling
55
 * redirections, as well as more advanced features like proxy settings, HTTP
56
 * authentication and cookie persistence (using a Zend_Http_CookieJar object)
57
 *
58
 * @todo Implement proxy settings
59
 * @category   Zend
60
 * @package    Zend_Http
61
 * @subpackage Client
62
 * @throws     Zend_Http_Client_Exception
63
 * @copyright  Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
64
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
65
 */
66
class Zend_Http_Client
67
{
68
    /**
69
     * HTTP request methods
70
     */
71
    const GET     = 'GET';
72
    const POST    = 'POST';
73
    const PUT     = 'PUT';
74
    const HEAD    = 'HEAD';
75
    const DELETE  = 'DELETE';
76
    const TRACE   = 'TRACE';
77
    const OPTIONS = 'OPTIONS';
78
    const CONNECT = 'CONNECT';
79
    const MERGE   = 'MERGE';
80
    const PATCH   = 'PATCH';
81
82
    /**
83
     * Supported HTTP Authentication methods
84
     */
85
    const AUTH_BASIC = 'basic';
86
    //const AUTH_DIGEST = 'digest'; <-- not implemented yet
87
88
    /**
89
     * HTTP protocol versions
90
     */
91
    const HTTP_1 = '1.1';
92
    const HTTP_0 = '1.0';
93
94
    /**
95
     * Content attributes
96
     */
97
    const CONTENT_TYPE   = 'Content-Type';
98
    const CONTENT_LENGTH = 'Content-Length';
99
100
    /**
101
     * POST data encoding methods
102
     */
103
    const ENC_URLENCODED = 'application/x-www-form-urlencoded';
104
    const ENC_FORMDATA   = 'multipart/form-data';
105
106
    /**
107
     * Value types for Body key/value pairs
108
     */
109
    const VTYPE_SCALAR  = 'SCALAR';
110
    const VTYPE_FILE    = 'FILE';
111
112
    /**
113
     * Configuration array, set using the constructor or using ::setConfig()
114
     *
115
     * @var array
116
     */
117
    protected $config = array(
118
        'maxredirects'    => 5,
119
        'strictredirects' => false,
120
        'useragent'       => 'Zend_Http_Client',
121
        'timeout'         => 10,
122
        'adapter'         => 'Zend_Http_Client_Adapter_Socket',
123
        'httpversion'     => self::HTTP_1,
124
        'keepalive'       => false,
125
        'storeresponse'   => true,
126
        'strict'          => true,
127
        'output_stream'   => false,
128
        'encodecookies'   => true,
129
        'rfc3986_strict'  => false
130
    );
131
132
    /**
133
     * The adapter used to perform the actual connection to the server
134
     *
135
     * @var Zend_Http_Client_Adapter_Interface
136
     */
137
    protected $adapter = null;
138
139
    /**
140
     * Request URI
141
     *
142
     * @var Zend_Uri_Http
143
     */
144
    protected $uri = null;
145
146
    /**
147
     * Associative array of request headers
148
     *
149
     * @var array
150
     */
151
    protected $headers = array();
152
153
    /**
154
     * HTTP request method
155
     *
156
     * @var string
157
     */
158
    protected $method = self::GET;
159
160
    /**
161
     * Associative array of GET parameters
162
     *
163
     * @var array
164
     */
165
    protected $paramsGet = array();
166
167
    /**
168
     * Associative array of POST parameters
169
     *
170
     * @var array
171
     */
172
    protected $paramsPost = array();
173
174
    /**
175
     * Request body content type (for POST requests)
176
     *
177
     * @var string
178
     */
179
    protected $enctype = null;
180
181
    /**
182
     * The raw post data to send. Could be set by setRawData($data, $enctype).
183
     *
184
     * @var string
185
     */
186
    protected $raw_post_data = null;
187
188
    /**
189
     * HTTP Authentication settings
190
     *
191
     * Expected to be an associative array with this structure:
192
     * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
193
     * Where 'type' should be one of the supported authentication types (see the AUTH_*
194
     * constants), for example 'basic' or 'digest'.
195
     *
196
     * If null, no authentication will be used.
197
     *
198
     * @var array|null
199
     */
200
    protected $auth;
201
202
    /**
203
     * File upload arrays (used in POST requests)
204
     *
205
     * An associative array, where each element is of the format:
206
     *   'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
207
     *
208
     * @var array
209
     */
210
    protected $files = array();
211
212
    /**
213
     * Ordered list of keys from key/value pair data to include in body
214
     *
215
     * An associative array, where each element is of the format:
216
     *   '<field name>' => VTYPE_SCALAR | VTYPE_FILE
217
     *
218
     * @var array
219
     */
220
    protected $body_field_order = array();
221
222
    /**
223
     * The client's cookie jar
224
     *
225
     * @var Zend_Http_CookieJar
0 ignored issues
show
Bug introduced by
The type Zend_Http_CookieJar was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
226
     */
227
    protected $cookiejar = null;
228
229
    /**
230
     * The last HTTP request sent by the client, as string
231
     *
232
     * @var string
233
     */
234
    protected $last_request = null;
235
236
    /**
237
     * The last HTTP response received by the client
238
     *
239
     * @var Zend_Http_Response
240
     */
241
    protected $last_response = null;
242
243
    /**
244
     * Redirection counter
245
     *
246
     * @var int
247
     */
248
    protected $redirectCounter = 0;
249
250
    /**
251
     * Status for unmasking GET array params
252
     *
253
     * @var boolean
254
     */
255
    protected $_unmaskStatus = false;
256
257
    /**
258
     * Status if the http_build_query function escapes brackets
259
     *
260
     * @var boolean
261
     */
262
    protected $_queryBracketsEscaped = true;
263
264
    /**
265
     * Fileinfo magic database resource
266
     *
267
     * This variable is populated the first time _detectFileMimeType is called
268
     * and is then reused on every call to this method
269
     *
270
     * @var resource
271
     */
272
    protected static $_fileInfoDb = null;
273
274
    /**
275
     * Constructor method. Will create a new HTTP client. Accepts the target
276
     * URL and optionally configuration array.
277
     *
278
     * @param Zend_Uri_Http|string $uri
279
     * @param array $config Configuration key-value pairs.
280
     */
281
    public function __construct($uri = null, $config = null)
282
    {
283
        if ($uri !== null) {
284
            $this->setUri($uri);
285
        }
286
        if ($config !== null) {
287
            $this->setConfig($config);
288
        }
289
290
        $this->_queryBracketsEscaped = version_compare(phpversion(), '5.1.3', '>=');
291
    }
292
293
    /**
294
     * Set the URI for the next request
295
     *
296
     * @param  Zend_Uri_Http|string $uri
297
     * @return Zend_Http_Client
298
     * @throws Zend_Http_Client_Exception
299
     */
300
    public function setUri($uri)
301
    {
302
        if ($uri instanceof Zend_Uri_Http) {
303
            // clone the URI in order to keep the passed parameter constant
304
            $uri = clone $uri;
305
        } elseif (is_string($uri)) {
0 ignored issues
show
introduced by
The condition is_string($uri) is always true.
Loading history...
306
            $uri = Zend_Uri::factory($uri);
307
        }
308
309
        if (!$uri instanceof Zend_Uri_Http) {
0 ignored issues
show
introduced by
$uri is always a sub-type of Zend_Uri_Http.
Loading history...
310
            /** @see Zend_Http_Client_Exception */
311
            require_once 'Zend/Http/Client/Exception.php';
312
            throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
313
        }
314
315
        // Set auth if username and password has been specified in the uri
316
        if ($uri->getUsername() && $uri->getPassword()) {
317
            $this->setAuth($uri->getUsername(), $uri->getPassword());
318
        }
319
320
        // We have no ports, set the defaults
321
        if (! $uri->getPort()) {
322
            $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80));
323
        }
324
325
        $this->uri = $uri;
326
327
        return $this;
328
    }
329
330
    /**
331
     * Get the URI for the next request
332
     *
333
     * @param boolean $as_string If true, will return the URI as a string
334
     * @return Zend_Uri_Http|string
335
     */
336
    public function getUri($as_string = false)
337
    {
338
        if ($as_string && $this->uri instanceof Zend_Uri_Http) {
339
            return $this->uri->__toString();
340
        } else {
341
            return $this->uri;
342
        }
343
    }
344
345
    /**
346
     * Set configuration parameters for this HTTP client
347
     *
348
     * @param  Zend_Config | array $config
349
     * @return Zend_Http_Client
350
     * @throws Zend_Http_Client_Exception
351
     */
352
    public function setConfig($config = array())
353
    {
354
        if ($config instanceof Zend_Config) {
0 ignored issues
show
Bug introduced by
The type Zend_Config was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
355
            $config = $config->toArray();
356
357
        } elseif (! is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
358
            /** @see Zend_Http_Client_Exception */
359
            require_once 'Zend/Http/Client/Exception.php';
360
            throw new Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' . gettype($config));
361
        }
362
363
        foreach ($config as $k => $v) {
364
            $this->config[strtolower($k)] = $v;
365
        }
366
367
        // Pass configuration options to the adapter if it exists
368
        if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
0 ignored issues
show
introduced by
$this->adapter is always a sub-type of Zend_Http_Client_Adapter_Interface.
Loading history...
369
            $this->adapter->setConfig($config);
370
        }
371
372
        return $this;
373
    }
374
375
    /**
376
     * Set the next request's method
377
     *
378
     * Validated the passed method and sets it. If we have files set for
379
     * POST requests, and the new method is not POST, the files are silently
380
     * dropped.
381
     *
382
     * @param string $method
383
     * @return Zend_Http_Client
384
     * @throws Zend_Http_Client_Exception
385
     */
386
    public function setMethod($method = self::GET)
387
    {
388
        if (! preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/', $method)) {
389
            require_once 'Zend/Http/Client/Exception.php';
390
            throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
391
        }
392
393
        if (($method == self::POST
394
                || $method == self::PUT
395
                || $method == self::DELETE
396
                || $method == self::PATCH
397
                || $method == self::OPTIONS)
398
            && $this->enctype === null
399
        ) {
400
            $this->setEncType(self::ENC_URLENCODED);
401
        }
402
403
        $this->method = $method;
404
405
        return $this;
406
    }
407
408
    /**
409
     * Set one or more request headers
410
     *
411
     * This function can be used in several ways to set the client's request
412
     * headers:
413
     * 1. By providing two parameters: $name as the header to set (e.g. 'Host')
414
     *    and $value as it's value (e.g. 'www.example.com').
415
     * 2. By providing a single header string as the only parameter
416
     *    e.g. 'Host: www.example.com'
417
     * 3. By providing an array of headers as the first parameter
418
     *    e.g. array('host' => 'www.example.com', 'x-foo: bar'). In This case
419
     *    the function will call itself recursively for each array item.
420
     *
421
     * @param string|array $name Header name, full header string ('Header: value')
422
     *     or an array of headers
423
     * @param mixed $value Header value or null
424
     * @return Zend_Http_Client
425
     * @throws Zend_Http_Client_Exception
426
     */
427
    public function setHeaders($name, $value = null)
428
    {
429
        // If we got an array, go recursive!
430
        if (is_array($name)) {
431
            foreach ($name as $k => $v) {
432
                if (is_string($k)) {
433
                    $this->setHeaders($k, $v);
434
                } else {
435
                    $this->setHeaders($v, null);
436
                }
437
            }
438
        } else {
439
            // Check if $name needs to be split
440
            if ($value === null && (strpos($name, ':') > 0)) {
441
                list($name, $value) = explode(':', $name, 2);
442
            }
443
444
            // Make sure the name is valid if we are in strict mode
445
            if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) {
446
                /** @see Zend_Http_Client_Exception */
447
                require_once 'Zend/Http/Client/Exception.php';
448
                throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
449
            }
450
451
            $normalized_name = strtolower($name);
452
453
            // If $value is null or false, unset the header
454
            if ($value === null || $value === false) {
455
                unset($this->headers[$normalized_name]);
456
457
            // Else, set the header
458
            } else {
459
                // Header names are stored lowercase internally.
460
                if (is_string($value)) {
461
                    $value = trim($value);
462
                }
463
                $this->headers[$normalized_name] = array($name, $value);
464
            }
465
        }
466
467
        return $this;
468
    }
469
470
    /**
471
     * Get the value of a specific header
472
     *
473
     * Note that if the header has more than one value, an array
474
     * will be returned.
475
     *
476
     * @param string $key
477
     * @return string|array|null The header value or null if it is not set
478
     */
479
    public function getHeader($key)
480
    {
481
        $key = strtolower($key);
482
        if (isset($this->headers[$key])) {
483
            return $this->headers[$key][1];
484
        } else {
485
            return null;
486
        }
487
    }
488
489
    /**
490
     * Set a GET parameter for the request. Wrapper around _setParameter
491
     *
492
     * @param string|array $name
493
     * @param string $value
494
     * @return Zend_Http_Client
495
     */
496
    public function setParameterGet($name, $value = null)
497
    {
498
        if (is_array($name)) {
499
            foreach ($name as $k => $v)
500
                $this->_setParameter('GET', $k, $v);
501
        } else {
502
            $this->_setParameter('GET', $name, $value);
503
        }
504
505
        return $this;
506
    }
507
508
    /**
509
     * Set a POST parameter for the request. Wrapper around _setParameter
510
     *
511
     * @param string|array $name
512
     * @param string $value
513
     * @return Zend_Http_Client
514
     */
515
    public function setParameterPost($name, $value = null)
516
    {
517
        if (is_array($name)) {
518
            foreach ($name as $k => $v)
519
                $this->_setParameter('POST', $k, $v);
520
        } else {
521
            $this->_setParameter('POST', $name, $value);
522
        }
523
524
        return $this;
525
    }
526
527
    /**
528
     * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
529
     *
530
     * @param string $type GET or POST
531
     * @param string $name
532
     * @param string $value
533
     * @return null
534
     */
535
    protected function _setParameter($type, $name, $value)
536
    {
537
        $parray = array();
538
        $type = strtolower($type);
539
        switch ($type) {
540
            case 'get':
541
                $parray = &$this->paramsGet;
542
                break;
543
            case 'post':
544
                $parray = &$this->paramsPost;
545
                if ( $value === null ) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
546
                    if (isset($this->body_field_order[$name]))
547
                        unset($this->body_field_order[$name]);
548
                } else {
549
                    $this->body_field_order[$name] = self::VTYPE_SCALAR;
550
                }
551
                break;
552
        }
553
554
        if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
555
            if (isset($parray[$name])) unset($parray[$name]);
556
        } else {
557
            $parray[$name] = $value;
558
        }
559
    }
560
561
    /**
562
     * Get the number of redirections done on the last request
563
     *
564
     * @return int
565
     */
566
    public function getRedirectionsCount()
567
    {
568
        return $this->redirectCounter;
569
    }
570
571
    /**
572
     * Set HTTP authentication parameters
573
     *
574
     * $type should be one of the supported types - see the self::AUTH_*
575
     * constants.
576
     *
577
     * To enable authentication:
578
     * <code>
579
     * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
580
     * </code>
581
     *
582
     * To disable authentication:
583
     * <code>
584
     * $this->setAuth(false);
585
     * </code>
586
     *
587
     * @see http://www.faqs.org/rfcs/rfc2617.html
588
     * @param string|false $user User name or false disable authentication
589
     * @param string $password Password
590
     * @param string $type Authentication type
591
     * @return Zend_Http_Client
592
     * @throws Zend_Http_Client_Exception
593
     */
594
    public function setAuth($user, $password = '', $type = self::AUTH_BASIC)
595
    {
596
        // If we got false or null, disable authentication
597
        if ($user === false || $user === null) {
598
            $this->auth = null;
599
600
            // Clear the auth information in the uri instance as well
601
            if ($this->uri instanceof Zend_Uri_Http) {
0 ignored issues
show
introduced by
$this->uri is always a sub-type of Zend_Uri_Http.
Loading history...
602
                $this->getUri()->setUsername('');
603
                $this->getUri()->setPassword('');
604
            }
605
        // Else, set up authentication
606
        } else {
607
            // Check we got a proper authentication type
608
            if (! defined('self::AUTH_' . strtoupper($type))) {
609
                /** @see Zend_Http_Client_Exception */
610
                require_once 'Zend/Http/Client/Exception.php';
611
                throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
612
            }
613
614
            $this->auth = array(
615
                'user' => (string) $user,
616
                'password' => (string) $password,
617
                'type' => $type
618
            );
619
        }
620
621
        return $this;
622
    }
623
624
    /**
625
     * Set the HTTP client's cookie jar.
626
     *
627
     * A cookie jar is an object that holds and maintains cookies across HTTP requests
628
     * and responses.
629
     *
630
     * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable
631
     * @return Zend_Http_Client
632
     * @throws Zend_Http_Client_Exception
633
     */
634
    public function setCookieJar($cookiejar = true)
635
    {
636
        Zend_Loader::loadClass('Zend_Http_CookieJar');
637
638
        if ($cookiejar instanceof Zend_Http_CookieJar) {
639
            $this->cookiejar = $cookiejar;
640
        } elseif ($cookiejar === true) {
641
            $this->cookiejar = new Zend_Http_CookieJar();
642
        } elseif (! $cookiejar) {
0 ignored issues
show
introduced by
The condition $cookiejar is always false.
Loading history...
643
            $this->cookiejar = null;
644
        } else {
645
            /** @see Zend_Http_Client_Exception */
646
            require_once 'Zend/Http/Client/Exception.php';
647
            throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar');
648
        }
649
650
        return $this;
651
    }
652
653
    /**
654
     * Return the current cookie jar or null if none.
655
     *
656
     * @return Zend_Http_CookieJar|null
657
     */
658
    public function getCookieJar()
659
    {
660
        return $this->cookiejar;
661
    }
662
663
    /**
664
     * Add a cookie to the request. If the client has no Cookie Jar, the cookies
665
     * will be added directly to the headers array as "Cookie" headers.
666
     *
667
     * @param Zend_Http_Cookie|string $cookie
668
     * @param string|null $value If "cookie" is a string, this is the cookie value.
669
     * @return Zend_Http_Client
670
     * @throws Zend_Http_Client_Exception
671
     */
672
    public function setCookie($cookie, $value = null)
673
    {
674
        Zend_Loader::loadClass('Zend_Http_Cookie');
675
676
        if (is_array($cookie)) {
0 ignored issues
show
introduced by
The condition is_array($cookie) is always false.
Loading history...
677
            foreach ($cookie as $c => $v) {
678
                if (is_string($c)) {
679
                    $this->setCookie($c, $v);
680
                } else {
681
                    $this->setCookie($v);
682
                }
683
            }
684
685
            return $this;
686
        }
687
688
        if ($value !== null && $this->config['encodecookies']) {
689
            $value = urlencode($value);
690
        }
691
692
        if (isset($this->cookiejar)) {
693
            if ($cookie instanceof Zend_Http_Cookie) {
0 ignored issues
show
Bug introduced by
The type Zend_Http_Cookie was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
694
                $this->cookiejar->addCookie($cookie);
695
            } elseif (is_string($cookie) && $value !== null) {
696
                $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}",
697
                                                       $this->uri,
698
                                                       $this->config['encodecookies']);
699
                $this->cookiejar->addCookie($cookie);
700
            }
701
        } else {
702
            if ($cookie instanceof Zend_Http_Cookie) {
703
                $name = $cookie->getName();
704
                $value = $cookie->getValue();
705
                $cookie = $name;
706
            }
707
708
            if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) {
709
                /** @see Zend_Http_Client_Exception */
710
                require_once 'Zend/Http/Client/Exception.php';
711
                throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})");
712
            }
713
714
            $value = addslashes($value);
715
716
            if (! isset($this->headers['cookie'])) {
717
                $this->headers['cookie'] = array('Cookie', '');
718
            }
719
            $this->headers['cookie'][1] .= $cookie . '=' . $value . '; ';
720
        }
721
722
        return $this;
723
    }
724
725
    /**
726
     * Set a file to upload (using a POST request)
727
     *
728
     * Can be used in two ways:
729
     *
730
     * 1. $data is null (default): $filename is treated as the name if a local file which
731
     *    will be read and sent. Will try to guess the content type using mime_content_type().
732
     * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
733
     *    contents and no file is read from the file system. In this case, you need to
734
     *    manually set the Content-Type ($ctype) or it will default to
735
     *    application/octet-stream.
736
     *
737
     * @param string $filename Name of file to upload, or name to save as
738
     * @param string $formname Name of form element to send as
739
     * @param string $data Data to send (if null, $filename is read and sent)
740
     * @param string $ctype Content type to use (if $data is set and $ctype is
741
     *     null, will be application/octet-stream)
742
     * @return Zend_Http_Client
743
     * @throws Zend_Http_Client_Exception
744
     */
745
    public function setFileUpload($filename, $formname, $data = null, $ctype = null)
746
    {
747
        if ($data === null) {
748
            if (($data = @file_get_contents($filename)) === false) {
749
                /** @see Zend_Http_Client_Exception */
750
                require_once 'Zend/Http/Client/Exception.php';
751
                throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
752
            }
753
754
            if (! $ctype) {
755
                $ctype = $this->_detectFileMimeType($filename);
756
            }
757
        }
758
759
        // Force enctype to multipart/form-data
760
        $this->setEncType(self::ENC_FORMDATA);
761
762
        $this->files[] = array(
763
            'formname' => $formname,
764
            'filename' => basename($filename),
765
            'ctype'    => $ctype,
766
            'data'     => $data
767
        );
768
769
        $this->body_field_order[$formname] = self::VTYPE_FILE;
770
771
        return $this;
772
    }
773
774
    /**
775
     * Set the encoding type for POST data
776
     *
777
     * @param string $enctype
778
     * @return Zend_Http_Client
779
     */
780
    public function setEncType($enctype = self::ENC_URLENCODED)
781
    {
782
        $this->enctype = $enctype;
783
784
        return $this;
785
    }
786
787
    /**
788
     * Set the raw (already encoded) POST data.
789
     *
790
     * This function is here for two reasons:
791
     * 1. For advanced user who would like to set their own data, already encoded
792
     * 2. For backwards compatibilty: If someone uses the old post($data) method.
793
     *    this method will be used to set the encoded data.
794
     *
795
     * $data can also be stream (such as file) from which the data will be read.
796
     *
797
     * @param string|resource $data
798
     * @param string $enctype
799
     * @return Zend_Http_Client
800
     */
801
    public function setRawData($data, $enctype = null)
802
    {
803
        $this->raw_post_data = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data can also be of type resource. However, the property $raw_post_data is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
804
        $this->setEncType($enctype);
805
        if (is_resource($data)) {
806
            // We've got stream data
807
            $stat = @fstat($data);
808
            if($stat) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stat 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...
809
                $this->setHeaders(self::CONTENT_LENGTH, $stat['size']);
810
            }
811
        }
812
        return $this;
813
    }
814
815
    /**
816
     * Set the unmask feature for GET parameters as array
817
     *
818
     * Example:
819
     * foo%5B0%5D=a&foo%5B1%5D=b
820
     * becomes
821
     * foo=a&foo=b
822
     *
823
     * This is usefull for some services
824
     *
825
     * @param boolean $status
826
     * @return Zend_Http_Client
827
     */
828
    public function setUnmaskStatus($status = true)
829
    {
830
        $this->_unmaskStatus = (BOOL)$status;
831
        return $this;
832
    }
833
834
    /**
835
     * Returns the currently configured unmask status
836
     *
837
     * @return boolean
838
     */
839
    public function getUnmaskStatus()
840
    {
841
        return $this->_unmaskStatus;
842
    }
843
844
    /**
845
     * Clear all GET and POST parameters
846
     *
847
     * Should be used to reset the request parameters if the client is
848
     * used for several concurrent requests.
849
     *
850
     * clearAll parameter controls if we clean just parameters or also
851
     * headers and last_*
852
     *
853
     * @param bool $clearAll Should all data be cleared?
854
     * @return Zend_Http_Client
855
     */
856
    public function resetParameters($clearAll = false)
857
    {
858
        // Reset parameter data
859
        $this->paramsGet     = array();
860
        $this->paramsPost    = array();
861
        $this->files         = array();
862
        $this->raw_post_data = null;
863
        $this->enctype       = null;
864
865
        if($clearAll) {
866
            $this->headers = array();
867
            $this->last_request = null;
868
            $this->last_response = null;
869
        } else {
870
            // Clear outdated headers
871
            if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) {
872
                unset($this->headers[strtolower(self::CONTENT_TYPE)]);
873
            }
874
            if (isset($this->headers[strtolower(self::CONTENT_LENGTH)])) {
875
                unset($this->headers[strtolower(self::CONTENT_LENGTH)]);
876
            }
877
        }
878
879
        return $this;
880
    }
881
882
    /**
883
     * Get the last HTTP request as string
884
     *
885
     * @return string
886
     */
887
    public function getLastRequest()
888
    {
889
        return $this->last_request;
890
    }
891
892
    /**
893
     * Get the last HTTP response received by this client
894
     *
895
     * If $config['storeresponse'] is set to false, or no response was
896
     * stored yet, will return null
897
     *
898
     * @return Zend_Http_Response or null if none
899
     */
900
    public function getLastResponse()
901
    {
902
        return $this->last_response;
903
    }
904
905
    /**
906
     * Load the connection adapter
907
     *
908
     * While this method is not called more than one for a client, it is
909
     * seperated from ->request() to preserve logic and readability
910
     *
911
     * @param Zend_Http_Client_Adapter_Interface|string $adapter
912
     * @return null
913
     * @throws Zend_Http_Client_Exception
914
     */
915
    public function setAdapter($adapter)
916
    {
917
        if (is_string($adapter)) {
918
            try {
919
                Zend_Loader::loadClass($adapter);
920
            } catch (Zend_Exception $e) {
921
                /** @see Zend_Http_Client_Exception */
922
                require_once 'Zend/Http/Client/Exception.php';
923
                throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}", 0, $e);
924
            }
925
926
            $adapter = new $adapter;
927
        }
928
929
        if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) {
930
            /** @see Zend_Http_Client_Exception */
931
            require_once 'Zend/Http/Client/Exception.php';
932
            throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
933
        }
934
935
        $this->adapter = $adapter;
936
        $config = $this->config;
937
        unset($config['adapter']);
938
        $this->adapter->setConfig($config);
939
    }
940
941
    /**
942
     * Load the connection adapter
943
     *
944
     * @return Zend_Http_Client_Adapter_Interface $adapter
945
     */
946
    public function getAdapter()
947
    {
948
        if (null === $this->adapter) {
949
            $this->setAdapter($this->config['adapter']);
950
        }
951
952
        return $this->adapter;
953
    }
954
955
    /**
956
     * Set streaming for received data
957
     *
958
     * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming
959
     * @return Zend_Http_Client
960
     */
961
    public function setStream($streamfile = true)
962
    {
963
        $this->setConfig(array("output_stream" => $streamfile));
964
        return $this;
965
    }
966
967
    /**
968
     * Get status of streaming for received data
969
     * @return boolean|string
970
     */
971
    public function getStream()
972
    {
973
        return $this->config["output_stream"];
974
    }
975
976
    /**
977
     * Create temporary stream
978
     *
979
     * @return resource
980
     */
981
    protected function _openTempStream()
982
    {
983
        $this->_stream_name = $this->config['output_stream'];
0 ignored issues
show
Bug Best Practice introduced by
The property _stream_name does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
984
        if(!is_string($this->_stream_name)) {
985
            // If name is not given, create temp name
986
            $this->_stream_name = tempnam(isset($this->config['stream_tmp_dir'])?$this->config['stream_tmp_dir']:sys_get_temp_dir(),
987
                 'Zend_Http_Client');
988
        }
989
990
        if (false === ($fp = @fopen($this->_stream_name, "w+b"))) {
991
                if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
0 ignored issues
show
introduced by
$this->adapter is always a sub-type of Zend_Http_Client_Adapter_Interface.
Loading history...
992
                    $this->adapter->close();
993
                }
994
                require_once 'Zend/Http/Client/Exception.php';
995
                throw new Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}");
996
        }
997
998
        return $fp;
999
    }
1000
1001
    /**
1002
     * Send the HTTP request and return an HTTP response object
1003
     *
1004
     * @param string $method
1005
     * @return Zend_Http_Response
1006
     * @throws Zend_Http_Client_Exception
1007
     */
1008
    public function request($method = null)
1009
    {
1010
        if (! $this->uri instanceof Zend_Uri_Http) {
0 ignored issues
show
introduced by
$this->uri is always a sub-type of Zend_Uri_Http.
Loading history...
1011
            /** @see Zend_Http_Client_Exception */
1012
            require_once 'Zend/Http/Client/Exception.php';
1013
            throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
1014
        }
1015
1016
        if ($method) {
1017
            $this->setMethod($method);
1018
        }
1019
        $this->redirectCounter = 0;
1020
        $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
1021
1022
        // Make sure the adapter is loaded
1023
        if ($this->adapter == null) {
1024
            $this->setAdapter($this->config['adapter']);
1025
        }
1026
1027
        // Send the first request. If redirected, continue.
1028
        do {
1029
            // Clone the URI and add the additional GET parameters to it
1030
            $uri = clone $this->uri;
1031
            if (! empty($this->paramsGet)) {
1032
                $query = $uri->getQuery();
1033
                   if (! empty($query)) {
1034
                       $query .= '&';
1035
                   }
1036
                $query .= http_build_query($this->paramsGet, null, '&');
1037
                if ($this->config['rfc3986_strict']) {
1038
                    $query = str_replace('+', '%20', $query);
1039
                }
1040
1041
                // @see ZF-11671 to unmask for some services to foo=val1&foo=val2
1042
                if ($this->getUnmaskStatus()) {
1043
                    if ($this->_queryBracketsEscaped) {
1044
                        $query = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $query);
1045
                    } else {
1046
                        $query = preg_replace('/\\[(?:[0-9]|[1-9][0-9]+)\\]=/', '=', $query);
1047
                    }
1048
                }
1049
1050
                $uri->setQuery($query);
1051
            }
1052
1053
            $body = $this->_prepareBody();
1054
            $headers = $this->_prepareHeaders();
1055
1056
            // check that adapter supports streaming before using it
1057
            if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) {
1058
                /** @see Zend_Http_Client_Exception */
1059
                require_once 'Zend/Http/Client/Exception.php';
1060
                throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1061
            }
1062
1063
            // Open the connection, send the request and read the response
1064
            $this->adapter->connect($uri->getHost(), $uri->getPort(),
0 ignored issues
show
Bug introduced by
$uri->getPort() of type string is incompatible with the type integer expected by parameter $port of Zend_Http_Client_Adapter_Interface::connect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1064
            $this->adapter->connect($uri->getHost(), /** @scrutinizer ignore-type */ $uri->getPort(),
Loading history...
1065
                ($uri->getScheme() == 'https' ? true : false));
1066
1067
            if($this->config['output_stream']) {
1068
                if($this->adapter instanceof Zend_Http_Client_Adapter_Stream) {
1069
                    $stream = $this->_openTempStream();
1070
                    $this->adapter->setOutputStream($stream);
1071
                } else {
1072
                    /** @see Zend_Http_Client_Exception */
1073
                    require_once 'Zend/Http/Client/Exception.php';
1074
                    throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1075
                }
1076
            }
1077
1078
            $this->last_request = $this->adapter->write($this->method,
1079
                $uri, $this->config['httpversion'], $headers, $body);
1080
1081
            $response = $this->adapter->read();
1082
            if (! $response) {
1083
                /** @see Zend_Http_Client_Exception */
1084
                require_once 'Zend/Http/Client/Exception.php';
1085
                throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
1086
            }
1087
1088
            if($this->config['output_stream']) {
1089
                $streamMetaData = stream_get_meta_data($stream);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $stream does not seem to be defined for all execution paths leading up to this point.
Loading history...
1090
                if ($streamMetaData['seekable']) {
1091
                    rewind($stream);
1092
                }
1093
                // cleanup the adapter
1094
                $this->adapter->setOutputStream(null);
1095
                $response = Zend_Http_Response_Stream::fromStream($response, $stream);
1096
                $response->setStreamName($this->_stream_name);
1097
                if(!is_string($this->config['output_stream'])) {
1098
                    // we used temp name, will need to clean up
1099
                    $response->setCleanup(true);
1100
                }
1101
            } else {
1102
                $response = Zend_Http_Response::fromString($response);
1103
            }
1104
1105
            if ($this->config['storeresponse']) {
1106
                $this->last_response = $response;
1107
            }
1108
1109
            // Load cookies into cookie jar
1110
            if (isset($this->cookiejar)) {
1111
                $this->cookiejar->addCookiesFromResponse($response, $uri, $this->config['encodecookies']);
1112
            }
1113
1114
            // If we got redirected, look for the Location header
1115
            if ($response->isRedirect() && ($location = $response->getHeader('location'))) {
1116
1117
                // Avoid problems with buggy servers that add whitespace at the
1118
                // end of some headers (See ZF-11283)
1119
                $location = trim($location);
0 ignored issues
show
Bug introduced by
It seems like $location can also be of type array; however, parameter $str of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1119
                $location = trim(/** @scrutinizer ignore-type */ $location);
Loading history...
1120
1121
                // Check whether we send the exact same request again, or drop the parameters
1122
                // and send a GET request
1123
                if ($response->getStatus() == 303 ||
1124
                   ((! $this->config['strictredirects']) && ($response->getStatus() == 302 ||
1125
                       $response->getStatus() == 301))) {
1126
1127
                    $this->resetParameters();
1128
                    $this->setMethod(self::GET);
1129
                }
1130
1131
                // If we got a well formed absolute URI
1132
                if (($scheme = substr($location, 0, 6)) && ($scheme == 'http:/' || $scheme == 'https:')) {
1133
                    $this->setHeaders('host', null);
1134
                    $this->setUri($location);
1135
1136
                } else {
1137
1138
                    // Split into path and query and set the query
1139
                    if (strpos($location, '?') !== false) {
1140
                        list($location, $query) = explode('?', $location, 2);
1141
                    } else {
1142
                        $query = '';
1143
                    }
1144
                    $this->uri->setQuery($query);
1145
1146
                    // Else, if we got just an absolute path, set it
1147
                    if(strpos($location, '/') === 0) {
1148
                        $this->uri->setPath($location);
1149
1150
                        // Else, assume we have a relative path
1151
                    } else {
1152
                        // Get the current path directory, removing any trailing slashes
1153
                        $path = $this->uri->getPath();
1154
                        $path = rtrim(substr($path, 0, strrpos($path, '/')), "/");
1155
                        $this->uri->setPath($path . '/' . $location);
1156
                    }
1157
                }
1158
                ++$this->redirectCounter;
1159
1160
            } else {
1161
                // If we didn't get any location, stop redirecting
1162
                break;
1163
            }
1164
1165
        } while ($this->redirectCounter < $this->config['maxredirects']);
1166
1167
        return $response;
1168
    }
1169
1170
    /**
1171
     * Prepare the request headers
1172
     *
1173
     * @return array
1174
     */
1175
    protected function _prepareHeaders()
1176
    {
1177
        $headers = array();
1178
1179
        // Set the host header
1180
        if (! isset($this->headers['host'])) {
1181
            $host = $this->uri->getHost();
1182
1183
            // If the port is not default, add it
1184
            if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
1185
                  ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
1186
                $host .= ':' . $this->uri->getPort();
1187
            }
1188
1189
            $headers[] = "Host: {$host}";
1190
        }
1191
1192
        // Set the connection header
1193
        if (! isset($this->headers['connection'])) {
1194
            if (! $this->config['keepalive']) {
1195
                $headers[] = "Connection: close";
1196
            }
1197
        }
1198
1199
        // Set the Accept-encoding header if not set - depending on whether
1200
        // zlib is available or not.
1201
        if (! isset($this->headers['accept-encoding'])) {
1202
            if (function_exists('gzinflate')) {
1203
                $headers[] = 'Accept-encoding: gzip, deflate';
1204
            } else {
1205
                $headers[] = 'Accept-encoding: identity';
1206
            }
1207
        }
1208
1209
        // Set the Content-Type header
1210
        if (($this->method == self::POST || $this->method == self::PUT) &&
1211
           (! isset($this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) {
1212
1213
            $headers[] = self::CONTENT_TYPE . ': ' . $this->enctype;
1214
        }
1215
1216
        // Set the user agent header
1217
        if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
1218
            $headers[] = "User-Agent: {$this->config['useragent']}";
1219
        }
1220
1221
        // Set HTTP authentication if needed
1222
        if (is_array($this->auth)) {
1223
            $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
1224
            $headers[] = "Authorization: {$auth}";
1225
        }
1226
1227
        // Load cookies from cookie jar
1228
        if (isset($this->cookiejar)) {
1229
            $cookstr = $this->cookiejar->getMatchingCookies($this->uri,
1230
                true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT);
1231
1232
            if ($cookstr) {
1233
                $headers[] = "Cookie: {$cookstr}";
1234
            }
1235
        }
1236
1237
        // Add all other user defined headers
1238
        foreach ($this->headers as $header) {
1239
            list($name, $value) = $header;
1240
            if (is_array($value)) {
1241
                $value = implode(', ', $value);
1242
            }
1243
1244
            $headers[] = "$name: $value";
1245
        }
1246
1247
        return $headers;
1248
    }
1249
1250
    /**
1251
     * Prepare the request body (for POST and PUT requests)
1252
     *
1253
     * @return string
1254
     * @throws Zend_Http_Client_Exception
1255
     */
1256
    protected function _prepareBody()
1257
    {
1258
        // According to RFC2616, a TRACE request should not have a body.
1259
        if ($this->method == self::TRACE) {
1260
            return '';
1261
        }
1262
1263
        if (isset($this->raw_post_data) && is_resource($this->raw_post_data)) {
0 ignored issues
show
introduced by
The condition is_resource($this->raw_post_data) is always false.
Loading history...
1264
            return $this->raw_post_data;
1265
        }
1266
        // If mbstring overloads substr and strlen functions, we have to
1267
        // override it's internal encoding
1268
        if (function_exists('mb_internal_encoding') &&
1269
           ((int) ini_get('mbstring.func_overload')) & 2) {
1270
1271
            $mbIntEnc = mb_internal_encoding();
1272
            mb_internal_encoding('ASCII');
1273
        }
1274
1275
        // If we have raw_post_data set, just use it as the body.
1276
        if (isset($this->raw_post_data)) {
1277
            $this->setHeaders(self::CONTENT_LENGTH, strlen($this->raw_post_data));
1278
            if (isset($mbIntEnc)) {
1279
                mb_internal_encoding($mbIntEnc);
1280
            }
1281
1282
            return $this->raw_post_data;
1283
        }
1284
1285
        $body = '';
1286
1287
        // If we have files to upload, force enctype to multipart/form-data
1288
        if (count ($this->files) > 0) {
1289
            $this->setEncType(self::ENC_FORMDATA);
1290
        }
1291
1292
        // If we have POST parameters or files, encode and add them to the body
1293
        if (count($this->paramsPost) > 0 || count($this->files) > 0) {
1294
            switch($this->enctype) {
1295
                case self::ENC_FORMDATA:
1296
                    // Encode body as multipart/form-data
1297
                    $boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
1298
                    $this->setHeaders(self::CONTENT_TYPE, self::ENC_FORMDATA . "; boundary={$boundary}");
1299
1300
                    // Encode all files and POST vars in the order they were given
1301
                    foreach ($this->body_field_order as $fieldName=>$fieldType) {
1302
                        switch ($fieldType) {
1303
                            case self::VTYPE_FILE:
1304
                                foreach ($this->files as $file) {
1305
                                    if ($file['formname']===$fieldName) {
1306
                                        $fhead = array(self::CONTENT_TYPE => $file['ctype']);
1307
                                        $body .= self::encodeFormData($boundary, $file['formname'], $file['data'], $file['filename'], $fhead);
1308
                                    }
1309
                                }
1310
                                break;
1311
                            case self::VTYPE_SCALAR:
1312
                                if (isset($this->paramsPost[$fieldName])) {
1313
                                    if (is_array($this->paramsPost[$fieldName])) {
1314
                                        $flattened = self::_flattenParametersArray($this->paramsPost[$fieldName], $fieldName);
1315
                                        foreach ($flattened as $pp) {
1316
                                            $body .= self::encodeFormData($boundary, $pp[0], $pp[1]);
1317
                                        }
1318
                                    } else {
1319
                                        $body .= self::encodeFormData($boundary, $fieldName, $this->paramsPost[$fieldName]);
1320
                                    }
1321
                                }
1322
                                break;
1323
                        }
1324
                    }
1325
1326
                    $body .= "--{$boundary}--\r\n";
1327
                    break;
1328
1329
                case self::ENC_URLENCODED:
1330
                    // Encode body as application/x-www-form-urlencoded
1331
                    $this->setHeaders(self::CONTENT_TYPE, self::ENC_URLENCODED);
1332
                    $body = http_build_query($this->paramsPost, '', '&');
1333
                    break;
1334
1335
                default:
1336
                    if (isset($mbIntEnc)) {
1337
                        mb_internal_encoding($mbIntEnc);
1338
                    }
1339
1340
                    /** @see Zend_Http_Client_Exception */
1341
                    require_once 'Zend/Http/Client/Exception.php';
1342
                    throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." .
1343
                        " Please use Zend_Http_Client::setRawData to send this kind of content.");
1344
                    break;
1345
            }
1346
        }
1347
1348
        // Set the Content-Length if we have a body or if request is POST/PUT
1349
        if ($body || $this->method == self::POST || $this->method == self::PUT) {
1350
            $this->setHeaders(self::CONTENT_LENGTH, strlen($body));
1351
        }
1352
1353
        if (isset($mbIntEnc)) {
1354
            mb_internal_encoding($mbIntEnc);
1355
        }
1356
1357
        return $body;
1358
    }
1359
1360
    /**
1361
     * Helper method that gets a possibly multi-level parameters array (get or
1362
     * post) and flattens it.
1363
     *
1364
     * The method returns an array of (key, value) pairs (because keys are not
1365
     * necessarily unique. If one of the parameters in as array, it will also
1366
     * add a [] suffix to the key.
1367
     *
1368
     * This method is deprecated since Zend Framework 1.9 in favour of
1369
     * self::_flattenParametersArray() and will be dropped in 2.0
1370
     *
1371
     * @deprecated since 1.9
1372
     *
1373
     * @param  array $parray    The parameters array
1374
     * @param  bool  $urlencode Whether to urlencode the name and value
1375
     * @return array
1376
     */
1377
    protected function _getParametersRecursive($parray, $urlencode = false)
1378
    {
1379
        // Issue a deprecated notice
1380
        trigger_error("The " .  __METHOD__ . " method is deprecated and will be dropped in 2.0.",
1381
            E_USER_NOTICE);
1382
1383
        if (! is_array($parray)) {
0 ignored issues
show
introduced by
The condition is_array($parray) is always true.
Loading history...
1384
            return $parray;
1385
        }
1386
        $parameters = array();
1387
1388
        foreach ($parray as $name => $value) {
1389
            if ($urlencode) {
1390
                $name = urlencode($name);
1391
            }
1392
1393
            // If $value is an array, iterate over it
1394
            if (is_array($value)) {
1395
                $name .= ($urlencode ? '%5B%5D' : '[]');
1396
                foreach ($value as $subval) {
1397
                    if ($urlencode) {
1398
                        $subval = urlencode($subval);
1399
                    }
1400
                    $parameters[] = array($name, $subval);
1401
                }
1402
            } else {
1403
                if ($urlencode) {
1404
                    $value = urlencode($value);
1405
                }
1406
                $parameters[] = array($name, $value);
1407
            }
1408
        }
1409
1410
        return $parameters;
1411
    }
1412
1413
    /**
1414
     * Attempt to detect the MIME type of a file using available extensions
1415
     *
1416
     * This method will try to detect the MIME type of a file. If the fileinfo
1417
     * extension is available, it will be used. If not, the mime_magic
1418
     * extension which is deprected but is still available in many PHP setups
1419
     * will be tried.
1420
     *
1421
     * If neither extension is available, the default application/octet-stream
1422
     * MIME type will be returned
1423
     *
1424
     * @param  string $file File path
1425
     * @return string       MIME type
1426
     */
1427
    protected function _detectFileMimeType($file)
1428
    {
1429
        $type = null;
1430
1431
        // First try with fileinfo functions
1432
        if (function_exists('finfo_open')) {
1433
            if (self::$_fileInfoDb === null) {
0 ignored issues
show
introduced by
The condition self::_fileInfoDb === null is always false.
Loading history...
1434
                self::$_fileInfoDb = @finfo_open(FILEINFO_MIME);
1435
            }
1436
1437
            if (self::$_fileInfoDb) {
1438
                $type = finfo_file(self::$_fileInfoDb, $file);
1439
            }
1440
1441
        } elseif (function_exists('mime_content_type')) {
1442
            $type = mime_content_type($file);
1443
        }
1444
1445
        // Fallback to the default application/octet-stream
1446
        if (! $type) {
1447
            $type = 'application/octet-stream';
1448
        }
1449
1450
        return $type;
1451
    }
1452
1453
    /**
1454
     * Encode data to a multipart/form-data part suitable for a POST request.
1455
     *
1456
     * @param string $boundary
1457
     * @param string $name
1458
     * @param mixed $value
1459
     * @param string $filename
1460
     * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" => "binary")
1461
     * @return string
1462
     */
1463
    public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array())
1464
    {
1465
        $ret = "--{$boundary}\r\n" .
1466
            'Content-Disposition: form-data; name="' . $name .'"';
1467
1468
        if ($filename) {
1469
            $ret .= '; filename="' . $filename . '"';
1470
        }
1471
        $ret .= "\r\n";
1472
1473
        foreach ($headers as $hname => $hvalue) {
1474
            $ret .= "{$hname}: {$hvalue}\r\n";
1475
        }
1476
        $ret .= "\r\n";
1477
1478
        $ret .= "{$value}\r\n";
1479
1480
        return $ret;
1481
    }
1482
1483
    /**
1484
     * Create a HTTP authentication "Authorization:" header according to the
1485
     * specified user, password and authentication method.
1486
     *
1487
     * @see http://www.faqs.org/rfcs/rfc2617.html
1488
     * @param string $user
1489
     * @param string $password
1490
     * @param string $type
1491
     * @return string
1492
     * @throws Zend_Http_Client_Exception
1493
     */
1494
    public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
1495
    {
1496
        $authHeader = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $authHeader is dead and can be removed.
Loading history...
1497
1498
        switch ($type) {
1499
            case self::AUTH_BASIC:
1500
                // In basic authentication, the user name cannot contain ":"
1501
                if (strpos($user, ':') !== false) {
1502
                    /** @see Zend_Http_Client_Exception */
1503
                    require_once 'Zend/Http/Client/Exception.php';
1504
                    throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication");
1505
                }
1506
1507
                $authHeader = 'Basic ' . base64_encode($user . ':' . $password);
1508
                break;
1509
1510
            //case self::AUTH_DIGEST:
1511
                /**
1512
                 * @todo Implement digest authentication
1513
                 */
1514
            //    break;
1515
1516
            default:
1517
                /** @see Zend_Http_Client_Exception */
1518
                require_once 'Zend/Http/Client/Exception.php';
1519
                throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
1520
        }
1521
1522
        return $authHeader;
1523
    }
1524
1525
    /**
1526
     * Convert an array of parameters into a flat array of (key, value) pairs
1527
     *
1528
     * Will flatten a potentially multi-dimentional array of parameters (such
1529
     * as POST parameters) into a flat array of (key, value) paris. In case
1530
     * of multi-dimentional arrays, square brackets ([]) will be added to the
1531
     * key to indicate an array.
1532
     *
1533
     * @since  1.9
1534
     *
1535
     * @param  array  $parray
1536
     * @param  string $prefix
1537
     * @return array
1538
     */
1539
    protected static function _flattenParametersArray($parray, $prefix = null)
1540
    {
1541
        if (! is_array($parray)) {
0 ignored issues
show
introduced by
The condition is_array($parray) is always true.
Loading history...
1542
            return $parray;
1543
        }
1544
1545
        $parameters = array();
1546
1547
        foreach($parray as $name => $value) {
1548
1549
            // Calculate array key
1550
            if ($prefix) {
1551
                if (is_int($name)) {
1552
                    $key = $prefix . '[]';
1553
                } else {
1554
                    $key = $prefix . "[$name]";
1555
                }
1556
            } else {
1557
                $key = $name;
1558
            }
1559
1560
            if (is_array($value)) {
1561
                $parameters = array_merge($parameters, self::_flattenParametersArray($value, $key));
1562
1563
            } else {
1564
                $parameters[] = array($key, $value);
1565
            }
1566
        }
1567
1568
        return $parameters;
1569
    }
1570
1571
}
1572