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 (#821)
by
unknown
04:02
created

MetsDocument::_getPhysicalStructure()   D

Complexity

Conditions 19
Paths 66

Size

Total Lines 55
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 33
c 0
b 0
f 0
dl 0
loc 55
rs 4.5166
cc 19
nc 66
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
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 TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
16
use TYPO3\CMS\Core\Database\ConnectionPool;
17
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use Ubl\Iiif\Tools\IiifHelper;
20
use Ubl\Iiif\Services\AbstractImageService;
21
use TYPO3\CMS\Core\Log\LogManager;
22
23
/**
24
 * MetsDocument class for the 'dlf' extension.
25
 *
26
 * @author Sebastian Meyer <[email protected]>
27
 * @author Henrik Lochmann <[email protected]>
28
 * @package TYPO3
29
 * @subpackage dlf
30
 * @access public
31
 * @property int $cPid This holds the PID for the configuration
32
 * @property-read array $dmdSec This holds the XML file's dmdSec parts with their IDs as array key
33
 * @property-read array $fileGrps This holds the file ID -> USE concordance
34
 * @property-read bool $hasFulltext Are there any fulltext files available?
35
 * @property-read array $metadataArray This holds the documents' parsed metadata array
36
 * @property-read \SimpleXMLElement $mets This holds the XML file's METS part as \SimpleXMLElement object
37
 * @property-read int $numPages The holds the total number of pages
38
 * @property-read int $parentId This holds the UID of the parent document or zero if not multi-volumed
39
 * @property-read array $physicalStructure This holds the physical structure
40
 * @property-read array $physicalStructureInfo This holds the physical structure metadata
41
 * @property-read int $pid This holds the PID of the document or zero if not in database
42
 * @property-read bool $ready Is the document instantiated successfully?
43
 * @property-read string $recordId The METS file's / IIIF manifest's record identifier
44
 * @property-read int $rootId This holds the UID of the root document or zero if not multi-volumed
45
 * @property-read array $smLinks This holds the smLinks between logical and physical structMap
46
 * @property-read array $tableOfContents This holds the logical structure
47
 * @property-read string $thumbnail This holds the document's thumbnail location
48
 * @property-read string $toplevelId This holds the toplevel structure's @ID (METS) or the manifest's @id (IIIF)
49
 * @property-read string $parentHref URL of the parent document (determined via mptr element), or empty string if none is available
50
 */
51
final class MetsDocument extends Doc
52
{
53
    /**
54
     * This holds the whole XML file as string for serialization purposes
55
     * @see __sleep() / __wakeup()
56
     *
57
     * @var string
58
     * @access protected
59
     */
60
    protected $asXML = '';
61
62
    /**
63
     * This holds the XML file's dmdSec parts with their IDs as array key
64
     *
65
     * @var array
66
     * @access protected
67
     */
68
    protected $dmdSec = [];
69
70
    /**
71
     * Are the METS file's dmdSecs loaded?
72
     * @see $dmdSec
73
     *
74
     * @var bool
75
     * @access protected
76
     */
77
    protected $dmdSecLoaded = false;
78
79
    /**
80
     * The extension key
81
     *
82
     * @var	string
83
     * @access public
84
     */
85
    public static $extKey = 'dlf';
86
87
    /**
88
     * This holds the file ID -> USE concordance
89
     * @see _getFileGrps()
90
     *
91
     * @var array
92
     * @access protected
93
     */
94
    protected $fileGrps = [];
95
96
    /**
97
     * Are the image file groups loaded?
98
     * @see $fileGrps
99
     *
100
     * @var bool
101
     * @access protected
102
     */
103
    protected $fileGrpsLoaded = false;
104
105
    /**
106
     * This holds the XML file's METS part as \SimpleXMLElement object
107
     *
108
     * @var \SimpleXMLElement
109
     * @access protected
110
     */
111
    protected $mets;
112
113
    /**
114
     * This holds the whole XML file as \SimpleXMLElement object
115
     *
116
     * @var \SimpleXMLElement
117
     * @access protected
118
     */
119
    protected $xml;
120
121
    /**
122
     * URL of the parent document (determined via mptr element),
123
     * or empty string if none is available
124
     *
125
     * @var string|null
126
     * @access protected
127
     */
128
    protected $parentHref;
129
130
    /**
131
     * This adds metadata from METS structural map to metadata array.
132
     *
133
     * @access	public
134
     *
135
     * @param	array	&$metadata: The metadata array to extend
136
     * @param	string	$id: The "@ID" attribute of the logical structure node
137
     *
138
     * @return  void
139
     */
140
    public function addMetadataFromMets(&$metadata, $id)
141
    {
142
        $details = $this->getLogicalStructure($id);
143
        if (!empty($details)) {
144
            $metadata['mets_order'][0] = $details['order'];
145
            $metadata['mets_label'][0] = $details['label'];
146
            $metadata['mets_orderlabel'][0] = $details['orderlabel'];
147
        }
148
    }
149
150
    /**
151
     *
152
     * {@inheritDoc}
153
     * @see \Kitodo\Dlf\Common\Doc::establishRecordId()
154
     */
155
    protected function establishRecordId($pid)
156
    {
157
        // Check for METS object @ID.
158
        if (!empty($this->mets['OBJID'])) {
159
            $this->recordId = (string) $this->mets['OBJID'];
160
        }
161
        // Get hook objects.
162
        $hookObjects = Helper::getHookObjects('Classes/Common/MetsDocument.php');
163
        // Apply hooks.
164
        foreach ($hookObjects as $hookObj) {
165
            if (method_exists($hookObj, 'construct_postProcessRecordId')) {
166
                $hookObj->construct_postProcessRecordId($this->xml, $this->recordId);
167
            }
168
        }
169
    }
170
171
    /**
172
     *
173
     * {@inheritDoc}
174
     * @see \Kitodo\Dlf\Common\Doc::getDownloadLocation()
175
     */
176
    public function getDownloadLocation($id)
177
    {
178
        $fileMimeType = $this->getFileMimeType($id);
179
        $fileLocation = $this->getFileLocation($id);
180
        if ($fileMimeType === 'application/vnd.kitodo.iiif') {
181
            $fileLocation = (strrpos($fileLocation, 'info.json') === strlen($fileLocation) - 9) ? $fileLocation : (strrpos($fileLocation, '/') === strlen($fileLocation) ? $fileLocation . 'info.json' : $fileLocation . '/info.json');
182
            $conf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
183
            IiifHelper::setUrlReader(IiifUrlReader::getInstance());
184
            IiifHelper::setMaxThumbnailHeight($conf['iiifThumbnailHeight']);
185
            IiifHelper::setMaxThumbnailWidth($conf['iiifThumbnailWidth']);
186
            $service = IiifHelper::loadIiifResource($fileLocation);
187
            if ($service !== null && $service instanceof AbstractImageService) {
188
                return $service->getImageUrl();
189
            }
190
        } elseif ($fileMimeType === 'application/vnd.netfpx') {
191
            $baseURL = $fileLocation . (strpos($fileLocation, '?') === false ? '?' : '');
192
            // TODO CVT is an optional IIP server capability; in theory, capabilities should be determined in the object request with '&obj=IIP-server'
193
            return $baseURL . '&CVT=jpeg';
194
        }
195
        return $fileLocation;
196
    }
197
198
    /**
199
     * {@inheritDoc}
200
     * @see \Kitodo\Dlf\Common\Doc::getFileLocation()
201
     */
202
    public function getFileLocation($id)
203
    {
204
        $location = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/mets:FLocat[@LOCTYPE="URL"]');
205
        if (
206
            !empty($id)
207
            && !empty($location)
208
        ) {
209
            return (string) $location[0]->attributes('http://www.w3.org/1999/xlink')->href;
210
        } else {
211
            $this->logger->warning('There is no file node with @ID "' . $id . '"');
212
            return '';
213
        }
214
    }
215
216
    /**
217
     * {@inheritDoc}
218
     * @see \Kitodo\Dlf\Common\Doc::getFileMimeType()
219
     */
220
    public function getFileMimeType($id)
221
    {
222
        $mimetype = $this->mets->xpath('./mets:fileSec/mets:fileGrp/mets:file[@ID="' . $id . '"]/@MIMETYPE');
223
        if (
224
            !empty($id)
225
            && !empty($mimetype)
226
        ) {
227
            return (string) $mimetype[0];
228
        } else {
229
            $this->logger->warning('There is no file node with @ID "' . $id . '" or no MIME type specified');
230
            return '';
231
        }
232
    }
233
234
    /**
235
     * {@inheritDoc}
236
     * @see \Kitodo\Dlf\Common\Doc::getLogicalStructure()
237
     */
238
    public function getLogicalStructure($id, $recursive = false)
239
    {
240
        $details = [];
241
        // Is the requested logical unit already loaded?
242
        if (
243
            !$recursive
244
            && !empty($this->logicalUnits[$id])
245
        ) {
246
            // Yes. Return it.
247
            return $this->logicalUnits[$id];
248
        } elseif (!empty($id)) {
249
            // Get specified logical unit.
250
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]');
251
        } else {
252
            // Get all logical units at top level.
253
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]/mets:div');
254
        }
255
        if (!empty($divs)) {
256
            if (!$recursive) {
257
                // Get the details for the first xpath hit.
258
                $details = $this->getLogicalStructureInfo($divs[0]);
259
            } else {
260
                // Walk the logical structure recursively and fill the whole table of contents.
261
                foreach ($divs as $div) {
262
                    $this->tableOfContents[] = $this->getLogicalStructureInfo($div, $recursive);
263
                }
264
            }
265
        }
266
        return $details;
267
    }
