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 ( a4e5c9...2c0076 )
by Anderson
02:35
created

ElasticConnection::search()   C

Complexity

Conditions 12
Paths 9

Size

Total Lines 49
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 49
rs 5.1474
c 0
b 0
f 0
cc 12
eloc 28
nc 9
nop 5

How to fix   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
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
        $url = "$index/$type/_search";
301
302
        $cleanQuery = function ($queryPart, callable $recusiveFn) {
303
            if (!is_array($queryPart)) {
304
                return $queryPart;
305
            }
306
307
            foreach ($queryPart as $key => $item) {
308
                if ($key == 'query' && isset($queryPart['query']['bool'])) {
309
                    $queryPart['bool'] = $recusiveFn($queryPart['query']['bool'], $recusiveFn);
310
                    unset($queryPart['query']['bool']);
311
                }
312
313
                if (isset($item['query']['bool'])) {
314
                    $queryPart[$key]['bool'] = $recusiveFn($item['query']['bool'], $recusiveFn);
315
                    unset($queryPart[$key]['query']);
316
                } else if (is_array($item)) {
317
                    $queryPart[$key] = $recusiveFn($item, $recusiveFn);
318
                }
319
            }
320
321
            return $queryPart;
322
        };
323
324
        foreach ($body['query']['bool'] as $key => $item) {
325
            $body['query']['bool'][$key] = $cleanQuery($item, $cleanQuery);
326
        }
327
328
        $response = $this->curlRequest->request($url, $body, 'POST');
329
        $this->throwExceptionFromResponse($response);
330
        $return = $response['content'];
331
332
        if (isset($return['hits']['hits'])) {
333
            return $return['hits']['hits'];
334
        }
335
336
        return [];
337
    }
338
339
    private function unsetEmpties(array &$array, array &$parent = null) {
340
        if (!is_array($array)) {
341
            return null;
342
        }
343
344
        for ($count = 2; $count > 0; $count--) {
345
            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...
346
                if (is_array($item) && empty($item)) {
347
                    unset($array[$key]);
348
349
                    if (is_array($parent)) {
350
                        $this->unsetEmpties($parent);
351
                    }
352
                } else if (is_array($item)) {
353
                    $this->unsetEmpties($array[$key], $array);
354
                }
355
            }
356
        }
357
    }
358
359
    /**
360
     * @param string $index
361
     * @return bool
362
     */
363
    public function indexExists($index) {
364
        $response = $this->curlRequest->request($index, [], 'HEAD');
365
366
        return $response['status'] === 200;
367
    }
368
369
    /**
370
     * @param string $index
371
     * @param string $type
372
     * @return bool
373
     */
374
    public function typeExists($index, $type) {
375
        $response = $this->curlRequest->request("$index/$type", [], 'HEAD');
376
377
        return $response['status'] === 200;
378
    }
379
380
    private function throwExceptionFromResponse($response, $appendPrefix = '') {
381
        if (isset($response['content']['error']['reason'])) {
382
            if (!empty($appendPrefix)) {
383
                $appendPrefix .= ': ';
384
            }
385
386
            throw new ElasticOperationException($appendPrefix . $response['content']['error']['reason']);
387
        }
388
    }
389
390
    public function hasConnection() {
391
        $response = $this->curlRequest->request('', [], 'HEAD');
392
393
        return $response['status'] == 200;
394
    }
395
396
    private function setErrorFromElasticReturn($return) {
397
        if (isset($return['error']['root_cause'][0]['reason'])) {
398
            $this->setError($return['error']['root_cause'][0]['reason']);
399
        } else if (isset($return['error']['reason'])) {
400
            $this->setError($return['error']['reason']);
401
        }
402
    }
403
404
    public function getElasticsearchVersion() {
405
        if (is_null($this->esVersion)) {
406
            $response = $this->curlRequest->request('', [], 'GET');
407
408
            if (isset($response['content']['version']['number'])) {
409
                $this->esVersion = floatval($response['content']['version']['number']);
410
            } else {
411
                throw new ConnectionException('Unable to fetch elasticsearch version. ');
412
            }
413
        }
414
415
        return $this->esVersion;
416
    }
417
}
418