Completed
Push — master ( 0eb270...4f660e )
by
unknown
04:04 queued 01:31
created

src/Transport/Http.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Elastica\Transport;
4
5
use Elastica\Exception\Connection\HttpException;
6
use Elastica\Exception\ConnectionException;
7
use Elastica\Exception\PartialShardFailureException;
8
use Elastica\Exception\ResponseException;
9
use Elastica\JSON;
10
use Elastica\Request;
11
use Elastica\Response;
12
use Elastica\Util;
13
14
/**
15
 * Elastica Http Transport object.
16
 *
17
 * @author Nicolas Ruflin <[email protected]>
18
 */
19
class Http extends AbstractTransport
20
{
21
    /**
22
     * Http scheme.
23
     *
24
     * @var string Http scheme
25
     */
26
    protected $_scheme = 'http';
27
28
    /**
29
     * Curl resource to reuse.
30
     *
31
     * @var resource Curl resource to reuse
32
     */
33
    protected static $_curlConnection;
34
35
    /**
36
     * Makes calls to the elasticsearch server.
37
     *
38
     * All calls that are made to the server are done through this function
39
     *
40
     * @param array $params Host, Port, ...
41
     *
42
     * @throws ConnectionException
43
     * @throws ResponseException
44
     * @throws HttpException
45
     *
46
     * @return Response Response object
47
     */
48
    public function exec(Request $request, array $params): Response
49
    {
50
        $connection = $this->getConnection();
51
52
        $conn = $this->_getConnection($connection->isPersistent());
53
54
        // If url is set, url is taken. Otherwise port, host and path
55
        $url = $connection->hasConfig('url') ? $connection->getConfig('url') : '';
56
57
        if (!empty($url)) {
58
            $baseUri = $url;
59
        } else {
60
            $baseUri = $this->_scheme.'://'.$connection->getHost().':'.$connection->getPort().'/'.$connection->getPath();
61
        }
62
63
        $requestPath = $request->getPath();
64
        if (!Util::isDateMathEscaped($requestPath)) {
65
            $requestPath = Util::escapeDateMath($requestPath);
66
        }
67
68
        $baseUri .= $requestPath;
69
70
        $query = $request->getQuery();
71
72
        if (!empty($query)) {
73
            $baseUri .= '?'.\http_build_query(
74
                $this->sanityzeQueryStringBool($query)
75
            );
76
        }
77
78
        \curl_setopt($conn, \CURLOPT_URL, $baseUri);
79
        \curl_setopt($conn, \CURLOPT_TIMEOUT_MS, $connection->getTimeout() * 1000);
80
        \curl_setopt($conn, \CURLOPT_FORBID_REUSE, 0);
81
82
        // Tell ES that we support the compressed responses
83
        // An "Accept-Encoding" header containing all supported encoding types is sent
84
        // curl will decode the response automatically if the response is encoded
85
        \curl_setopt($conn, \CURLOPT_ENCODING, '');
86
87
        /* @see Connection::setConnectTimeout() */
88
        $connectTimeoutMs = $connection->getConnectTimeout() * 1000;
89
90
        // Let's only apply this value if the number of ms is greater than or equal to "1".
91
        // In case "0" is passed as an argument, the value is reset to its default (300 s)
92
        if ($connectTimeoutMs >= 1) {
93
            \curl_setopt($conn, \CURLOPT_CONNECTTIMEOUT_MS, $connectTimeoutMs);
94
        }
95
96
        if (null !== $proxy = $connection->getProxy()) {
97
            \curl_setopt($conn, \CURLOPT_PROXY, $proxy);
98
        }
99
100
        $username = $connection->getUsername();
101
        $password = $connection->getPassword();
102
        if (null !== $username && null !== $password) {
103
            \curl_setopt($conn, \CURLOPT_HTTPAUTH, $this->_getAuthType());
104
            \curl_setopt($conn, \CURLOPT_USERPWD, "{$username}:{$password}");
105
        }
106
107
        $this->_setupCurl($conn);
108
109
        $headersConfig = $connection->hasConfig('headers') ? $connection->getConfig('headers') : [];
110
111
        $headers = [];
112
113
        if (!empty($headersConfig)) {
114
            foreach ($headersConfig as $header => $headerValue) {
0 ignored issues
show
The expression $headersConfig of type array|boolean|integer|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
115
                $headers[] = $header.': '.$headerValue;
116
            }
117
        }
118
119
        // TODO: REFACTOR
120
        $data = $request->getData();
121
        $httpMethod = $request->getMethod();
122
123
        if (!empty($data) || '0' === $data) {
124
            if ($this->hasParam('postWithRequestBody') && true == $this->getParam('postWithRequestBody')) {
125
                $httpMethod = Request::POST;
126
            }
127
128
            if (\is_array($data)) {
129
                $content = JSON::stringify($data, \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES);
130
            } else {
131
                $content = $data;
132
133
                // Escaping of / not necessary. Causes problems in base64 encoding of files
134
                $content = \str_replace('\/', '/', $content);
135
            }
136
137
            $headers[] = 'Content-Type: '.$request->getContentType();
138
            if ($connection->hasCompression()) {
139
                // Compress the body of the request ...
140
                \curl_setopt($conn, \CURLOPT_POSTFIELDS, \gzencode($content));
141
142
                // ... and tell ES that it is compressed
143
                $headers[] = 'Content-Encoding: gzip';
144
            } else {
145
                \curl_setopt($conn, \CURLOPT_POSTFIELDS, $content);
146
            }
147
        } else {
148
            \curl_setopt($conn, \CURLOPT_POSTFIELDS, '');
149
        }
150
151
        \curl_setopt($conn, \CURLOPT_HTTPHEADER, $headers);
152
153
        \curl_setopt($conn, \CURLOPT_NOBODY, 'HEAD' == $httpMethod);
154
155
        \curl_setopt($conn, \CURLOPT_CUSTOMREQUEST, $httpMethod);
156
157
        $start = \microtime(true);
158
159
        // cURL opt returntransfer leaks memory, therefore OB instead.
160
        \ob_start();
161
        \curl_exec($conn);
162
        $responseString = \ob_get_clean();
163
164
        $end = \microtime(true);
165
166
        // Checks if error exists
167
        $errorNumber = \curl_errno($conn);
168
169
        $response = new Response($responseString, \curl_getinfo($conn, \CURLINFO_HTTP_CODE));
170
        $response->setQueryTime($end - $start);
171
        $response->setTransferInfo(\curl_getinfo($conn));
172
        if ($connection->hasConfig('bigintConversion')) {
173
            $response->setJsonBigintConversion($connection->getConfig('bigintConversion'));
174
        }
175
176
        if ($response->hasError()) {
177
            throw new ResponseException($request, $response);
178
        }
179
180
        if ($response->hasFailedShards()) {
181
            throw new PartialShardFailureException($request, $response);
182
        }
183
184
        if ($errorNumber > 0) {
185
            throw new HttpException($errorNumber, $request, $response);
186
        }
187
188
        return $response;
189
    }
190
191
    /**
192
     * Called to add additional curl params.
193
     *
194
     * @param resource $curlConnection Curl connection
195
     */
196
    protected function _setupCurl($curlConnection): void
197
    {
198
        if ($this->getConnection()->hasConfig('curl')) {
199
            foreach ($this->getConnection()->getConfig('curl') as $key => $param) {
0 ignored issues
show
The expression $this->getConnection()->getConfig('curl') of type array|boolean|integer|string|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
200
                \curl_setopt($curlConnection, $key, $param);
201
            }
202
        }
203
    }
204
205
    /**
206
     * Return Curl resource.
207
     *
208
     * @param bool $persistent False if not persistent connection
209
     *
210
     * @return resource Connection resource
211
     */
212
    protected function _getConnection(bool $persistent = true)
213
    {
214
        if (!$persistent || !self::$_curlConnection) {
215
            self::$_curlConnection = \curl_init();
216
        }
217
218
        return self::$_curlConnection;
219
    }
220
221
    protected function _getAuthType()
222
    {
223
        switch ($this->_connection->getAuthType()) {
224
            case 'digest':
225
                return \CURLAUTH_DIGEST;
226
            case 'gssnegotiate':
227
                return \CURLAUTH_GSSNEGOTIATE;
228
            case 'ntlm':
229
                return \CURLAUTH_NTLM;
230
            case 'basic':
231
                return \CURLAUTH_BASIC;
232
            default:
233
                return \CURLAUTH_ANY;
234
        }
235
    }
236
}
237