268
269
    /**
270
     * This gets details about a logical structure element
271
     *
272
     * @access protected
273
     *
274
     * @param \SimpleXMLElement $structure: The logical structure node
275
     * @param bool $recursive: Whether to include the child elements
276
     *
277
     * @return array Array of the element's id, label, type and physical page indexes/mptr link
278
     */
279
    protected function getLogicalStructureInfo(\SimpleXMLElement $structure, $recursive = false)
280
    {
281
        // Get attributes.
282
        foreach ($structure->attributes() as $attribute => $value) {
283
            $attributes[$attribute] = (string) $value;
284
        }
285
        // Load plugin configuration.
286
        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
287
        // Extract identity information.
288
        $details = [];
289
        $details['id'] = $attributes['ID'];
290
        $details['dmdId'] = (isset($attributes['DMDID']) ? $attributes['DMDID'] : '');
291
        $details['order'] = (isset($attributes['ORDER']) ? $attributes['ORDER'] : '');
292
        $details['label'] = (isset($attributes['LABEL']) ? $attributes['LABEL'] : '');
293
        $details['orderlabel'] = (isset($attributes['ORDERLABEL']) ? $attributes['ORDERLABEL'] : '');
294
        $details['contentIds'] = (isset($attributes['CONTENTIDS']) ? $attributes['CONTENTIDS'] : '');
295
        $details['volume'] = '';
296
        // Set volume information only if no label is set and this is the toplevel structure element.
297
        if (
298
            empty($details['label'])
299
            && $details['id'] == $this->_getToplevelId()
300
        ) {
301
            $metadata = $this->getMetadata($details['id']);
302
            if (!empty($metadata['volume'][0])) {
303
                $details['volume'] = $metadata['volume'][0];
304
            }
305
        }
306
        $details['pagination'] = '';
307
        $details['type'] = $attributes['TYPE'];
308
        $details['thumbnailId'] = '';
309
        // Load smLinks.
310
        $this->_getSmLinks();
311
        // Load physical structure.
312
        $this->_getPhysicalStructure();
313
        // Get the physical page or external file this structure element is pointing at.
314
        $details['points'] = '';
315
        // Is there a mptr node?
316
        if (count($structure->children('http://www.loc.gov/METS/')->mptr)) {
317
            // Yes. Get the file reference.
318
            $details['points'] = (string) $structure->children('http://www.loc.gov/METS/')->mptr[0]->attributes('http://www.w3.org/1999/xlink')->href;
319
        } elseif (
320
            !empty($this->physicalStructure)
321
            && array_key_exists($details['id'], $this->smLinks['l2p'])
322
        ) {
323
            // Link logical structure to the first corresponding physical page/track.
324
            $details['points'] = max(intval(array_search($this->smLinks['l2p'][$details['id']][0], $this->physicalStructure, true)), 1);
325
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
326
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
327
                if (!empty($this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb])) {
328
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['files'][$fileGrpThumb];
329
                    break;
330
                }
331
            }
