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 ( db31ee...a5f74a )
by Anderson
05:41
created

ElasticConnection::get()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 10
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 (boolval($mappings)) {
59
            $params['mappings'] = MappingHelper::patchMappings($mappings, floor($this->getElasticsearchVersion()));
0 ignored issues
show
Bug introduced by
It seems like $mappings defined by parameter $mappings on line 50 can also be of type null; however, DoctrineElastic\Helper\M...Helper::patchMappings() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
60
        }
61
62
        if (boolval($settings)) {
63
            $params['settings'] = $settings;
64
        }
65
66
        if (boolval($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, [], '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
        $response = $this->curlRequest->request("$index/_mapping/$type", $mappings, 'PUT');
129
130
        $this->throwExceptionFromResponse($response, "Error creating type '$type' in '$index' index");
131
132
        $return = $response['content'];
133
134
        if (isset($return['acknowledged'])) {
135
            return $return['acknowledged'];
136
        }
137
138
        $this->setErrorFromElasticReturn($return);
139
140
        return false;
141
    }
142
143
    /**
144
     * @param string $index
145
     * @param string $type
146
     * @param array $body
147
     * @param array $mergeParams
148
     * @param array|null $return
149
     * @return bool
150
     */
151
    public function insert($index, $type, array $body, array $mergeParams = [], array &$return = null) {
152
        if (!$this->indexExists($index)) {
153
            trigger_error("$index index does not exists at insert attempt");
154
            return false;
155
        }
156
157
        if (!$this->typeExists($index, $type)) {
158
            trigger_error("$type type does not exists at insert attempt");
159
            return false;
160
        }
161
162
        $url = "$index/$type";
163
        if (isset($body['_id'])) {
164
            $url .= '/' . $body['_id'];
165
            unset($body['_id']);
166
        }
167
168
        if (!empty($mergeParams)) {
169
            $url = "$url?" . http_build_query($mergeParams);
170
        }
171
172
        $response = $this->curlRequest->request($url, $body, 'POST');
173
174
        $this->throwExceptionFromResponse($response);
175
        $return = $response['content'];
176
177
        if (isset($return['created'])) {
178
            return $return['created'];
179
        }
180
181
        $this->setErrorFromElasticReturn($return);
182
183
        return false;
184
    }
185
186
    /**
187
     * @param string $index
188
     * @param string $type
189
     * @param string $_id
190
     * @param array $body
191
     * @param array $mergeParams
192
     * @param array|null $return
193
     *
194
     * @return bool
195
     */
196
    public function update($index, $type, $_id, array $body = [], array $mergeParams = [], array &$return = null) {
197
        if (!$this->indexExists($index)) {
198
            return false;
199
        }
200
201
        if (array_key_exists('doc', $body)) {
202
            $params = $body;
203
        } else {
204
            $params = ['doc' => $body];
205
        }
206
207
        $response = $this->curlRequest->request("$index/$type/$_id/_update", $params, 'POST');
208
        $this->throwExceptionFromResponse($response);
209
210
        $return = $response['content'];
211
212
        if (isset($return['_id'])) {
213
            return true;
214
        }
215
216
        $this->setErrorFromElasticReturn($return);
217
218
        return false;
219
    }
220
221
    /**
222
     * @param string $index
223
     * @param string $type
224
     * @param string $_id
225
     * @param array $mergeParams
226
     * @param array|null $return
227
     * @return bool
228
     */
229
    public function delete($index, $type, $_id, array $mergeParams = [], array &$return = null) {
230
        if (!$this->indexExists($index)) {
231
            return false;
232
        }
233
234
        $response = $this->curlRequest->request("$index/$type/$_id", [], 'DELETE');
235
        $this->throwExceptionFromResponse($response);
236
        $return = $response['content'];
237
238
        if (isset($return['found']) && !$return['found']) {
239
            error_log("Doc with _id '$_id' was not found for delete. Index: '$index', Type: '$type' ");
240
        }
241
242
        if (isset($return['_id'])) {
243
            return true;
244
        }
245
246
        $this->setErrorFromElasticReturn($return);
247
248
        return false;
249
    }
250
251
    public function updateWhere($index, $type, array $where, array &$return = null) {
252
        // TODO
253
    }
254
255
    public function deleteWhere($index, $type, array $where, array &$return = null) {
256
        // TODO
257
    }
258
259
    /**
260
     *
261
     * @param string $index
262
     * @param string $type
263
     * @param string $_id
264
     * @param array $mergeParams
265
     * @param array|null $return
266
     * @return array|null
267
     */
268
    public function get($index, $type, $_id, array $mergeParams = [], array &$return = null) {
269
        if (!$this->indexExists($index)) {
270
            return null;
271
        }
272
273
        $response = $this->curlRequest->request("$index/$type/$_id", [], 'GET');
274
        $return = $response['content'];
275
276
        if ($response['status'] == 404) {
277
            return null;
278
        }
279
280
        if (isset($return['found']) && boolval($return['found'])) {
281
            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...
282
        }
283
284
        return null;
285
    }
286
287
    /**
288
     * Returns the [hits][hits] array from query
289
     *
290
     * @param string $index
291
     * @param string $type
292
     * @param array $body
293
     * @param array $mergeParams
294
     * @param array|null $return
295
     * @return array
296
     */
297
    public function search($index, $type, array $body = [], array $mergeParams = [], array &$return = null) {
298
        if (!$this->indexExists($index)) {
299
            return [];
300
        }
301
302
        $this->unsetEmpties($body);
303
304
        if (isset($body['query']) && empty($body['query'])) {
305
            unset($body['query']);
306
        }
307
308
        $response = $this->curlRequest->request("$index/$type/_search", $body, 'POST');
309
        $this->throwExceptionFromResponse($response);
310
        $return = $response['content'];
311
312
        if (isset($return['hits']['hits'])) {
313
            return $return['hits']['hits'];
314
        }
315
316
        return [];
317
    }
318
319
    private function unsetEmpties(array &$array, array &$parent = null) {
320
        for ($count = 2; $count > 0; $count--) {
321
            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...
322
                if (is_array($item) && empty($item)) {
323
                    unset($array[$key]);
324
325
                    if (is_array($parent)) {
326
                        $this->unsetEmpties($parent);
327
                    }
328
                } else if (is_array($item)) {
329
                    $this->unsetEmpties($array[$key], $array);
330
                }
331
            }
332
        }
333
    }
