Curl::buildOptions()   F
last analyzed

Complexity

Conditions 16
Paths 2592

Size

Total Lines 75

Duplication

Lines 6
Ratio 8 %

Importance

Changes 0
Metric Value
cc 16
nc 2592
nop 2
dl 6
loc 75
rs 1.4
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
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10
 * @link          https://cakephp.org CakePHP(tm) Project
11
 * @since         3.7.0
12
 * @license       https://opensource.org/licenses/mit-license.php MIT License
13
 */
14
namespace Cake\Http\Client\Adapter;
15
16
use Cake\Http\Client\AdapterInterface;
17
use Cake\Http\Client\Request;
18
use Cake\Http\Client\Response;
19
use Cake\Http\Exception\HttpException;
20
21
/**
22
 * Implements sending Cake\Http\Client\Request via ext/curl.
23
 *
24
 * In addition to the standard options documented in Cake\Http\Client,
25
 * this adapter supports all available curl options. Additional curl options
26
 * can be set via the `curl` option key when making requests or configuring
27
 * a client.
28
 */
29
class Curl implements AdapterInterface
30
{
31
    /**
32
     * {@inheritDoc}
33
     */
34
    public function send(Request $request, array $options)
35
    {
36
        $ch = curl_init();
37
        $options = $this->buildOptions($request, $options);
38
        curl_setopt_array($ch, $options);
39
40
        $body = $this->exec($ch);
41
        if ($body === false) {
42
            $errorCode = curl_errno($ch);
43
            $error = curl_error($ch);
44
            curl_close($ch);
45
46
            $status = 500;
47
            if ($errorCode === CURLE_OPERATION_TIMEOUTED) {
48
                $status = 504;
49
            }
50
            throw new HttpException("cURL Error ({$errorCode}) {$error}", $status);
51
        }
52
53
        $responses = $this->createResponse($ch, $body);
54
        curl_close($ch);
55
56
        return $responses;
57
    }
58
59
    /**
60
     * Convert client options into curl options.
61
     *
62
     * @param \Cake\Http\Client\Request $request The request.
63
     * @param array $options The client options
64
     * @return array
65
     */
66
    public function buildOptions(Request $request, array $options)
67
    {
68
        $headers = [];
69 View Code Duplication
        foreach ($request->getHeaders() as $key => $values) {
70
            $headers[] = $key . ': ' . implode(', ', $values);
71
        }
72
73
        $out = [
74
            CURLOPT_URL => (string)$request->getUri(),
75
            CURLOPT_HTTP_VERSION => $this->getProtocolVersion($request),
76
            CURLOPT_RETURNTRANSFER => true,
77
            CURLOPT_HEADER => true,
78
            CURLOPT_HTTPHEADER => $headers,
79
        ];
80
        switch ($request->getMethod()) {
81
            case Request::METHOD_GET:
82
                $out[CURLOPT_HTTPGET] = true;
83
                break;
84
85
            case Request::METHOD_POST:
86
                $out[CURLOPT_POST] = true;
87
                break;
88
89
            default:
90
                $out[CURLOPT_POST] = true;
91
                $out[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
92
                break;
93
        }
94
95
        $body = $request->getBody();
96
        if ($body) {
97
            $body->rewind();
98
            $out[CURLOPT_POSTFIELDS] = $body->getContents();
99
            // GET requests with bodies require custom request to be used.
100
            if (isset($out[CURLOPT_HTTPGET])) {
101
                $out[CURLOPT_CUSTOMREQUEST] = 'get';
102
            }
103
        }
104
105 View Code Duplication
        if (empty($options['ssl_cafile'])) {
106
            $options['ssl_cafile'] = CORE_PATH . 'config' . DIRECTORY_SEPARATOR . 'cacert.pem';
107
        }
108
        if (!empty($options['ssl_verify_host'])) {
109
            // Value of 1 or true is deprecated. Only 2 or 0 should be used now.
110
            $options['ssl_verify_host'] = 2;
111
        }
112
        $optionMap = [
113
            'timeout' => CURLOPT_TIMEOUT,
114
            'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
115
            'ssl_verify_host' => CURLOPT_SSL_VERIFYHOST,
116
            'ssl_cafile' => CURLOPT_CAINFO,
117
            'ssl_local_cert' => CURLOPT_SSLCERT,
118
            'ssl_passphrase' => CURLOPT_SSLCERTPASSWD,
119
        ];
120
        foreach ($optionMap as $option => $curlOpt) {
121
            if (isset($options[$option])) {
122
                $out[$curlOpt] = $options[$option];
123
            }
124
        }
125
        if (isset($options['proxy']['proxy'])) {
126
            $out[CURLOPT_PROXY] = $options['proxy']['proxy'];
127
        }
128
        if (isset($options['proxy']['username'])) {
129
            $password = !empty($options['proxy']['password']) ? $options['proxy']['password'] : '';
130
            $out[CURLOPT_PROXYUSERPWD] = $options['proxy']['username'] . ':' . $password;
131
        }
132
        if (isset($options['curl']) && is_array($options['curl'])) {
133
            // Can't use array_merge() because keys will be re-ordered.
134
            foreach ($options['curl'] as $key => $value) {
135
                $out[$key] = $value;
136
            }
137
        }
138
139
        return $out;
140
    }
141
142
    /**
143
     * Convert HTTP version number into curl value.
144
     *
145
     * @param \Cake\Http\Client\Request $request The request to get a protocol version for.
146
     * @return int
147
     */
148
    protected function getProtocolVersion(Request $request)
149
    {
150
        switch ($request->getProtocolVersion()) {
151
            case '1.0':
152
                return CURL_HTTP_VERSION_1_0;
153
            case '1.1':
154
                return CURL_HTTP_VERSION_1_1;
155
            case '2':
156
            case '2.0':
157
                if (defined('CURL_HTTP_VERSION_2TLS')) {
158
                    return CURL_HTTP_VERSION_2TLS;
159
                }
160
                if (defined('CURL_HTTP_VERSION_2_0')) {
161
                    return CURL_HTTP_VERSION_2_0;
162
                }
163
                throw new HttpException('libcurl 7.33 or greater required for HTTP/2 support');
164
        }
165
166
        return CURL_HTTP_VERSION_NONE;
167
    }
168
169
    /**
170
     * Convert the raw curl response into an Http\Client\Response
171
     *
172
     * @param resource $handle Curl handle
173
     * @param string $responseData string The response data from curl_exec
174
     * @return \Cake\Http\Client\Response
175
     */
176
    protected function createResponse($handle, $responseData)
177
    {
178
        $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
179
        $headers = trim(substr($responseData, 0, $headerSize));
180
        $body = substr($responseData, $headerSize);
181
        $response = new Response(explode("\r\n", $headers), $body);
182
183
        return [$response];
184
    }
185
186
    /**
187
     * Execute the curl handle.
188
     *
189
     * @param resource $ch Curl Resource handle
190
     * @return string
191
     */
192
    protected function exec($ch)
193
    {
194
        return curl_exec($ch);
195
    }
196
}
197