Completed
Push — master ( 5ad7ea...0c833f )
by Ema
02:22
created

src/Transport/Http.php (1 issue)

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, $connection->getTimeout());
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
        $connectTimeout = $connection->getConnectTimeout();
89
        if ($connectTimeout > 0) {
90
            \curl_setopt($conn, CURLOPT_CONNECTTIMEOUT, $connectTimeout);
91
        }
92
93
        $proxy = $connection->getProxy();
94
95
        // See: https://github.com/facebook/hhvm/issues/4875
96 View Code Duplication
        if (null === $proxy && \defined('HHVM_VERSION')) {
97
            $proxy = \getenv('http_proxy') ?: null;
98
        }
99
100
        if (null !== $proxy) {
101
            \curl_setopt($conn, CURLOPT_PROXY, $proxy);
102
        }
103
104
        $username = $connection->getUsername();
105
        $password = $connection->getPassword();
106
        if (null !== $username && null !== $password) {
107
            \curl_setopt($conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
108
            \curl_setopt($conn, CURLOPT_USERPWD, "{$username}:{$password}");
109
        }
110
111
        $this->_setupCurl($conn);
112
113
        $headersConfig = $connection->hasConfig('headers') ? $connection->getConfig('headers') : [];
114
115
        $headers = [];
116
117
        if (!empty($headersConfig)) {
118
            $headers = [];
119
            foreach ($headersConfig as $header => $headerValue) {
120
                \array_push($headers, $header.': '.$headerValue);
121
            }
122
        }
123
124
        // TODO: REFACTOR
125
        $data = $request->getData();
126
        $httpMethod = $request->getMethod();
127
128
        if (!empty($data) || '0' === $data) {
129
            if ($this->hasParam('postWithRequestBody') && true == $this->getParam('postWithRequestBody')) {
130
                $httpMethod = Request::POST;
131
            }
132
133
            if (\is_array($data)) {
134
                $content = JSON::stringify($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
135
            } else {
136
                $content = $data;
137
138
                // Escaping of / not necessary. Causes problems in base64 encoding of files
139
                $content = \str_replace('\/', '/', $content);
140
            }
141
142
            \array_push($headers, \sprintf('Content-Type: %s', $request->getContentType()));
143
            if ($connection->hasCompression()) {
144
                // Compress the body of the request ...
145
                \curl_setopt($conn, CURLOPT_POSTFIELDS, \gzencode($content));
146
147
                // ... and tell ES that it is compressed
148
                \array_push($headers, 'Content-Encoding: gzip');
149
            } else {
150
                \curl_setopt($conn, CURLOPT_POSTFIELDS, $content);
151
            }
152
        } else {
153
            \curl_setopt($conn, CURLOPT_POSTFIELDS, '');
154
        }
155
156
        \curl_setopt($conn, CURLOPT_HTTPHEADER, $headers);
157
158
        \curl_setopt($conn, CURLOPT_NOBODY, 'HEAD' == $httpMethod);
159
160
        \curl_setopt($conn, CURLOPT_CUSTOMREQUEST, $httpMethod);
161
162
        $start = \microtime(true);
163
164
        // cURL opt returntransfer leaks memory, therefore OB instead.
165
        \ob_start();
166
        \curl_exec($conn);
167
        $responseString = \ob_get_clean();
168
169
        $end = \microtime(true);
170
171
        // Checks if error exists
172
        $errorNumber = \curl_errno($conn);
173
174
        $response = new Response($responseString, \curl_getinfo($conn, CURLINFO_HTTP_CODE));
175
        $response->setQueryTime($end - $start);
176
        $response->setTransferInfo(\curl_getinfo($conn));
177
        if ($connection->hasConfig('bigintConversion')) {
178
            $response->setJsonBigintConversion($connection->getConfig('bigintConversion'));
179
        }
180
181
        if ($response->hasError()) {
182
            throw new ResponseException($request, $response);
183
        }
184
185
        if ($response->hasFailedShards()) {
186
            throw new PartialShardFailureException($request, $response);
187
        }
188
189
        if ($errorNumber > 0) {
190
            throw new HttpException($errorNumber, $request, $response);
191
        }
192
193
        return $response;
194
    }
195
196
    /**
197
     * Called to add additional curl params.
198
     *
199
     * @param resource $curlConnection Curl connection
200
     */
201
    protected function _setupCurl($curlConnection): void
202
    {
203
        if ($this->getConnection()->hasConfig('curl')) {
204
            foreach ($this->getConnection()->getConfig('curl') as $key => $param) {
0 ignored issues
show
The expression $this->getConnection()->getConfig('curl') of type array|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...
205
                \curl_setopt($curlConnection, $key, $param);
206
            }
207
        }
208
    }
209
210
    /**
211
     * Return Curl resource.
212
     *
213
     * @param bool $persistent False if not persistent connection
214
     *
215
     * @return resource Connection resource
216
     */
217
    protected function _getConnection(bool $persistent = true)
218
    {
219
        if (!$persistent || !self::$_curlConnection) {
220
            self::$_curlConnection = \curl_init();
221
        }
222
223
        return self::$_curlConnection;
224
    }
225
}
226