Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

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.
Passed
Pull Request — master (#673)
by Alexander
02:58
created

DocumentList::moveDown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Common;
14
15
use Kitodo\Dlf\Common\SolrSearchResult\ResultDocument;
16
use Psr\Log\LoggerAwareInterface;
17
use Psr\Log\LoggerAwareTrait;
18
use Solarium\QueryType\Select\Result\Result;
19
use TYPO3\CMS\Core\SingletonInterface;
20
use TYPO3\CMS\Core\Database\ConnectionPool;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Core\Utility\MathUtility;
23
24
/**
25
 * List class for the 'dlf' extension
26
 *
27
 * @author Sebastian Meyer <[email protected]>
28
 * @author Frank Ulrich Weber <[email protected]>
29
 * @package TYPO3
30
 * @subpackage dlf
31
 * @access public
32
 * @property array $metadata This holds the list's metadata
33
 */
34
class DocumentList implements \ArrayAccess, \Countable, \Iterator, LoggerAwareInterface, SingletonInterface
35
{
36
    use LoggerAwareTrait;
37
38
    /**
39
     * This holds the number of documents in the list
40
     * @see \Countable
41
     *
42
     * @var int
43
     * @access protected
44
     */
45
    protected $count = 0;
46
47
    /**
48
     * This holds the list entries in sorted order
49
     * @see \ArrayAccess
50
     *
51
     * @var array
52
     * @access protected
53
     */
54
    protected $elements = [];
55
56
    /**
57
     * This holds the list's metadata
58
     *
59
     * @var array
60
     * @access protected
61
     */
62
    protected $metadata = [];
63
64
    /**
65
     * This holds the current list position
66
     * @see \Iterator
67
     *
68
     * @var int
69
     * @access protected
70
     */
71
    protected $position = 0;
72
73
    /**
74
     * This holds the full records of already processed list elements
75
     *
76
     * @var array
77
     * @access protected
78
     */
79
    protected $records = [];
80
81
    /**
82
     * Instance of \Kitodo\Dlf\Common\Solr class
83
     *
84
     * @var \Kitodo\Dlf\Common\Solr
85
     * @access protected
86
     */
87
    protected $solr;
88
89
    /**
90
     * This holds the Solr metadata configuration
91
     *
92
     * @var array
93
     * @access protected
94
     */
95
    protected $solrConfig = [];
96
97
    /**
98
     * This adds an array of elements at the given position to the list
99
     *
100
     * @access public
101
     *
102
     * @param array $elements: Array of elements to add to list
103
     * @param int $position: Numeric position for including
104
     *
105
     * @return void
106
     */
107
    public function add(array $elements, $position = -1)
108
    {
109
        $position = MathUtility::forceIntegerInRange($position, 0, $this->count, $this->count);
110
        if (!empty($elements)) {
111
            array_splice($this->elements, $position, 0, $elements);
112
            $this->count = count($this->elements);
113
        }
114
    }
115
116
    /**
117
     * This counts the elements
118
     * @see \Countable::count()
119
     *
120
     * @access public
121
     *
122
     * @return int The number of elements in the list
123
     */
124
    public function count()
125
    {
126
        return $this->count;
127
    }
128
129
    /**
130
     * This returns the current element
131
     * @see \Iterator::current()
132
     *
133
     * @access public
134
     *
135
     * @return array The current element
136
     */
137
    public function current()
138
    {
139
        if ($this->valid()) {
140
            return $this->getRecord($this->elements[$this->position]);
141
        } else {
142
            $this->logger->notice('Invalid position "' . $this->position . '" for list element');
1 ignored issue
show
Bug introduced by
The method notice() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
            $this->logger->/** @scrutinizer ignore-call */ 
143
                           notice('Invalid position "' . $this->position . '" for list element');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
143
            return;
144
        }
145
    }
146
147
    /**
148
     * This returns the full record of any list element
149
     *
150
     * @access protected
151
     *
152
     * @param mixed $element: The list element
153
     *
154
     * @return mixed The element's full record
155
     */
156
    protected function getRecord($element)
157
    {
158
        if (
159
            is_array($element)
160
            && array_keys($element) == ['u', 'h', 's', 'p']
161
        ) {
162
            // Return already processed record if possible.
163
            if (!empty($this->records[$element['u']])) {
164
                return $this->records[$element['u']];
165
            }
166
            $record = [
167
                'uid' => $element['u'],
168
                'page' => 1,
169
                'preview' => '',
170
                'subparts' => $element['p']
171
            ];
172
            // Check if it's a list of database records or Solr documents.
173
            if (
174
                !empty($this->metadata['options']['source'])
175
                && $this->metadata['options']['source'] == 'collection'
176
            ) {
177
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
178
                    ->getQueryBuilderForTable('tx_dlf_documents');
179
180
                // Get document's thumbnail and metadata from database.
181
                $result = $queryBuilder
182
                    ->select(
183
                        'tx_dlf_documents.uid AS uid',
184
                        'tx_dlf_documents.thumbnail AS thumbnail',
185
                        'tx_dlf_documents.metadata AS metadata'
186
                    )
187
                    ->from('tx_dlf_documents')
188
                    ->where(
189
                        $queryBuilder->expr()->orX(
190
                            $queryBuilder->expr()->eq('tx_dlf_documents.uid', intval($record['uid'])),
191
                            $queryBuilder->expr()->eq('tx_dlf_documents.partof', intval($record['uid']))
192
                        ),
193
                        Helper::whereExpression('tx_dlf_documents')
194
                    )
195
                    ->execute();
196
197
                // Process results.
198
                while ($resArray = $result->fetch()) {
199
                    // Prepare document's metadata.
200
                    $metadata = unserialize($resArray['metadata']);
201
                    if (
202
                        !empty($metadata['type'][0])
203
                        && MathUtility::canBeInterpretedAsInteger($metadata['type'][0])
204
                    ) {
205
                        $metadata['type'][0] = Helper::getIndexNameFromUid($metadata['type'][0], 'tx_dlf_structures', $this->metadata['options']['pid']);
206
                    }
207
                    if (
208
                        !empty($metadata['owner'][0])
209
                        && MathUtility::canBeInterpretedAsInteger($metadata['owner'][0])
210
                    ) {
211
                        $metadata['owner'][0] = Helper::getIndexNameFromUid($metadata['owner'][0], 'tx_dlf_libraries', $this->metadata['options']['pid']);
212
                    }
213
                    if (
214
                        !empty($metadata['collection'])
215
                        && is_array($metadata['collection'])
216
                    ) {
217
                        foreach ($metadata['collection'] as $i => $collection) {
218
                            if (MathUtility::canBeInterpretedAsInteger($collection)) {
219
                                $metadata['collection'][$i] = Helper::getIndexNameFromUid($metadata['collection'][$i], 'tx_dlf_collections', $this->metadata['options']['pid']);
220
                            }
221
                        }
222
                    }
223
                    // Add metadata to list element.
224
                    if ($resArray['uid'] == $record['uid']) {
225
                        $record['thumbnail'] = $resArray['thumbnail'];
226
                        $record['metadata'] = $metadata;
227
                    } elseif (($key = array_search(['u' => $resArray['uid']], $record['subparts'], true)) !== false) {
228
                        $record['subparts'][$key] = [
229
                            'uid' => $resArray['uid'],
230
                            'page' => 1,
231
                            'preview' => (!empty($record['subparts'][$key]['h']) ? $record['subparts'][$key]['h'] : ''),
232
                            'thumbnail' => $resArray['thumbnail'],
233
                            'metadata' => $metadata
234
                        ];
235
                    }
236
                }
237
            } elseif (
238
                !empty($this->metadata['options']['source'])
239
                && $this->metadata['options']['source'] == 'search'
240
            ) {
241
                if ($this->solrConnect()) {
242
                    $result = $this->getSolrResult($record);
243
                    $record = $this->getSolrRecord($record, $result);
244
                }
245
            }
246
            // Save record for later usage.
247
            $this->records[$element['u']] = $record;
248
        } else {
249
            $this->logger->notice('No UID of list element to fetch full record');
250
            $record = $element;
251
        }
252
        return $record;
253
    }
254
255
    /**
256
     * It gets SOLR result
257
     *
258
     * @access private
259
     *
260
     * @param array $record: for searched document
261
     *
262
     * @return Result
263
     */
264
    private function getSolrResult($record)
265
    {
266
        $fields = Solr::getFields();
267
268
        $query = $this->solr->service->createSelect();
269
        // Restrict the fields to the required ones
270
        $query->setFields($fields['uid'] . ',' . $fields['id'] . ',' . $fields['toplevel'] . ',' . $fields['thumbnail'] . ',' . $fields['page']);
271
        foreach ($this->solrConfig as $solr_name) {
272
            $query->addField($solr_name);
273
        }
274
        // Set additional query parameters.
275
        // Set reasonable limit for safety reasons.
276
        // We don't expect to get more than 10.000 hits per UID.
277
        $query->setStart(0)->setRows(10000);
278
        // Take over existing filter queries.
279
        $filterQueries = isset($this->metadata['options']['params']['filterquery']) ? $this->metadata['options']['params']['filterquery'] : [];
280
        // Extend filter query to get all documents with the same UID.
281
        foreach ($filterQueries as $key => $value) {
282
            if (isset($value['query'])) {
283
                $filterQuery[$key] = $value['query'] . ' OR ' . $fields['toplevel'] . ':true';
284
                $filterQuery = [
285
                    'key' => $key,
286
                    'query' => $value['query'] . ' OR ' . $fields['toplevel'] . ':true'
287
                ];
288
                $query->addFilterQuery($filterQuery);
289
            }
290
        }
291
        // Add filter query to get all documents with the required uid.
292
        $query->createFilterQuery('uid')->setQuery($fields['uid'] . ':' . Solr::escapeQuery($record['uid']));
293
        // Add sorting.
294
        if (is_array($this->metadata['options']['params']['sort'])) {
295
            foreach ($this->metadata['options']['params']['sort'] as $key => $direction) {
296
                $query->addSort($key, $direction);
297
            }
298
        }
299
        // Set query.
300
        $query->setQuery($this->metadata['options']['select'] . ' OR ' . $fields['toplevel'] . ':true');
301
302
        // If it is a fulltext search, enable highlighting.
303
        if ($this->metadata['fulltextSearch']) {
304
            $query->getHighlighting();
305
        };
306
307
        $solrRequest = $this->solr->service->createRequest($query);
308
309
        // If it is a fulltext search, enable highlighting.
310
        if ($this->metadata['fulltextSearch']) {
311
            // field for which highlighting is going to be performed,
312
            // is required if you want to have OCR highlighting
313
            $solrRequest->addParam('hl.ocr.fl', $fields['fulltext']);
314
            // return the coordinates of highlighted search as absolute coordinates
315
            $solrRequest->addParam('hl.ocr.absoluteHighlights', 'on');
316
            // max amount of snippets for a single page
317
            $solrRequest->addParam('hl.snippets', 20);
318
            // we store the fulltext on page level and can disable this option
319
            $solrRequest->addParam('hl.ocr.trackPages', 'off');
320
        }
321
        // Perform search for all documents with the same uid that either fit to the search or marked as toplevel.
322
        $response = $this->solr->service->executeRequest($solrRequest);
323
        return $this->solr->service->createResult($query, $response);
324
    }
325
326
    /**
327
     * It processes SOLR result into record, which is
328
     * going to be displayed in the frontend list.
329
     *
330
     * @access private
331
     *
332
     * @param array $record: for searched document
333
     * @param Result $result: found in the SOLR index
334
     *
335
     * @return array
336
     */
337
    private function getSolrRecord($record, $result)
338
    {
339
        // If it is a fulltext search, fetch the highlighting results.
340
        if ($this->metadata['fulltextSearch']) {
341
            $data = $result->getData();
342
            $highlighting = $data['ocrHighlighting'];
343
        }
344
345
        // Process results.
346
        foreach ($result as $resArray) {
347
            // Prepare document's metadata.
348
            $metadata = [];
349
            foreach ($this->solrConfig as $index_name => $solr_name) {
350
                if (!empty($resArray->$solr_name)) {
351
                    $metadata[$index_name] = (is_array($resArray->$solr_name) ? $resArray->$solr_name : [$resArray->$solr_name]);
352
                }
353
            }
354
            // Add metadata to list elements.
355
            if ($resArray->toplevel) {
356
                $record['thumbnail'] = $resArray->thumbnail;
357
                $record['metadata'] = $metadata;
358
            } else {
359
                $highlight = '';
360
                if (!empty($highlighting)) {
361
                    $resultDocument = new ResultDocument($resArray, $highlighting, Solr::getFields());
362
                    $highlight = $resultDocument->getSnippets();
363
                }
364
365
                $record['subparts'][$resArray->id] = [
366
                    'uid' => $resArray->uid,
367
                    'page' => $resArray->page,
368
                    'preview' => $highlight,
369
                    'thumbnail' => $resArray->thumbnail,
370
                    'metadata' => $metadata
371
                ];
372
            }
373
        }
374
        return $record;
375
    }
376
377
    /**
378
     * This returns the current position
379
     * @see \Iterator::key()
380
     *
381
     * @access public
382
     *
383
     * @return int The current position
384
     */
385
    public function key()
386
    {
387
        return $this->position;
388
    }
389
390
    /**
391
     * This moves the element at the given position up or down
392
     *
393
     * @access public
394
     *
395
     * @param int $position: Numeric position for moving
396
     * @param int $steps: Amount of steps to move up or down
397
     *
398
     * @return void
399
     */
400
    public function move($position, $steps)
401
    {
402
        $position = intval($position);
403
        // Check if list position is valid.
404
        if (
405
            $position < 0
406
            || $position >= $this->count
407
        ) {
408
            $this->logger->warning('Invalid position "' . $position . '" for element moving');
409
            return;
410
        }
411
        $steps = intval($steps);
412
        // Check if moving given amount of steps is possible.
413
        if (($position + $steps) < 0
414
            || ($position + $steps) >= $this->count
415
        ) {
416
            $this->logger->warning('Invalid steps "' . $steps . '" for moving element at position "' . $position . '"');
417
            return;
418
        }
419
        $element = $this->remove($position);
420
        $this->add([$element], $position + $steps);
421
    }
422
423
    /**
424
     * This moves the element at the given position up
425
     *
426
     * @access public
427
     *
428
     * @param int $position: Numeric position for moving
429
     *
430
     * @return void
431
     */
432
    public function moveUp($position)
433
    {
434
        $this->move($position, -1);
435
    }
436
437
    /**
438
     * This moves the element at the given position down
439
     *
440
     * @access public
441
     *
442
     * @param int $position: Numeric position for moving
443
     *
444
     * @return void
445
     */
446
    public function moveDown($position)
447
    {
448
        $this->move($position, 1);
449
    }
450
451
    /**
452
     * This increments the current list position
453
     * @see \Iterator::next()
454
     *
455
     * @access public
456
     *
457
     * @return void
458
     */
459
    public function next()
460
    {
461
        $this->position++;
462
    }
463
464
    /**
465
     * This checks if an offset exists
466
     * @see \ArrayAccess::offsetExists()
467
     *
468
     * @access public
469
     *
470
     * @param mixed $offset: The offset to check
471
     *
472
     * @return bool Does the given offset exist?
473
     */
474
    public function offsetExists($offset)
475
    {
476
        return isset($this->elements[$offset]);
477
    }
478
479
    /**
480
     * This returns the element at the given offset
481
     * @see \ArrayAccess::offsetGet()
482
     *
483
     * @access public
484
     *
485
     * @param mixed $offset: The offset to return
486
     *
487
     * @return array The element at the given offset
488
     */
489
    public function offsetGet($offset)
490
    {
491
        if ($this->offsetExists($offset)) {
492
            return $this->getRecord($this->elements[$offset]);
493
        } else {
494
            $this->logger->notice('Invalid offset "' . $offset . '" for list element');
495
            return;
496
        }
497
    }
498
499
    /**
500
     * This sets the element at the given offset
501
     * @see \ArrayAccess::offsetSet()
502
     *
503
     * @access public
504
     *
505
     * @param mixed $offset: The offset to set (non-integer offsets will be appended)
506
     * @param mixed $value: The value to set
507
     *
508
     * @return void
509
     */
510
    public function offsetSet($offset, $value)
511
    {
512
        if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($offset)) {
513
            $this->elements[$offset] = $value;
514
        } else {
515
            $this->elements[] = $value;
516
        }
517
        // Re-number the elements.
518
        $this->elements = array_values($this->elements);
519
        $this->count = count($this->elements);
520
    }
521
522
    /**
523
     * This removes the element at the given position from the list
524
     *
525
     * @access public
526
     *
527
     * @param int $position: Numeric position for removing
528
     *
529
     * @return array The removed element
530
     */
531
    public function remove($position)
532
    {
533
        $position = intval($position);
534
        if (
535
            $position < 0
536
            || $position >= $this->count
537
        ) {
538
            $this->logger->warning('Invalid position "' . $position . '" for element removing');
539
            return;
540
        }
541
        $removed = array_splice($this->elements, $position, 1);
542
        $this->count = count($this->elements);
543
        return $this->getRecord($removed[0]);
544
    }
545
546
    /**
547
     * This removes elements at the given range from the list
548
     *
549
     * @access public
550
     *
551
     * @param int $position: Numeric position for start of range
552
     * @param int $length: Numeric position for length of range
553
     *
554
     * @return array The indizes of the removed elements
555
     */
556
    public function removeRange($position, $length)
557
    {
558
        $position = intval($position);
559
        if (
560
            $position < 0
561
            || $position >= $this->count
562
        ) {
563
            $this->logger->warning('Invalid position "' . $position . '" for element removing');
564
            return;
565
        }
566
        $removed = array_splice($this->elements, $position, $length);
567
        $this->count = count($this->elements);
568
        return $removed;
569
    }
570
571
    /**
572
     * This clears the current list
573
     *
574
     * @access public
575
     *
576
     * @return void
577
     */
578
    public function reset()
579
    {
580
        $this->elements = [];
581
        $this->records = [];
582
        $this->metadata = [];
583
        $this->count = 0;
584
        $this->position = 0;
585
    }
586
587
    /**
588
     * This resets the list position
589
     * @see \Iterator::rewind()
590
     *
591
     * @access public
592
     *
593
     * @return void
594
     */
595
    public function rewind()
596
    {
597
        $this->position = 0;
598
    }
599
600
    /**
601
     * This saves the current list
602
     *
603
     * @access public
604
     *
605
     * @param int $pid: PID for saving in database
606
     *
607
     * @return void
608
     */
609
    public function save($pid = 0)
610
    {
611
        $pid = max(intval($pid), 0);
612
        // If no PID is given, save to the user's session instead
613
        if ($pid > 0) {
614
            // TODO: Liste in Datenbank speichern (inkl. Sichtbarkeit, Beschreibung, etc.)
615
        } else {
616
            Helper::saveToSession([$this->elements, $this->metadata], get_class($this));
617
        }
618
    }
619
620
    /**
621
     * Connects to Solr server.
622
     *
623
     * @access protected
624
     *
625
     * @return bool true on success or false on failure
626
     */
627
    protected function solrConnect()
628
    {
629
        // Get Solr instance.
630
        if (!$this->solr) {
631
            // Connect to Solr server.
632
            $solr = Solr::getInstance($this->metadata['options']['core']);
633
            if ($solr->ready) {
634
                $this->solr = $solr;
635
                // Get indexed fields.
636
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
637
                    ->getQueryBuilderForTable('tx_dlf_metadata');
638
639
                // Load index configuration.
640
                $result = $queryBuilder
641
                    ->select(
642
                        'tx_dlf_metadata.index_name AS index_name',
643
                        'tx_dlf_metadata.index_tokenized AS index_tokenized',
644
                        'tx_dlf_metadata.index_indexed AS index_indexed'
645
                    )
646
                    ->from('tx_dlf_metadata')
647
                    ->where(
648
                        $queryBuilder->expr()->eq('tx_dlf_metadata.is_listed', 1),
649
                        $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($this->metadata['options']['pid'])),
650
                        Helper::whereExpression('tx_dlf_metadata')
651
                    )
652
                    ->orderBy('tx_dlf_metadata.sorting', 'ASC')
653
                    ->execute();
654
655
                while ($resArray = $result->fetch()) {
656
                    $this->solrConfig[$resArray['index_name']] = $resArray['index_name'] . '_' . ($resArray['index_tokenized'] ? 't' : 'u') . 's' . ($resArray['index_indexed'] ? 'i' : 'u');
657
                }
658
                // Add static fields.
659
                $this->solrConfig['type'] = 'type';
660
            } else {
661
                return false;
662
            }
663
        }
664
        return true;
665
    }