334
335
    /**
336
     * @param string $index
337
     * @return bool
338
     */
339
    public function indexExists($index) {
340
        $response = $this->curlRequest->request($index, [], 'HEAD');
341
342
        return $response['status'] === 200;
343
    }
344
345
    /**
346
     * @param string $index
347
     * @param string $type
348
     * @return bool
349
     */
350
    public function typeExists($index, $type) {
351
        $response = $this->curlRequest->request("$index/$type", [], 'HEAD');
352
353
        return $response['status'] === 200;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $response['status'] === 200; (boolean) is incompatible with the return type declared by the interface DoctrineElastic\Connecti...onInterface::typeExists of type array.

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...
354
    }
355
356
    private function throwExceptionFromResponse($response, $appendPrefix = '') {
357
        if (isset($response['content']['error']['reason'])) {
358
            if (!empty($appendPrefix)) {
359
                $appendPrefix .= ': ';
360
            }
361
362
            throw new ElasticOperationException($appendPrefix . $response['content']['error']['reason']);
363
        }
364
    }
365
366
    public function hasConnection() {
367
        $response = $this->curlRequest->request('', [], 'HEAD');
368
369
        return $response['status'] == 200;
370
    }
371
372
    private function setErrorFromElasticReturn($return) {
373
        if (isset($return['error']['root_cause'][0]['reason'])) {
374
            $this->setError($return['error']['root_cause'][0]['reason']);
375
        } else if (isset($return['error']['reason'])) {
376
            $this->setError($return['error']['reason']);
377
        }
378
    }
379
380
    public function getElasticsearchVersion() {
381
        if (is_null($this->esVersion)) {
382
            $response = $this->curlRequest->request('', [], 'GET');
383
384
            if (isset($response['content']['version']['number'])) {
385
                $this->esVersion = floatval($response['content']['version']['number']);
386
            } else {
387
                throw new ConnectionException('Unable to fetch elasticsearch version. ');
388
            }
389
        }
390
391
        return $this->esVersion;
392
    }
393
}
394