332
            // Get page/track number of the first page/track related to this structure element.
333
            $details['pagination'] = $this->physicalStructureInfo[$this->smLinks['l2p'][$details['id']][0]]['orderlabel'];
334
        } elseif ($details['id'] == $this->_getToplevelId()) {
335
            // Point to self if this is the toplevel structure.
336
            $details['points'] = 1;
337
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
338
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
339
                if (
340
                    !empty($this->physicalStructure)
341
                    && !empty($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb])
342
                ) {
343
                    $details['thumbnailId'] = $this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb];
344
                    break;
345
                }
346
            }
347
        }
348
        // Get the files this structure element is pointing at.
349
        $details['files'] = [];
350
        $fileUse = $this->_getFileGrps();
351
        // Get the file representations from fileSec node.
352
        foreach ($structure->children('http://www.loc.gov/METS/')->fptr as $fptr) {
353
            // Check if file has valid @USE attribute.
354
            if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
0 ignored issues
show
Bug introduced by
The method attributes() 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

354
            if (!empty($fileUse[(string) $fptr->/** @scrutinizer ignore-call */ attributes()->FILEID])) {

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...
355
                $details['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
356
            }
357
        }
358
        // Keep for later usage.
359
        $this->logicalUnits[$details['id']] = $details;
360
        // Walk the structure recursively? And are there any children of the current element?
361
        if (
362
            $recursive
363
            && count($structure->children('http://www.loc.gov/METS/')->div)
364
        ) {
365
            $details['children'] = [];
366
            foreach ($structure->children('http://www.loc.gov/METS/')->div as $child) {
367
                // Repeat for all children.
368
                $details['children'][] = $this->getLogicalStructureInfo($child, true);
0 ignored issues
show
Bug introduced by
It seems like $child can also be of type null; however, parameter $structure of Kitodo\Dlf\Common\MetsDo...tLogicalStructureInfo() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

368
                $details['children'][] = $this->getLogicalStructureInfo(/** @scrutinizer ignore-type */ $child, true);
Loading history...
369
            }
370
        }
371
        return $details;
372
    }
373
374
    /**
375
     * {@inheritDoc}
376
     * @see \Kitodo\Dlf\Common\Doc::getMetadata()
377
     */
378
    public function getMetadata($id, $cPid = 0)