666
667
    /**
668
     * This sorts the current list by the given field
669
     *
670
     * @access public
671
     *
672
     * @param string $by: Sort the list by this field
673
     * @param bool $asc: Sort ascending?
674
     *
675
     * @return void
676
     */
677
    public function sort($by, $asc = true)
678
    {
679
        $newOrder = [];
680
        $nonSortable = [];
681
        foreach ($this->elements as $num => $element) {
682
            // Is this element sortable?
683
            if (!empty($element['s'][$by])) {
684
                $newOrder[$element['s'][$by] . str_pad($num, 6, '0', STR_PAD_LEFT)] = $element;
685
            } else {
686
                $nonSortable[] = $element;
687
            }
688
        }
689
        // Reorder elements.
690
        if ($asc) {
691
            ksort($newOrder, SORT_LOCALE_STRING);
692
        } else {
693
            krsort($newOrder, SORT_LOCALE_STRING);
694
        }
695
        // Add non sortable elements to the end of the list.
696
        $newOrder = array_merge(array_values($newOrder), $nonSortable);
697
        // Check if something is missing.
698
        if ($this->count == count($newOrder)) {
699
            $this->elements = $newOrder;
700
        } else {
701
            $this->logger->error('Sorted list elements do not match unsorted elements');
702
        }
703
    }
