Passed
Pull Request — master (#86)
by
unknown
02:39
created

DocumentList::__construct()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
c 0
b 0
f 0
dl 0
loc 25
rs 9.2222
cc 6
nc 6
nop 2
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');
0 ignored issues
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