379
    {
380
        // Make sure $cPid is a non-negative integer.
381
        $cPid = max(intval($cPid), 0);
382
        // If $cPid is not given, try to get it elsewhere.
383
        if (
384
            !$cPid
385
            && ($this->cPid || $this->pid)
386
        ) {
387
            // Retain current PID.
388
            $cPid = ($this->cPid ? $this->cPid : $this->pid);
389
        } elseif (!$cPid) {
390
            $this->logger->warning('Invalid PID ' . $cPid . ' for metadata definitions');
391
            return [];
392
        }
393
        // Get metadata from parsed metadata array if available.
394
        if (
395
            !empty($this->metadataArray[$id])
396
            && $this->metadataArray[0] == $cPid
397
        ) {
398
            return $this->metadataArray[$id];
399
        }
400
        // Initialize metadata array with empty values.
401
        $metadata = [
402
            'title' => [],
403
            'title_sorting' => [],
404
            'author' => [],
405
            'place' => [],
406
            'year' => [],
407
            'prod_id' => [],
408
            'record_id' => [],
409
            'opac_id' => [],
410
            'union_id' => [],
411
            'urn' => [],
412
            'purl' => [],
413
            'type' => [],
414
            'volume' => [],
415
            'volume_sorting' => [],
416
            'license' => [],
417
            'terms' => [],
418
            'restrictions' => [],
419
            'out_of_print' => [],
420
            'rights_info' => [],
421
            'collection' => [],
422
            'owner' => [],
423
            'mets_label' => [],
424
            'mets_orderlabel' => [],
425
            'document_format' => ['METS'],
426
        ];
427
        // Get the logical structure node's @DMDID.
428
        if (!empty($this->logicalUnits[$id])) {
429
            $dmdIds = $this->logicalUnits[$id]['dmdId'];
430
        } else {
431
            $dmdIds = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@DMDID');
432
            $dmdIds = (string) $dmdIds[0];
433
        }
434
        if (!empty($dmdIds)) {
435
            // Handle multiple DMDIDs separately.
436
            $dmdIds = explode(' ', $dmdIds);
437
            $hasSupportedMetadata = false;
438
        } else {
439
            // There is no dmdSec for this structure node.
440
            return [];
441
        }
442
        // Load available metadata formats and dmdSecs.
443
        $this->loadFormats();
444
        $this->_getDmdSec();
445
        foreach ($dmdIds as $dmdId) {
446
            // Is this metadata format supported?
447
            if (!empty($this->formats[$this->dmdSec[$dmdId]['type']])) {
448
                if (!empty($this->formats[$this->dmdSec[$dmdId]['type']]['class'])) {
449
                    $class = $this->formats[$this->dmdSec[$dmdId]['type']]['class'];
450
                    // Get the metadata from class.
451
                    if (
452
                        class_exists($class)
453
                        && ($obj = GeneralUtility::makeInstance($class)) instanceof MetadataInterface
454
                    ) {
455
                        $obj->extractMetadata($this->dmdSec[$dmdId]['xml'], $metadata);
456
                    } else {
457
                        $this->logger->warning('Invalid class/method "' . $class . '->extractMetadata()" for metadata format "' . $this->dmdSec[$dmdId]['type'] . '"');
458
                    }
459
                }
460
            } else {
461
                $this->logger->notice('Unsupported metadata format "' . $this->dmdSec[$dmdId]['type'] . '" in dmdSec with @ID "' . $dmdId . '"');
462
                // Continue searching for supported metadata with next @DMDID.
463
                continue;
464
            }
465
            // Get the structure's type.
466
            if (!empty($this->logicalUnits[$id])) {
467
                $metadata['type'] = [$this->logicalUnits[$id]['type']];
468
            } else {
469
                $struct = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $id . '"]/@TYPE');
470
                if (!empty($struct)) {
471
                    $metadata['type'] = [(string) $struct[0]];
472
                }
473
            }
474
            // Get the additional metadata from database.
475
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
476
                ->getQueryBuilderForTable('tx_dlf_metadata');
477
            // Get hidden records, too.
478
            $queryBuilder
479
                ->getRestrictions()
480
                ->removeByType(HiddenRestriction::class);
481
            // Get all metadata with configured xpath and applicable format first.
482
            $resultWithFormat = $queryBuilder
483
                ->select(
484
                    'tx_dlf_metadata.index_name AS index_name',
485
                    'tx_dlf_metadataformat_joins.xpath AS xpath',
486
                    'tx_dlf_metadataformat_joins.xpath_sorting AS xpath_sorting',
487
                    'tx_dlf_metadata.is_sortable AS is_sortable',
488
                    'tx_dlf_metadata.default_value AS default_value',
489
                    'tx_dlf_metadata.format AS format'
490
                )
491
                ->from('tx_dlf_metadata')
492
                ->innerJoin(
493
                    'tx_dlf_metadata',
494
                    'tx_dlf_metadataformat',
495
                    'tx_dlf_metadataformat_joins',
496
                    $queryBuilder->expr()->eq(
497
                        'tx_dlf_metadataformat_joins.parent_id',
498
                        'tx_dlf_metadata.uid'
499
                    )
500
                )
501
                ->innerJoin(
502
                    'tx_dlf_metadataformat_joins',
503
                    'tx_dlf_formats',
504
                    'tx_dlf_formats_joins',
505
                    $queryBuilder->expr()->eq(
506
                        'tx_dlf_formats_joins.uid',
507
                        'tx_dlf_metadataformat_joins.encoded'
508
                    )
509
                )
510
                ->where(
511
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
512
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
513
                    $queryBuilder->expr()->eq('tx_dlf_metadataformat_joins.pid', intval($cPid)),
514
                    $queryBuilder->expr()->eq('tx_dlf_formats_joins.type', $queryBuilder->createNamedParameter($this->dmdSec[$dmdId]['type']))
515
                )
516
                ->execute();
517
            // Get all metadata without a format, but with a default value next.
518
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
519
                ->getQueryBuilderForTable('tx_dlf_metadata');
520
            // Get hidden records, too.
521
            $queryBuilder
522
                ->getRestrictions()
523
                ->removeByType(HiddenRestriction::class);
524
            $resultWithoutFormat = $queryBuilder
525
                ->select(
526
                    'tx_dlf_metadata.index_name AS index_name',
527
                    'tx_dlf_metadata.is_sortable AS is_sortable',
528
                    'tx_dlf_metadata.default_value AS default_value',
529
                    'tx_dlf_metadata.format AS format'
530
                )
531
                ->from('tx_dlf_metadata')
532
                ->where(
533
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($cPid)),
534
                    $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0),
535
                    $queryBuilder->expr()->eq('tx_dlf_metadata.format', 0),
536
                    $queryBuilder->expr()->neq('tx_dlf_metadata.default_value', $queryBuilder->createNamedParameter(''))
537
                )
538
                ->execute();
539
            // Merge both result sets.
540
            $allResults = array_merge($resultWithFormat->fetchAll(), $resultWithoutFormat->fetchAll());
541
            // We need a \DOMDocument here, because SimpleXML doesn't support XPath functions properly.
542
            $domNode = dom_import_simplexml($this->dmdSec[$dmdId]['xml']);