704
705
    /**
706
     * This unsets the element at the given offset
707
     * @see \ArrayAccess::offsetUnset()
708
     *
709
     * @access public
710
     *
711
     * @param mixed $offset: The offset to unset
712
     *
713
     * @return void
714
     */
715
    public function offsetUnset($offset)
716
    {
717
        unset($this->elements[$offset]);
718
        // Re-number the elements.
719
        $this->elements = array_values($this->elements);
720
        $this->count = count($this->elements);
721
    }
722
723
    /**
724
     * This checks if the current list position is valid
725
     * @see \Iterator::valid()
726
     *
727
     * @access public
728
     *
729
     * @return bool Is the current list position valid?
730
     */
731
    public function valid()
732
    {
733
        return isset($this->elements[$this->position]);
734
    }
735
736
    /**
737
     * This returns $this->metadata via __get()
738
     *
739
     * @access protected
740
     *
741
     * @return array The list's metadata
742
     */
743
    protected function _getMetadata()
744
    {
745
        return $this->metadata;
746
    }
747
748
    /**
749
     * This sets $this->metadata via __set()
750
     *
751
     * @access protected
752
     *
753
     * @param array $metadata: Array of new metadata
754
     *
755
     * @return void
756
     */
757
    protected function _setMetadata(array $metadata = [])
