GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 85cf53...3d1975 )
by Anderson
02:22
created

ElasticConnection::insert()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 3
eloc 13
nc 4
nop 5
1
<?php
2
3
namespace DoctrineElastic\Connection;
4
5
use DoctrineElastic\Exception\ConnectionException;
6
use DoctrineElastic\Exception\ElasticOperationException;
7
use DoctrineElastic\Helper\MappingHelper;
8
use DoctrineElastic\Http\CurlRequest;
9
use DoctrineElastic\Traiting\ErrorGetterTrait;
10
11
/**
12
 * Default elastic connection class for general operations
13
 * Notice that the original elastic result of most of operations can be get by $return param
14
 *
15
 * @author Andsalves <[email protected]>
16
 */
17
class ElasticConnection implements ElasticConnectionInterface {
18
19
    use ErrorGetterTrait;
20
21
    /** Override default elastic limit size query */
22
    const DEFAULT_MAX_RESULTS = 10000;
23
24
    /** @var CurlRequest */
25
    protected $curlRequest;
26
27
    /** @var float */
28
    protected $esVersion;
29
30
    public function __construct(array $hosts) {
31
        $this->curlRequest = new CurlRequest();
32
        $baseHost = reset($hosts);
33
34
        if (empty($baseHost) || !is_string($baseHost) || !preg_match('/http/', $baseHost)) {
35
            throw new ConnectionException("Elasticsearch host is invalid. ");
36
        }
37
38
        $this->curlRequest->setBaseUrl($baseHost);
39
    }
40
41
    /**
42
     * @param string $index
43
     * @param array|null $mappings
44
     * @param array|null $settings
45
     * @param array|null $aliases
46
     * @param array|null $return
47
     * @return bool
48
     */
49
    public function createIndex(
50
        $index, array $mappings = null, array $settings = null, array $aliases = null, array &$return = null
51
    ) {
52
        if ($this->indexExists($index)) {
53
            throw new \InvalidArgumentException(sprintf("'%s' index already exists", $index));
54
        }
55
56
        $params = [];
57
58
        if (is_array($mappings) && !empty($mappings)) {
59
            $params['mappings'] = MappingHelper::patchMappings($mappings, floor($this->getElasticsearchVersion()));
60
        }
61
62
        if (is_array($settings) && !empty($settings)) {
63
            $params['settings'] = $settings;
64
        }
65
66
        if (is_array($aliases) && !empty($aliases)) {
67
            $params['aliases'] = $aliases;
68
        }
69
70
        $response = $this->curlRequest->request($index, $params, 'PUT');
71
        $return = $response['content'];
72
73
        if (isset($return['acknowledged']) && $return['acknowledged']) {
74
            return $return['acknowledged'];
75
        }
76
77
        $this->setErrorFromElasticReturn($return);
78
79
        return false;
80
    }
81
82
    /**
83
     * @param string $index
84
     * @param array|null $return
85
     * @return bool
86
     * @throws ElasticOperationException
87
     */
88
    public function deleteIndex($index, array &$return = null) {
89
        if (is_string($index) && !strstr('_all', $index) && !strstr('*', $index)) {
90
            $response = $this->curlRequest->request("$index?refresh=true", [], 'DELETE');
91
            $return = $response['content'];
92
93
            if ($response['status'] == 404) {
94
                throw new ElasticOperationException("Index '$index' doesn't exist so cannot be deleted. ");
95
            }
96
97
            if (isset($return['acknowledged'])) {
98
                return $return['acknowledged'];
99
            }
100
        } else {
101
            throw new ElasticOperationException('Index name is invalid for deletion. ');
102
        }
103
104
        $this->setErrorFromElasticReturn($return);
105
106
        return false;
107
    }
108
109
    /**
110
     * @param string $index
111
     * @param string $type
112
     * @param array $mappings
113
     * @param array|null $return
114
     * @return bool
115
     * @throws ElasticOperationException
116
     */
117
    public function createType($index, $type, array $mappings = [], array &$return = null) {
118
        if (!$this->indexExists($index)) {
119
            throw new \InvalidArgumentException(sprintf("%s' index does not exists", $index));
120
        }
121
122
        if ($this->typeExists($index, $type)) {
123
            throw new \InvalidArgumentException(sprintf("Type 's%' already exists on index %s", $type, $index));
124
        }
125
126
        $mappings = MappingHelper::patchMappings($mappings, floor($this->getElasticsearchVersion()));
127
128
        $url = "$index/_mapping/$type";
129
        $response = $this->curlRequest->request($url, $mappings, 'PUT');
130
131
        $this->throwExceptionFromResponse($response, "Error creating type '$type' in '$index' index");
132
133
        $return = $response['content'];
134
135
        if (isset($return['acknowledged'])) {
136
            return $return['acknowledged'];
137
        }
138
139
        $this->setErrorFromElasticReturn($return);
140
141
        return false;
142
    }
143
144
    /**
145
     * @param string $index
146
     * @param string $type
147
     * @param array $body
148
     * @param array $queryParams
149
     * @param array|null $return
150
     * @return bool
151
     */
152
    public function insert($index, $type, array $body, array $queryParams = [], array &$return = null) {
153
        $url = "$index/$type";
154
        if (isset($body['_id'])) {
155
            $url .= '/' . $body['_id'];
156
            unset($body['_id']);
157
        }
158
159
        $url = "$url?" . http_build_query(array_merge(['refresh' => "true"], $queryParams));
160
161
        $response = $this->curlRequest->request($url, $body, 'POST');
162
163
        $this->throwExceptionFromResponse($response);
164
        $return = $response['content'];
165
166
        if (isset($return['created'])) {
167
            return $return['created'];
168
        }
169
170
        $this->setErrorFromElasticReturn($return);
171
172
        return false;
173
    }
174
175
    /**
176
     * @param string $index
177
     * @param string $type
178
     * @param string $_id
179
     * @param array $body
180
     * @param array $queryParams
181
     * @param array|null $return
182
     *
183
     * @return bool
184
     */
185
    public function update($index, $type, $_id, array $body = [], array $queryParams = [], array &$return = null) {
186
        if (!$this->indexExists($index)) {
187
            return false;
188
        }
189
190
        if (array_key_exists('doc', $body)) {
191
            $params = $body;
192
        } else {
193
            $params = ['doc' => $body];
194
        }
195
196
        $url = "$index/$type/$_id/_update?" . http_build_query(array_merge(['refresh' => 'true'], $queryParams));
197
        $response = $this->curlRequest->request($url, $params, 'POST');
198
        $this->throwExceptionFromResponse($response);
199
200
        $return = $response['content'];
201
202
        if (isset($return['_id'])) {
203
            return true;
204
        }
205
206
        $this->setErrorFromElasticReturn($return);
207
208
        return false;
209
    }
210
211
    /**
212
     * @param string $index
213
     * @param string $type
214
     * @param string $_id
215
     * @param array $queryParams
216
     * @param array|null $return
217
     * @return bool
218
     */
219
    public function delete($index, $type, $_id, array $queryParams = [], array &$return = null) {
220
        if (!$this->indexExists($index)) {
221
            return false;
222
        }
223
224
        $url = "$index/$type/$_id?" . http_build_query(array_merge(['refresh' => 'true'], $queryParams));
225
        $response = $this->curlRequest->request($url, [], 'DELETE');
226
        $this->throwExceptionFromResponse($response);
227
        $return = $response['content'];
228
229
        if (isset($return['found']) && !$return['found']) {
230
            error_log("Doc with _id '$_id' was not found for delete. Index: '$index', Type: '$type' ");
231
        }
232
233
        if (isset($return['_id'])) {
234
            return true;
235
        }
236
237
        $this->setErrorFromElasticReturn($return);
238
239
        return false;
240
    }
241
242
    public function updateWhere($index, $type, array $where, array &$return = null) {
243
        // TODO
244
    }
245
246
    public function deleteWhere($index, $type, array $where, array &$return = null) {
247
        // TODO
248
    }
249
250
    /**
251
     *
252
     * @param string $index
253
     * @param string $type
254
     * @param string $_id
255
     * @param array $queryParams
256
     * @param array|null $return
257
     * @return array|null
258
     */
259
    public function get($index, $type, $_id, array $queryParams = [], array &$return = null) {
260
        if (!$this->indexExists($index)) {
261
            return null;
262
        }
263
264
        $url = "$index/$type/$_id";
265
        $response = $this->curlRequest->request($url, [], 'GET');
266
        $return = $response['content'];
267
268
        if ($response['status'] == 404) {
269
            return null;
270
        }
271
272
        if (isset($return['found']) && boolval($return['found'])) {
273
            return $return;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $return; (object|integer|double|string|array|boolean) is incompatible with the return type declared by the interface DoctrineElastic\Connecti...onnectionInterface::get of type array|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
274
        }
275
276
        return null;
277
    }
278
279
    /**
280
     * Returns the [hits][hits] array from query
281
     *
282
     * @param string $index
283
     * @param string $type
284
     * @param array $body
285
     * @param array $queryParams
286
     * @param array|null $return
287
     * @return array
288
     */
289
    public function search($index, $type, array $body = [], array $queryParams = [], array &$return = null) {
290
        if (!$this->indexExists($index)) {
291
            return [];
292
        }
293
294
        $this->unsetEmpties($body);
295
296
        if (isset($body['query']) && empty($body['query'])) {
297
            unset($body['query']);
298
        }
299
300
301
        $url = "$index/$type/_search";
302
        $response = $this->curlRequest->request($url, $body, 'POST');
303
        $this->throwExceptionFromResponse($response);
304
        $return = $response['content'];
305
306
        if (isset($return['hits']['hits'])) {
307
            return $return['hits']['hits'];
308
        }
309
310
        return [];
311
    }
312
313
    private function unsetEmpties(array &$array, array &$parent = null) {
314
        if (!is_array($array)) {
315
            return null;
316
        }
317
318
        for ($count = 2; $count > 0; $count--) {
319
            foreach ($array as $key => $item) {
0 ignored issues
show
Bug introduced by
The expression $array of type null|array 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...
320
                if (is_array($item) && empty($item)) {
321
                    unset($array[$key]);
322
323
                    if (is_array($parent)) {
324
                        $this->unsetEmpties($parent);
325
                    }
326
                } else if (is_array($item)) {
327
                    $this->unsetEmpties($array[$key], $array);
328
                }
329
            }
330
        }
331
    }
332
333
    /**
334
     * @param string $index
335
     * @return bool
336
     */
337
    public function indexExists($index) {
338
        $response = $this->curlRequest->request($index, [], 'HEAD');
339
340
        return $response['status'] === 200;
341
    }
342
343
    /**
344
     * @param string $index
345
     * @param string $type
346
     * @return bool
347
     */
348
    public function typeExists($index, $type) {
349
        $response = $this->curlRequest->request("$index/$type", [], 'HEAD');
350
351
        return $response['status'] === 200;
352
    }
353
354
    private function throwExceptionFromResponse($response, $appendPrefix = '') {
355
        if (isset($response['content']['error']['reason'])) {
356
            if (!empty($appendPrefix)) {
357
                $appendPrefix .= ': ';
358
            }
359
360
            throw new ElasticOperationException($appendPrefix . $response['content']['error']['reason']);
361
        }
362
    }
363
364
    public function hasConnection() {
365
        $response = $this->curlRequest->request('', [], 'HEAD');
366
367
        return $response['status'] == 200;
368
    }
369
370
    private function setErrorFromElasticReturn($return) {
371
        if (isset($return['error']['root_cause'][0]['reason'])) {
372
            $this->setError($return['error']['root_cause'][0]['reason']);
373
        } else if (isset($return['error']['reason'])) {
374
            $this->setError($return['error']['reason']);
375
        }
376
    }
377
378
    public function getElasticsearchVersion() {
379
        if (is_null($this->esVersion)) {
380
            $response = $this->curlRequest->request('', [], 'GET');
381
382
            if (isset($response['content']['version']['number'])) {
383
                $this->esVersion = floatval($response['content']['version']['number']);
384
            } else {
385
                throw new ConnectionException('Unable to fetch elasticsearch version. ');
386
            }
387
        }
388
389
        return $this->esVersion;
390
    }
391
}
392