543
            $domXPath = new \DOMXPath($domNode->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $domNode->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, maybe add an additional type check? ( Ignorable by Annotation )

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

543
            $domXPath = new \DOMXPath(/** @scrutinizer ignore-type */ $domNode->ownerDocument);
Loading history...
544
            $this->registerNamespaces($domXPath);
545
            // OK, now make the XPath queries.
546
            foreach ($allResults as $resArray) {
547
                // Set metadata field's value(s).
548
                if (
549
                    $resArray['format'] > 0
550
                    && !empty($resArray['xpath'])
551
                    && ($values = $domXPath->evaluate($resArray['xpath'], $domNode))
552
                ) {
553
                    if (
554
                        $values instanceof \DOMNodeList
555
                        && $values->length > 0
556
                    ) {
557
                        $metadata[$resArray['index_name']] = [];
558
                        foreach ($values as $value) {
559
                            $metadata[$resArray['index_name']][] = trim((string) $value->nodeValue);
560
                        }
561
                    } elseif (!($values instanceof \DOMNodeList)) {
562
                        $metadata[$resArray['index_name']] = [trim((string) $values)];
563
                    }
564
                }
565
                // Set default value if applicable.
566
                if (
567
                    empty($metadata[$resArray['index_name']][0])
568
                    && strlen($resArray['default_value']) > 0
569
                ) {
570
                    $metadata[$resArray['index_name']] = [$resArray['default_value']];
571
                }
572
                // Set sorting value if applicable.
573
                if (
574
                    !empty($metadata[$resArray['index_name']])
575
                    && $resArray['is_sortable']
576
                ) {
577
                    if (
578
                        $resArray['format'] > 0
579
                        && !empty($resArray['xpath_sorting'])
580
                        && ($values = $domXPath->evaluate($resArray['xpath_sorting'], $domNode))
581
                    ) {
582
                        if (
583
                            $values instanceof \DOMNodeList
584
                            && $values->length > 0
585
                        ) {
586
                            $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values->item(0)->nodeValue);
587
                        } elseif (!($values instanceof \DOMNodeList)) {
588
                            $metadata[$resArray['index_name'] . '_sorting'][0] = trim((string) $values);
589
                        }
590
                    }
591
                    if (empty($metadata[$resArray['index_name'] . '_sorting'][0])) {
592
                        $metadata[$resArray['index_name'] . '_sorting'][0] = $metadata[$resArray['index_name']][0];
593
                    }
594
                }
595
            }
596
            // Set title to empty string if not present.
597
            if (empty($metadata['title'][0])) {
598
                $metadata['title'][0] = '';
599
                $metadata['title_sorting'][0] = '';
600
            }
601
            // Extract metadata only from first supported dmdSec.
602
            $hasSupportedMetadata = true;
603
            break;
604
        }
605
        if ($hasSupportedMetadata) {
606
            return $metadata;
607
        } else {
608
            $this->logger->warning('No supported metadata found for logical structure with @ID "' . $id . '"');
609
            return [];
610
        }
611
    }
612
613
    /**
614
     * {@inheritDoc}
615
     * @see \Kitodo\Dlf\Common\Doc::getFullText()
616
     */
617
    public function getFullText($id)
618
    {
619
        $fullText = '';
620
621
        // Load fileGrps and check for full text files.
622
        $this->_getFileGrps();
623
        if ($this->hasFulltext) {
624
            $fullText = $this->getFullTextFromXml($id);
625
        }
626
        return $fullText;
627
    }
628
629
    /**
630
     * {@inheritDoc}
631
     * @see Doc::getStructureDepth()
632
     */
633
    public function getStructureDepth($logId)
634
    {
635
        $ancestors = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $logId . '"]/ancestor::*');
636
        if (!empty($ancestors)) {
637
            return count($ancestors);
638
        } else {
639
            return 0;
640
        }
641
    }
642
643
    /**
644
     * {@inheritDoc}
645
     * @see \Kitodo\Dlf\Common\Doc::init()
646
     */
647
    protected function init($location)
648
    {
649
        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(get_class($this));
650
        // Get METS node from XML file.
651
        $this->registerNamespaces($this->xml);
652
        $mets = $this->xml->xpath('//mets:mets');
653
        if (!empty($mets)) {
654
            $this->mets = $mets[0];
655
            // Register namespaces.
656
            $this->registerNamespaces($this->mets);
657
        } else {
658
            if (!empty($location)) {
659
                $this->logger->error('No METS part found in document with location "' . $location . '".');
660
            } else if (!empty($this->recordId)) {
661
                $this->logger->error('No METS part found in document with recordId "' . $this->recordId . '".');
662
            } else {
663
                $this->logger->error('No METS part found in current document.');
664
            }
665
        }
666
    }
667
668
    /**
669
     * {@inheritDoc}
670
     * @see \Kitodo\Dlf\Common\Doc::loadLocation()
671
     */
672
    protected function loadLocation($location)
673
    {
674
        $fileResource = Helper::getUrl($location);
675
        if ($fileResource !== false) {
676
            $xml = Helper::getXmlFileAsString($fileResource);
677
            // Set some basic properties.
678
            if ($xml !== false) {
679
                $this->xml = $xml;
680
                return true;
681
            }
682
        }
683
        $this->logger->error('Could not load XML file from "' . $location . '"');
684
        return false;
685
    }
686
687
    /**
688
     * {@inheritDoc}
689
     * @see \Kitodo\Dlf\Common\Doc::ensureHasFulltextIsSet()
690
     */
691
    protected function ensureHasFulltextIsSet()
692
    {
693
        // Are the fileGrps already loaded?
694
        if (!$this->fileGrpsLoaded) {
695
            $this->_getFileGrps();
696
        }
697
    }
698
699
    /**
700
     * {@inheritDoc}
701
     * @see Doc::setPreloadedDocument()
702
     */
703
    protected function setPreloadedDocument($preloadedDocument)
704
    {
705
706
        if ($preloadedDocument instanceof \SimpleXMLElement) {
707
            $this->xml = $preloadedDocument;
708
            return true;
709
        }
710
        return false;
711
    }
712
713
    /**
714
     * {@inheritDoc}
715
     * @see Doc::getDocument()
716
     */
717
    protected function getDocument()
718
    {
719
        return $this->mets;
720
    }
721
722
    /**
723
     * This builds an array of the document's dmdSecs
724
     *
725
     * @access protected
726
     *
727
     * @return array Array of dmdSecs with their IDs as array key
728
     */
729
    protected function _getDmdSec()