758
    {
759
        $this->metadata = $metadata;
760
    }
761
762
    /**
763
     * This is the constructor
764
     *
765
     * @access public
766
     *
767
     * @param array $elements: Array of documents initially setting up the list
768
     * @param array $metadata: Array of initial metadata
769
     *
770
     * @return void
771
     */
772
    public function __construct(array $elements = [], array $metadata = [])
773
    {
774
        if (
775
            empty($elements)
776
            && empty($metadata)
777
        ) {
778
            // Let's check the user's session.
779
            $sessionData = Helper::loadFromSession(get_class($this));
780
            // Restore list from session data.
781
            if (is_array($sessionData)) {
782
                if (is_array($sessionData[0])) {
783
                    $this->elements = $sessionData[0];
784
                }
785
                if (is_array($sessionData[1])) {
786
                    $this->metadata = $sessionData[1];
787
                }
788
            }
789
        } else {
790
            // Add metadata to the list.
791
            $this->metadata = $metadata;
792
            // Add initial set of elements to the list.
793
            $this->elements = $elements;
794
        }
795
796
        $this->count = count($this->elements);
797
    }
798
799
    /**
800
     * This magic method is invoked each time a clone is called on the object variable
801
     *
802
     * @access protected
803
     *
804
     * @return void
805
     */
