1
|
|
|
<?php |
2
|
|
|
namespace Elastica; |
3
|
|
|
|
4
|
|
|
use Elastica\Exception\InvalidException; |
5
|
|
|
use Elastica\Exception\ResponseException; |
6
|
|
|
use Elastica\Index\Settings as IndexSettings; |
7
|
|
|
use Elastica\Index\Stats as IndexStats; |
8
|
|
|
use Elastica\ResultSet\BuilderInterface; |
9
|
|
|
use Elasticsearch\Endpoints\AbstractEndpoint; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Elastica index object. |
13
|
|
|
* |
14
|
|
|
* Handles reads, deletes and configurations of an index |
15
|
|
|
* |
16
|
|
|
* @author Nicolas Ruflin <[email protected]> |
17
|
|
|
*/ |
18
|
|
|
class Index implements SearchableInterface |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* Index name. |
22
|
|
|
* |
23
|
|
|
* @var string Index name |
24
|
|
|
*/ |
25
|
|
|
protected $_name; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Client object. |
29
|
|
|
* |
30
|
|
|
* @var \Elastica\Client Client object |
31
|
|
|
*/ |
32
|
|
|
protected $_client; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Creates a new index object. |
36
|
|
|
* |
37
|
|
|
* All the communication to and from an index goes of this object |
38
|
|
|
* |
39
|
|
|
* @param \Elastica\Client $client Client object |
40
|
|
|
* @param string $name Index name |
41
|
|
|
*/ |
42
|
|
View Code Duplication |
public function __construct(Client $client, $name) |
|
|
|
|
43
|
|
|
{ |
44
|
|
|
$this->_client = $client; |
45
|
|
|
|
46
|
|
|
if (!is_scalar($name)) { |
47
|
|
|
throw new InvalidException('Index name should be a scalar type'); |
48
|
|
|
} |
49
|
|
|
$this->_name = (string) $name; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Returns a type object for the current index with the given name. |
54
|
|
|
* |
55
|
|
|
* @param string $type Type name |
56
|
|
|
* |
57
|
|
|
* @return \Elastica\Type Type object |
58
|
|
|
*/ |
59
|
|
|
public function getType($type) |
60
|
|
|
{ |
61
|
|
|
return new Type($this, $type); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Return Index Stats. |
66
|
|
|
* |
67
|
|
|
* @return \Elastica\Index\Stats |
68
|
|
|
*/ |
69
|
|
|
public function getStats() |
70
|
|
|
{ |
71
|
|
|
return new IndexStats($this); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Gets all the type mappings for an index. |
76
|
|
|
* |
77
|
|
|
* @return array |
78
|
|
|
*/ |
79
|
|
View Code Duplication |
public function getMapping() |
|
|
|
|
80
|
|
|
{ |
81
|
|
|
$path = '_mapping'; |
82
|
|
|
|
83
|
|
|
$response = $this->request($path, Request::GET); |
84
|
|
|
$data = $response->getData(); |
85
|
|
|
|
86
|
|
|
// Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name |
87
|
|
|
$mapping = array_shift($data); |
88
|
|
|
|
89
|
|
|
if (isset($mapping['mappings'])) { |
90
|
|
|
return $mapping['mappings']; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
return []; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Returns the index settings object. |
98
|
|
|
* |
99
|
|
|
* @return \Elastica\Index\Settings Settings object |
100
|
|
|
*/ |
101
|
|
|
public function getSettings() |
102
|
|
|
{ |
103
|
|
|
return new IndexSettings($this); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Uses _bulk to send documents to the server. |
108
|
|
|
* |
109
|
|
|
* @param array|\Elastica\Document[] $docs Array of Elastica\Document |
110
|
|
|
* |
111
|
|
|
* @return \Elastica\Bulk\ResponseSet |
112
|
|
|
* |
113
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html |
114
|
|
|
*/ |
115
|
|
|
public function updateDocuments(array $docs) |
116
|
|
|
{ |
117
|
|
|
foreach ($docs as $doc) { |
118
|
|
|
$doc->setIndex($this->getName()); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return $this->getClient()->updateDocuments($docs); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Uses _bulk to send documents to the server. |
126
|
|
|
* |
127
|
|
|
* @param array|\Elastica\Document[] $docs Array of Elastica\Document |
128
|
|
|
* |
129
|
|
|
* @return \Elastica\Bulk\ResponseSet |
130
|
|
|
* |
131
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html |
132
|
|
|
*/ |
133
|
|
|
public function addDocuments(array $docs) |
134
|
|
|
{ |
135
|
|
|
foreach ($docs as $doc) { |
136
|
|
|
$doc->setIndex($this->getName()); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
return $this->getClient()->addDocuments($docs); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Deletes entries in the db based on a query. |
144
|
|
|
* |
145
|
|
|
* @param \Elastica\Query|string|array $query Query object or array |
146
|
|
|
* @param array $options Optional params |
147
|
|
|
* |
148
|
|
|
* @return \Elastica\Response |
149
|
|
|
* |
150
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/5.0/docs-delete-by-query.html |
151
|
|
|
*/ |
152
|
|
|
public function deleteByQuery($query, array $options = []) |
153
|
|
|
{ |
154
|
|
|
$query = Query::create($query)->getQuery(); |
155
|
|
|
|
156
|
|
|
return $this->request('_delete_by_query', Request::POST, ['query' => is_array($query) ? $query : $query->toArray()], $options); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Deletes the index. |
161
|
|
|
* |
162
|
|
|
* @return \Elastica\Response Response object |
163
|
|
|
*/ |
164
|
|
|
public function delete() |
165
|
|
|
{ |
166
|
|
|
$response = $this->request('', Request::DELETE); |
167
|
|
|
|
168
|
|
|
return $response; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Uses _bulk to delete documents from the server. |
173
|
|
|
* |
174
|
|
|
* @param array|\Elastica\Document[] $docs Array of Elastica\Document |
175
|
|
|
* |
176
|
|
|
* @return \Elastica\Bulk\ResponseSet |
177
|
|
|
* |
178
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html |
179
|
|
|
*/ |
180
|
|
|
public function deleteDocuments(array $docs) |
181
|
|
|
{ |
182
|
|
|
foreach ($docs as $doc) { |
183
|
|
|
$doc->setIndex($this->getName()); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
return $this->getClient()->deleteDocuments($docs); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Optimizes search index. |
191
|
|
|
* |
192
|
|
|
* Detailed arguments can be found here in the link |
193
|
|
|
* |
194
|
|
|
* @param array $args OPTIONAL Additional arguments |
195
|
|
|
* |
196
|
|
|
* @return array Server response |
197
|
|
|
* |
198
|
|
|
* @deprecated Replaced by forcemerge |
199
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-optimize.html |
200
|
|
|
*/ |
201
|
|
|
public function optimize($args = []) |
202
|
|
|
{ |
203
|
|
|
trigger_error('Deprecated: Elastica\Index::optimize() is deprecated and will be removed in further Elastica releases. Use Elastica\Index::forcemerge() instead.', E_USER_DEPRECATED); |
204
|
|
|
|
205
|
|
|
return $this->forcemerge($args); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Force merges index. |
210
|
|
|
* |
211
|
|
|
* Detailed arguments can be found here in the link |
212
|
|
|
* |
213
|
|
|
* @param array $args OPTIONAL Additional arguments |
214
|
|
|
* |
215
|
|
|
* @return array Server response |
216
|
|
|
* |
217
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html |
218
|
|
|
*/ |
219
|
|
|
public function forcemerge($args = []) |
220
|
|
|
{ |
221
|
|
|
return $this->request('_forcemerge', Request::POST, [], $args); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Refreshes the index. |
226
|
|
|
* |
227
|
|
|
* @return \Elastica\Response Response object |
228
|
|
|
* |
229
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html |
230
|
|
|
*/ |
231
|
|
|
public function refresh() |
232
|
|
|
{ |
233
|
|
|
return $this->request('_refresh', Request::POST, []); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Creates a new index with the given arguments. |
238
|
|
|
* |
239
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html |
240
|
|
|
* |
241
|
|
|
* @param array $args OPTIONAL Arguments to use |
242
|
|
|
* @param bool|array $options OPTIONAL |
243
|
|
|
* bool=> Deletes index first if already exists (default = false). |
244
|
|
|
* array => Associative array of options (option=>value) |
245
|
|
|
* |
246
|
|
|
* @throws \Elastica\Exception\InvalidException |
247
|
|
|
* @throws \Elastica\Exception\ResponseException |
248
|
|
|
* |
249
|
|
|
* @return \Elastica\Response Server response |
250
|
|
|
*/ |
251
|
|
|
public function create(array $args = [], $options = null) |
252
|
|
|
{ |
253
|
|
|
if (is_bool($options) && $options) { |
254
|
|
|
try { |
255
|
|
|
$this->delete(); |
256
|
|
|
} catch (ResponseException $e) { |
257
|
|
|
// Table can't be deleted, because doesn't exist |
258
|
|
|
} |
259
|
|
|
} elseif (is_array($options)) { |
260
|
|
|
foreach ($options as $key => $value) { |
261
|
|
|
switch ($key) { |
262
|
|
|
case 'recreate': |
263
|
|
|
try { |
264
|
|
|
$this->delete(); |
265
|
|
|
} catch (ResponseException $e) { |
266
|
|
|
// Table can't be deleted, because doesn't exist |
267
|
|
|
} |
268
|
|
|
break; |
269
|
|
|
default: |
270
|
|
|
throw new InvalidException('Invalid option '.$key); |
271
|
|
|
break; |
|
|
|
|
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
return $this->request('', Request::PUT, $args); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Checks if the given index is already created. |
281
|
|
|
* |
282
|
|
|
* @return bool True if index exists |
283
|
|
|
*/ |
284
|
|
|
public function exists() |
285
|
|
|
{ |
286
|
|
|
$response = $this->getClient()->request($this->getName(), Request::HEAD); |
287
|
|
|
|
288
|
|
|
return $response->getStatus() === 200; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* @param string|array|\Elastica\Query $query |
293
|
|
|
* @param int|array $options |
294
|
|
|
* @param BuilderInterface $builder |
295
|
|
|
* |
296
|
|
|
* @return Search |
297
|
|
|
*/ |
298
|
|
|
public function createSearch($query = '', $options = null, BuilderInterface $builder = null) |
299
|
|
|
{ |
300
|
|
|
$search = new Search($this->getClient(), $builder); |
301
|
|
|
$search->addIndex($this); |
302
|
|
|
$search->setOptionsAndQuery($options, $query); |
303
|
|
|
|
304
|
|
|
return $search; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Searches in this index. |
309
|
|
|
* |
310
|
|
|
* @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object |
311
|
|
|
* @param int|array $options OPTIONAL Limit or associative array of options (option=>value) |
312
|
|
|
* |
313
|
|
|
* @return \Elastica\ResultSet with all results inside |
314
|
|
|
* |
315
|
|
|
* @see \Elastica\SearchableInterface::search |
316
|
|
|
*/ |
317
|
|
|
public function search($query = '', $options = null) |
318
|
|
|
{ |
319
|
|
|
$search = $this->createSearch($query, $options); |
320
|
|
|
|
321
|
|
|
return $search->search(); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Counts results of query. |
326
|
|
|
* |
327
|
|
|
* @param string|array|\Elastica\Query $query Array with all query data inside or a Elastica\Query object |
328
|
|
|
* |
329
|
|
|
* @return int number of documents matching the query |
330
|
|
|
* |
331
|
|
|
* @see \Elastica\SearchableInterface::count |
332
|
|
|
*/ |
333
|
|
|
public function count($query = '') |
334
|
|
|
{ |
335
|
|
|
$search = $this->createSearch($query); |
336
|
|
|
|
337
|
|
|
return $search->count(); |
|
|
|
|
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Opens an index. |
342
|
|
|
* |
343
|
|
|
* @return \Elastica\Response Response object |
344
|
|
|
* |
345
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html |
346
|
|
|
*/ |
347
|
|
|
public function open() |
348
|
|
|
{ |
349
|
|
|
return $this->request('_open', Request::POST); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Closes the index. |
354
|
|
|
* |
355
|
|
|
* @return \Elastica\Response Response object |
356
|
|
|
* |
357
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html |
358
|
|
|
*/ |
359
|
|
|
public function close() |
360
|
|
|
{ |
361
|
|
|
return $this->request('_close', Request::POST); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Returns the index name. |
366
|
|
|
* |
367
|
|
|
* @return string Index name |
368
|
|
|
*/ |
369
|
|
|
public function getName() |
370
|
|
|
{ |
371
|
|
|
return $this->_name; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Returns index client. |
376
|
|
|
* |
377
|
|
|
* @return \Elastica\Client Index client object |
378
|
|
|
*/ |
379
|
|
|
public function getClient() |
380
|
|
|
{ |
381
|
|
|
return $this->_client; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* Adds an alias to the current index. |
386
|
|
|
* |
387
|
|
|
* @param string $name Alias name |
388
|
|
|
* @param bool $replace OPTIONAL If set, an existing alias will be replaced |
389
|
|
|
* |
390
|
|
|
* @return \Elastica\Response Response |
391
|
|
|
* |
392
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html |
393
|
|
|
*/ |
394
|
|
|
public function addAlias($name, $replace = false) |
395
|
|
|
{ |
396
|
|
|
$path = '_aliases'; |
397
|
|
|
|
398
|
|
|
$data = ['actions' => []]; |
399
|
|
|
|
400
|
|
|
if ($replace) { |
401
|
|
|
$status = new Status($this->getClient()); |
402
|
|
|
foreach ($status->getIndicesWithAlias($name) as $index) { |
403
|
|
|
$data['actions'][] = ['remove' => ['index' => $index->getName(), 'alias' => $name]]; |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
$data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]]; |
408
|
|
|
|
409
|
|
|
return $this->getClient()->request($path, Request::POST, $data); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Removes an alias pointing to the current index. |
414
|
|
|
* |
415
|
|
|
* @param string $name Alias name |
416
|
|
|
* |
417
|
|
|
* @return \Elastica\Response Response |
418
|
|
|
* |
419
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html |
420
|
|
|
*/ |
421
|
|
|
public function removeAlias($name) |
422
|
|
|
{ |
423
|
|
|
$path = '_aliases'; |
424
|
|
|
|
425
|
|
|
$data = ['actions' => [['remove' => ['index' => $this->getName(), 'alias' => $name]]]]; |
426
|
|
|
|
427
|
|
|
return $this->getClient()->request($path, Request::POST, $data); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
/** |
431
|
|
|
* Returns all index aliases. |
432
|
|
|
* |
433
|
|
|
* @return array Aliases |
434
|
|
|
*/ |
435
|
|
|
public function getAliases() |
436
|
|
|
{ |
437
|
|
|
$responseData = $this->request('_alias/*', Request::GET)->getData(); |
438
|
|
|
|
439
|
|
|
if (!isset($responseData[$this->getName()])) { |
440
|
|
|
return []; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
$data = $responseData[$this->getName()]; |
444
|
|
|
if (!empty($data['aliases'])) { |
445
|
|
|
return array_keys($data['aliases']); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
return []; |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
/** |
452
|
|
|
* Checks if the index has the given alias. |
453
|
|
|
* |
454
|
|
|
* @param string $name Alias name |
455
|
|
|
* |
456
|
|
|
* @return bool |
457
|
|
|
*/ |
458
|
|
|
public function hasAlias($name) |
459
|
|
|
{ |
460
|
|
|
return in_array($name, $this->getAliases()); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Clears the cache of an index. |
465
|
|
|
* |
466
|
|
|
* @return \Elastica\Response Response object |
467
|
|
|
* |
468
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html |
469
|
|
|
*/ |
470
|
|
|
public function clearCache() |
471
|
|
|
{ |
472
|
|
|
$path = '_cache/clear'; |
473
|
|
|
// TODO: add additional cache clean arguments |
474
|
|
|
return $this->request($path, Request::POST); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* Flushes the index to storage. |
479
|
|
|
* |
480
|
|
|
* @param array $options |
481
|
|
|
* |
482
|
|
|
* @return Response Response object |
483
|
|
|
* |
484
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html |
485
|
|
|
*/ |
486
|
|
|
public function flush(array $options = []) |
487
|
|
|
{ |
488
|
|
|
$path = '_flush'; |
489
|
|
|
|
490
|
|
|
return $this->request($path, Request::POST, [], $options); |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
/** |
494
|
|
|
* Can be used to change settings during runtime. One example is to use it for bulk updating. |
495
|
|
|
* |
496
|
|
|
* @param array $data Data array |
497
|
|
|
* |
498
|
|
|
* @return \Elastica\Response Response object |
499
|
|
|
* |
500
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html |
501
|
|
|
*/ |
502
|
|
|
public function setSettings(array $data) |
503
|
|
|
{ |
504
|
|
|
return $this->request('_settings', Request::PUT, $data); |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
/** |
508
|
|
|
* Makes calls to the elasticsearch server based on this index. |
509
|
|
|
* |
510
|
|
|
* @param string $path Path to call |
511
|
|
|
* @param string $method Rest method to use (GET, POST, DELETE, PUT) |
512
|
|
|
* @param array|string $data OPTIONAL Arguments as array or encoded string |
513
|
|
|
* @param array $query OPTIONAL Query params |
514
|
|
|
* |
515
|
|
|
* @return \Elastica\Response Response object |
516
|
|
|
*/ |
517
|
|
|
public function request($path, $method, $data = [], array $query = []) |
518
|
|
|
{ |
519
|
|
|
$path = $this->getName().'/'.$path; |
520
|
|
|
|
521
|
|
|
return $this->getClient()->request($path, $method, $data, $query); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* Makes calls to the elasticsearch server with usage official client Endpoint based on this index |
526
|
|
|
* |
527
|
|
|
* @param AbstractEndpoint $endpoint |
528
|
|
|
* @return Response |
529
|
|
|
*/ |
530
|
|
|
public function requestEndpoint(AbstractEndpoint $endpoint) |
531
|
|
|
{ |
532
|
|
|
$cloned = clone $endpoint; |
533
|
|
|
$cloned->setIndex($this->getName()); |
534
|
|
|
return $this->getClient()->requestEndpoint($cloned); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Analyzes a string. |
539
|
|
|
* |
540
|
|
|
* Detailed arguments can be found here in the link |
541
|
|
|
* |
542
|
|
|
* @param string $text String to be analyzed |
543
|
|
|
* @param array $args OPTIONAL Additional arguments |
544
|
|
|
* |
545
|
|
|
* @return array Server response |
546
|
|
|
* |
547
|
|
|
* @link https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html |
548
|
|
|
*/ |
549
|
|
|
public function analyze($text, $args = []) |
550
|
|
|
{ |
551
|
|
|
$data = $this->request('_analyze', Request::POST, $text, $args)->getData(); |
552
|
|
|
|
553
|
|
|
// Support for "Explain" parameter, that returns a different response structure from Elastic |
554
|
|
|
// @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html |
555
|
|
|
if (isset($args['explain']) && $args['explain']) { |
556
|
|
|
return $data['detail']; |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
return $data['tokens']; |
560
|
|
|
} |
561
|
|
|
} |
562
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.