730
    {
731
        if (!$this->dmdSecLoaded) {
732
            // Get available data formats.
733
            $this->loadFormats();
734
            // Get dmdSec nodes from METS.
735
            $dmdIds = $this->mets->xpath('./mets:dmdSec/@ID');
736
            if (!empty($dmdIds)) {
737
                foreach ($dmdIds as $dmdId) {
738
                    if ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[not(@MDTYPE="OTHER")]/@MDTYPE')) {
739
                        if (!empty($this->formats[(string) $type[0]])) {
740
                            $type = (string) $type[0];
741
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
742
                        }
743
                    } elseif ($type = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"]/@OTHERMDTYPE')) {
744
                        if (!empty($this->formats[(string) $type[0]])) {
745
                            $type = (string) $type[0];
746
                            $xml = $this->mets->xpath('./mets:dmdSec[@ID="' . (string) $dmdId . '"]/mets:mdWrap[@MDTYPE="OTHER"][@OTHERMDTYPE="' . $type . '"]/mets:xmlData/' . strtolower($type) . ':' . $this->formats[$type]['rootElement']);
747
                        }
748
                    }
749
                    if (!empty($xml)) {
750
                        $this->dmdSec[(string) $dmdId]['type'] = $type;
751
                        $this->dmdSec[(string) $dmdId]['xml'] = $xml[0];
752
                        $this->registerNamespaces($this->dmdSec[(string) $dmdId]['xml']);
753
                    }
754
                }
755
            }
756
            $this->dmdSecLoaded = true;
757
        }
758
        return $this->dmdSec;
759
    }
760
761
    /**
762
     * This builds the file ID -> USE concordance
763
     *
764
     * @access protected
765
     *
766
     * @return array Array of file use groups with file IDs
767
     */
768
    protected function _getFileGrps()
769
    {
770
        if (!$this->fileGrpsLoaded) {
771
            // Get configured USE attributes.
772
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
773
            $useGrps = GeneralUtility::trimExplode(',', $extConf['fileGrpImages']);
774
            if (!empty($extConf['fileGrpThumbs'])) {
775
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']));
776
            }
777
            if (!empty($extConf['fileGrpDownload'])) {
778
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpDownload']));
779
            }
780
            if (!empty($extConf['fileGrpFulltext'])) {
781
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']));
782
            }
783
            if (!empty($extConf['fileGrpAudio'])) {
784
                $useGrps = array_merge($useGrps, GeneralUtility::trimExplode(',', $extConf['fileGrpAudio']));
785
            }
786
            // Get all file groups.
787
            $fileGrps = $this->mets->xpath('./mets:fileSec/mets:fileGrp');
788
            if (!empty($fileGrps)) {
789
                // Build concordance for configured USE attributes.
790
                foreach ($fileGrps as $fileGrp) {
791
                    if (in_array((string) $fileGrp['USE'], $useGrps)) {
792
                        foreach ($fileGrp->children('http://www.loc.gov/METS/')->file as $file) {
793
                            $this->fileGrps[(string) $file->attributes()->ID] = (string) $fileGrp['USE'];
0 ignored issues
show
Bug introduced by
The method attributes() 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

793
                            $this->fileGrps[(string) $file->/** @scrutinizer ignore-call */ attributes()->ID] = (string) $fileGrp['USE'];

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...
794
                        }
795
                    }
796
                }
797
            }
798
            // Are there any fulltext files available?
799
            if (
800
                !empty($extConf['fileGrpFulltext'])
801
                && array_intersect(GeneralUtility::trimExplode(',', $extConf['fileGrpFulltext']), $this->fileGrps) !== []
802
            ) {
803
                $this->hasFulltext = true;
804
            }
805
            $this->fileGrpsLoaded = true;
806
        }
807
        return $this->fileGrps;
808
    }
809
810
    /**
811
     * {@inheritDoc}
812
     * @see \Kitodo\Dlf\Common\Doc::prepareMetadataArray()
813
     */
814
    protected function prepareMetadataArray($cPid)
815
    {
816
        $ids = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID]/@ID');
817
        // Get all logical structure nodes with metadata.
818
        if (!empty($ids)) {
819
            foreach ($ids as $id) {
820
                $this->metadataArray[(string) $id] = $this->getMetadata((string) $id, $cPid);
821
            }
822
        }
823
        // Set current PID for metadata definitions.
824
    }
825
826
    /**
827
     * This returns $this->mets via __get()
828
     *
829
     * @access protected
830
     *
831
     * @return \SimpleXMLElement The XML's METS part as \SimpleXMLElement object
832
     */
833
    protected function _getMets()
834
    {
835
        return $this->mets;
836
    }
837
838
    /**
839
     * {@inheritDoc}
840
     * @see \Kitodo\Dlf\Common\Doc::_getPhysicalStructure()
841
     */
842
    protected function _getPhysicalStructure()