806
    protected function __clone()
807
    {
808
        // This method is defined as protected because singleton objects should not be cloned.
809
    }
810
811
    /**
812
     * This magic method is called each time an invisible property is referenced from the object
813
     *
814
     * @access public
815
     *
816
     * @param string $var: Name of variable to get
817
     *
818
     * @return mixed Value of $this->$var
819
     */
820
    public function __get($var)
821
    {
822
        $method = '_get' . ucfirst($var);
823
        if (
824
            !property_exists($this, $var)
825
            || !method_exists($this, $method)
826
        ) {
827
            $this->logger->warning('There is no getter function for property "' . $var . '"');
828
            return;
829
        } else {
830
            return $this->$method();
831
        }
832
    }
833
834
    /**
835
     * This magic method is called each time an invisible property is checked for isset() or empty()
836
     *
837
     * @access public
838
     *
839
     * @param string $var: Name of variable to check
840
     *
841
     * @return bool true if variable is set and not empty, false otherwise
842
     */
843
    public function __isset($var)
844
    {
845
        return !empty($this->__get($var));
846
    }
847
848
    /**
849
     * This magic method is called each time an invisible property is referenced from the object
850
     *
851
     * @access public
852
     *
853
     * @param string $var: Name of variable to set
854
     * @param mixed $value: New value of variable
855
     *
856
     * @return void
857
     */
858
    public function __set($var, $value)
859
    {
860
        $method = '_set' . ucfirst($var);
861
        if (
862
            !property_exists($this, $var)
863
            || !method_exists($this, $method)
864
        ) {
865
            $this->logger->warning('There is no setter function for property "' . $var . '"');
866
        } else {
867
            $this->$method($value);
868
        }
869
    }
870
871
    /**
872
     * This magic method is executed prior to any serialization of the object
873
     * @see __wakeup()
874
     *
875
     * @access public
876
     *
877
     * @return array Properties to be serialized
878
     */
879
    public function __sleep()
880
    {
881
        return ['elements', 'metadata'];
882
    }
883
884
    /**
885
     * This magic method is executed after the object is deserialized
886
     * @see __sleep()
887
     *
888
     * @access public
889
     *
890
     * @return void
891
     */
892
    public function __wakeup()
893
    {
894
        $this->count = count($this->elements);
895
    }
896
}
897