843
    {
844
        // Is there no physical structure array yet?
845
        if (!$this->physicalStructureLoaded) {
846
            // Does the document have a structMap node of type "PHYSICAL"?
847
            $elementNodes = $this->mets->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div[@TYPE="physSequence"]/mets:div');
848
            if (!empty($elementNodes)) {
849
                // Get file groups.
850
                $fileUse = $this->_getFileGrps();
851
                // Get the physical sequence's metadata.
852
                $physNode = $this->mets->xpath('./mets:structMap[@TYPE="PHYSICAL"]/mets:div[@TYPE="physSequence"]');
853
                $physSeq[0] = (string) $physNode[0]['ID'];
854
                $this->physicalStructureInfo[$physSeq[0]]['id'] = (string) $physNode[0]['ID'];
855
                $this->physicalStructureInfo[$physSeq[0]]['dmdId'] = (isset($physNode[0]['DMDID']) ? (string) $physNode[0]['DMDID'] : '');
856
                $this->physicalStructureInfo[$physSeq[0]]['order'] = (isset($physNode[0]['ORDER']) ? (string) $physNode[0]['ORDER'] : '');
857
                $this->physicalStructureInfo[$physSeq[0]]['label'] = (isset($physNode[0]['LABEL']) ? (string) $physNode[0]['LABEL'] : '');
858
                $this->physicalStructureInfo[$physSeq[0]]['orderlabel'] = (isset($physNode[0]['ORDERLABEL']) ? (string) $physNode[0]['ORDERLABEL'] : '');
859
                $this->physicalStructureInfo[$physSeq[0]]['type'] = (string) $physNode[0]['TYPE'];
860
                $this->physicalStructureInfo[$physSeq[0]]['contentIds'] = (isset($physNode[0]['CONTENTIDS']) ? (string) $physNode[0]['CONTENTIDS'] : '');
861
                // Get the file representations from fileSec node.
862
                foreach ($physNode[0]->children('http://www.loc.gov/METS/')->fptr as $fptr) {
863
                    // Check if file has valid @USE attribute.
864
                    if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
865
                        $this->physicalStructureInfo[$physSeq[0]]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
866
                    }
867
                }
868
                // Build the physical elements' array from the physical structMap node.
869
                foreach ($elementNodes as $elementNode) {
870
                    $elements[(int) $elementNode['ORDER']] = (string) $elementNode['ID'];
871
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['id'] = (string) $elementNode['ID'];
872
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['dmdId'] = (isset($elementNode['DMDID']) ? (string) $elementNode['DMDID'] : '');
873
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['order'] = (isset($elementNode['ORDER']) ? (string) $elementNode['ORDER'] : '');
874
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['label'] = (isset($elementNode['LABEL']) ? (string) $elementNode['LABEL'] : '');
875
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['orderlabel'] = (isset($elementNode['ORDERLABEL']) ? (string) $elementNode['ORDERLABEL'] : '');
876
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['type'] = (string) $elementNode['TYPE'];
877
                    $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['contentIds'] = (isset($elementNode['CONTENTIDS']) ? (string) $elementNode['CONTENTIDS'] : '');
878
                    // Get the file representations from fileSec node.
879
                    foreach ($elementNode->children('http://www.loc.gov/METS/')->fptr as $fptr) {
880
                        // Check if file has valid @USE attribute.
881
                        if (!empty($fileUse[(string) $fptr->attributes()->FILEID])) {
882
                            $this->physicalStructureInfo[$elements[(int) $elementNode['ORDER']]]['files'][$fileUse[(string) $fptr->attributes()->FILEID]] = (string) $fptr->attributes()->FILEID;
883
                        }
884
                    }
885
                }
886
                // Sort array by keys (= @ORDER).
887
                if (ksort($elements)) {
888
                    // Set total number of pages/tracks.
889
                    $this->numPages = count($elements);
890
                    // Merge and re-index the array to get nice numeric indexes.
891
                    $this->physicalStructure = array_merge($physSeq, $elements);
892
                }
893
            }
894
            $this->physicalStructureLoaded = true;
895
        }
896
        return $this->physicalStructure;
897
    }
898
899
    /**
900
     * {@inheritDoc}
901
     * @see \Kitodo\Dlf\Common\Doc::_getSmLinks()
902
     */
903
    protected function _getSmLinks()
904
    {
905
        if (!$this->smLinksLoaded) {
906
            $smLinks = $this->mets->xpath('./mets:structLink/mets:smLink');
907
            if (!empty($smLinks)) {
908
                foreach ($smLinks as $smLink) {
909
                    $this->smLinks['l2p'][(string) $smLink->attributes('http://www.w3.org/1999/xlink')->from][] = (string) $smLink->attributes('http://www.w3.org/1999/xlink')->to;
910
                    $this->smLinks['p2l'][(string) $smLink->attributes('http://www.w3.org/1999/xlink')->to][] = (string) $smLink->attributes('http://www.w3.org/1999/xlink')->from;
911
                }
912
            }
913
            $this->smLinksLoaded = true;
914
        }
915
        return $this->smLinks;
916
    }
917
918
    /**
919
     * {@inheritDoc}
920
     * @see \Kitodo\Dlf\Common\Doc::_getThumbnail()
921
     */
922
    protected function _getThumbnail($forceReload = false)
923
    {
924
        if (
925
            !$this->thumbnailLoaded
926
            || $forceReload
927
        ) {
928
            // Retain current PID.
929
            $cPid = ($this->cPid ? $this->cPid : $this->pid);
930
            if (!$cPid) {
931
                $this->logger->error('Invalid PID ' . $cPid . ' for structure definitions');
932
                $this->thumbnailLoaded = true;
933
                return $this->thumbnail;
934
            }
935
            // Load extension configuration.
936
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey);
937
            if (empty($extConf['fileGrpThumbs'])) {
938
                $this->logger->warning('No fileGrp for thumbnails specified');
939
                $this->thumbnailLoaded = true;
940
                return $this->thumbnail;
941
            }
942
            $strctId = $this->_getToplevelId();
943
            $metadata = $this->getTitledata($cPid);
944
945
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
946
                ->getQueryBuilderForTable('tx_dlf_structures');
947
948
            // Get structure element to get thumbnail from.
949
            $result = $queryBuilder
950
                ->select('tx_dlf_structures.thumbnail AS thumbnail')
951
                ->from('tx_dlf_structures')
952
                ->where(
953
                    $queryBuilder->expr()->eq('tx_dlf_structures.pid', intval($cPid)),
954
                    $queryBuilder->expr()->eq('tx_dlf_structures.index_name', $queryBuilder->expr()->literal($metadata['type'][0])),
955
                    Helper::whereExpression('tx_dlf_structures')
956
                )
957
                ->setMaxResults(1)
958
                ->execute();
959
960
            $allResults = $result->fetchAll();
961
962
            if (count($allResults) == 1) {
963
                $resArray = $allResults[0];
964
                // Get desired thumbnail structure if not the toplevel structure itself.
965
                if (!empty($resArray['thumbnail'])) {
966
                    $strctType = Helper::getIndexNameFromUid($resArray['thumbnail'], 'tx_dlf_structures', $cPid);
967
                    // Check if this document has a structure element of the desired type.
968
                    $strctIds = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@TYPE="' . $strctType . '"]/@ID');
969
                    if (!empty($strctIds)) {
970
                        $strctId = (string) $strctIds[0];
971
                    }
972
                }
973
                // Load smLinks.
974
                $this->_getSmLinks();
975
                // Get thumbnail location.
976
                $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
977
                while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
978
                    if (
979
                        $this->_getPhysicalStructure()
980
                        && !empty($this->smLinks['l2p'][$strctId])
981
                        && !empty($this->physicalStructureInfo[$this->smLinks['l2p'][$strctId][0]]['files'][$fileGrpThumb])
982
                    ) {
983
                        $this->thumbnail = $this->getFileLocation($this->physicalStructureInfo[$this->smLinks['l2p'][$strctId][0]]['files'][$fileGrpThumb]);
984
                        break;
985
                    } elseif (!empty($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb])) {
986
                        $this->thumbnail = $this->getFileLocation($this->physicalStructureInfo[$this->physicalStructure[1]]['files'][$fileGrpThumb]);
987
                        break;
988
                    }
989
                }
990
            } else {
991
                $this->logger->error('No structure of type "' . $metadata['type'][0] . '" found in database');
992
            }
993
            $this->thumbnailLoaded = true;
994
        }
995
        return $this->thumbnail;
996
    }
997
998
    /**
999
     * {@inheritDoc}
1000
     * @see \Kitodo\Dlf\Common\Doc::_getToplevelId()
1001
     */
1002
    protected function _getToplevelId()
1003
    {
1004
        if (empty($this->toplevelId)) {
1005
            // Get all logical structure nodes with metadata, but without associated METS-Pointers.
1006
            $divs = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@DMDID and not(./mets:mptr)]');
1007
            if (!empty($divs)) {
1008
                // Load smLinks.
1009
                $this->_getSmLinks();
1010
                foreach ($divs as $div) {
1011
                    $id = (string) $div['ID'];
1012
                    // Are there physical structure nodes for this logical structure?
1013
                    if (array_key_exists($id, $this->smLinks['l2p'])) {
1014
                        // Yes. That's what we're looking for.
1015
                        $this->toplevelId = $id;
1016
                        break;
1017
                    } elseif (empty($this->toplevelId)) {
1018
                        // No. Remember this anyway, but keep looking for a better one.
1019
                        $this->toplevelId = $id;
1020
                    }
1021
                }
1022
            }
1023
        }
1024
        return $this->toplevelId;
1025
    }
1026
1027
    /**
1028
     * Try to determine URL of parent document.
1029
     *
1030
     * @return string|null
1031
     */
1032
    public function _getParentHref()
1033
    {
1034
        if ($this->parentHref === null) {
1035
            $this->parentHref = '';
0 ignored issues
show
Bug introduced by
The property parentHref is declared read-only in Kitodo\Dlf\Common\MetsDocument.
Loading history...
1036
1037
            // Get the closest ancestor of the current document which has a MPTR child.
1038
            $parentMptr = $this->mets->xpath('./mets:structMap[@TYPE="LOGICAL"]//mets:div[@ID="' . $this->toplevelId . '"]/ancestor::mets:div[./mets:mptr][1]/mets:mptr');
1039
            if (!empty($parentMptr)) {
1040
                $this->parentHref = (string) $parentMptr[0]->attributes('http://www.w3.org/1999/xlink')->href;
1041
            }
1042
        }
1043
1044
        return $this->parentHref;
1045
    }
1046
1047
    /**
1048
     * This magic method is executed prior to any serialization of the object
1049
     * @see __wakeup()
1050
     *
1051
     * @access public
1052
     *
1053
     * @return array Properties to be serialized
1054
     */
1055
    public function __sleep()
1056
    {
1057
        // \SimpleXMLElement objects can't be serialized, thus save the XML as string for serialization
1058
        $this->asXML = $this->xml->asXML();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->xml->asXML() can also be of type true. However, the property $asXML is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1059
        return ['uid', 'pid', 'recordId', 'parentId', 'asXML'];
1060
    }
1061
1062
    /**
1063
     * This magic method is used for setting a string value for the object
1064
     *
1065
     * @access public
1066
     *
1067
     * @return string String representing the METS object
1068
     */
1069
    public function __toString()
1070
    {
1071
        $xml = new \DOMDocument('1.0', 'utf-8');
1072
        $xml->appendChild($xml->importNode(dom_import_simplexml($this->mets), true));
1073
        $xml->formatOutput = true;
1074
        return $xml->saveXML();
1075
    }
1076
1077
    /**
1078
     * This magic method is executed after the object is deserialized
1079
     * @see __sleep()
1080
     *
1081
     * @access public
1082
     *
1083
     * @return void
1084
     */
1085
    public function __wakeup()
1086
    {
1087
        $xml = Helper::getXmlFileAsString($this->asXML);
1088
        if ($xml !== false) {
1089
            $this->asXML = '';
1090
            $this->xml = $xml;
1091
            // Rebuild the unserializable properties.
1092
            $this->init('');
1093
        } else {
1094
            $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
1095
            $this->logger->error('Could not load XML after deserialization');
1096
        }
1097
    }
1098
}
1099