Passed
Branch master (6c65a4)
by Christian
27:15 queued 11:09
created

Export::unsetExcludedSections()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 2
nop 1
dl 0
loc 12
rs 8.8571
c 0
b 0
f 0
1
<?php
2
namespace TYPO3\CMS\Impexp;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use TYPO3\CMS\Backend\Utility\BackendUtility;
18
use TYPO3\CMS\Core\Database\ReferenceIndex;
19
use TYPO3\CMS\Core\Exception;
20
use TYPO3\CMS\Core\Html\HtmlParser;
21
use TYPO3\CMS\Core\Resource\File;
22
use TYPO3\CMS\Core\Resource\ResourceFactory;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\PathUtility;
25
26
/**
27
 * EXAMPLE for using the impexp-class for exporting stuff:
28
 *
29
 * Create and initialize:
30
 * $this->export = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Impexp\ImportExport::class);
31
 * $this->export->init();
32
 * Set which tables relations we will allow:
33
 * $this->export->relOnlyTables[]="tt_news";	// exclusively includes. See comment in the class
34
 *
35
 * Adding records:
36
 * $this->export->export_addRecord("pages", $this->pageinfo);
37
 * $this->export->export_addRecord("pages", \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord("pages", 38));
38
 * $this->export->export_addRecord("pages", \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord("pages", 39));
39
 * $this->export->export_addRecord("tt_content", \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord("tt_content", 12));
40
 * $this->export->export_addRecord("tt_content", \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord("tt_content", 74));
41
 * $this->export->export_addRecord("sys_template", \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord("sys_template", 20));
42
 *
43
 * Adding all the relations (recursively in 5 levels so relations has THEIR relations registered as well)
44
 * for($a=0;$a<5;$a++) {
45
 * $addR = $this->export->export_addDBRelations($a);
46
 * if (empty($addR)) break;
47
 * }
48
 *
49
 * Finally load all the files.
50
 * $this->export->export_addFilesFromRelations();	// MUST be after the DBrelations are set so that file from ALL added records are included!
51
 *
52
 * Write export
53
 * $out = $this->export->compileMemoryToFileContent();
54
 */
55
56
/**
57
 * T3D file Export library (TYPO3 Record Document)
58
 */
59
class Export extends ImportExport
60
{
61
    /**
62
     * 1MB max file size
63
     *
64
     * @var int
65
     */
66
    public $maxFileSize = 1000000;
67
68
    /**
69
     * 1MB max record size
70
     *
71
     * @var int
72
     */
73
    public $maxRecordSize = 1000000;
74
75
    /**
76
     * 10MB max export size
77
     *
78
     * @var int
79
     */
80
    public $maxExportSize = 10000000;
81
82
    /**
83
     * Set  by user: If set, compression in t3d files is disabled
84
     *
85
     * @var bool
86
     */
87
    public $dontCompress = false;
88
89
    /**
90
     * If set, HTML file resources are included.
91
     *
92
     * @var bool
93
     */
94
    public $includeExtFileResources = false;
95
96
    /**
97
     * Files with external media (HTML/css style references inside)
98
     *
99
     * @var string
100
     */
101
    public $extFileResourceExtensions = 'html,htm,css';
102
103
    /**
104
     * Keys are [recordname], values are an array of fields to be included
105
     * in the export
106
     *
107
     * @var array
108
     */
109
    protected $recordTypesIncludeFields = [];
110
111
    /**
112
     * Default array of fields to be included in the export
113
     *
114
     * @var array
115
     */
116
    protected $defaultRecordIncludeFields = ['uid', 'pid'];
117
118
    /**
119
     * @var bool
120
     */
121
    protected $saveFilesOutsideExportFile = false;
122
123
    /**
124
     * @var string|null
125
     */
126
    protected $temporaryFilesPathForExport = null;
127
128
    /**************************
129
     * Initialize
130
     *************************/
131
132
    /**
133
     * Init the object
134
     *
135
     * @param bool $dontCompress If set, compression of t3d files is disabled
136
     */
137
    public function init($dontCompress = false)
138
    {
139
        parent::init();
140
        $this->dontCompress = $dontCompress;
141
        $this->mode = 'export';
142
    }
143
144
    /**************************
145
     * Export / Init + Meta Data
146
     *************************/
147
148
    /**
149
     * Set header basics
150
     */
151
    public function setHeaderBasics()
152
    {
153
        // Initializing:
154
        if (is_array($this->softrefCfg)) {
155
            foreach ($this->softrefCfg as $key => $value) {
156
                if (!strlen($value['mode'])) {
157
                    unset($this->softrefCfg[$key]);
158
                }
159
            }
160
        }
161
        // Setting in header memory:
162
        // Version of file format
163
        $this->dat['header']['XMLversion'] = '1.0';
164
        // Initialize meta data array (to put it in top of file)
165
        $this->dat['header']['meta'] = [];
166
        // Add list of tables to consider static
167
        $this->dat['header']['relStaticTables'] = $this->relStaticTables;
168
        // The list of excluded records
169
        $this->dat['header']['excludeMap'] = $this->excludeMap;
170
        // Soft Reference mode for elements
171
        $this->dat['header']['softrefCfg'] = $this->softrefCfg;
172
        // List of extensions the import depends on.
173
        $this->dat['header']['extensionDependencies'] = $this->extensionDependencies;
174
        $this->dat['header']['charset'] = 'utf-8';
175
    }
176
177
    /**
178
     * Set charset
179
     *
180
     * @param string $charset Charset for the content in the export. During import the character set will be converted if the target system uses another charset.
181
     */
182
    public function setCharset($charset)
183
    {
184
        $this->dat['header']['charset'] = $charset;
185
    }
186
187
    /**
188
     * Sets meta data
189
     *
190
     * @param string $title Title of the export
191
     * @param string $description Description of the export
192
     * @param string $notes Notes about the contents
193
     * @param string $packager_username Backend Username of the packager (the guy making the export)
194
     * @param string $packager_name Real name of the packager
195
     * @param string $packager_email Email of the packager
196
     */
197
    public function setMetaData($title, $description, $notes, $packager_username, $packager_name, $packager_email)
198
    {
199
        $this->dat['header']['meta'] = [
200
            'title' => $title,
201
            'description' => $description,
202
            'notes' => $notes,
203
            'packager_username' => $packager_username,
204
            'packager_name' => $packager_name,
205
            'packager_email' => $packager_email,
206
            'TYPO3_version' => TYPO3_version,
207
            'created' => strftime('%A %e. %B %Y', $GLOBALS['EXEC_TIME'])
208
        ];
209
    }
210
211
    /**
212
     * Option to enable having the files not included in the export file.
213
     * The files are saved to a temporary folder instead.
214
     *
215
     * @param bool $saveFilesOutsideExportFile
216
     * @see getTemporaryFilesPathForExport()
217
     */
218
    public function setSaveFilesOutsideExportFile($saveFilesOutsideExportFile)
219
    {
220
        $this->saveFilesOutsideExportFile = $saveFilesOutsideExportFile;
221
    }
222
223
    /**************************
224
     * Export / Init Page tree
225
     *************************/
226
227
    /**
228
     * Sets the page-tree array in the export header and returns the array in a flattened version
229
     *
230
     * @param array $idH Hierarchy of ids, the page tree: array([uid] => array("uid" => [uid], "subrow" => array(.....)), [uid] => ....)
231
     * @return array The hierarchical page tree converted to a one-dimensional list of pages
232
     */
233
    public function setPageTree($idH)
234
    {
235
        $this->dat['header']['pagetree'] = $this->unsetExcludedSections($idH);
236
        return $this->flatInversePageTree($this->dat['header']['pagetree']);
237
    }
238
239
    /**
240
     * Removes entries in the page tree which are found in ->excludeMap[]
241
     *
242
     * @param array $idH Page uid hierarchy
243
     * @return array Modified input array
244
     * @access private
245
     * @see setPageTree()
246
     */
247
    public function unsetExcludedSections($idH)
248
    {
249
        if (is_array($idH)) {
250
            foreach ($idH as $k => $v) {
251
                if ($this->excludeMap['pages:' . $idH[$k]['uid']]) {
252
                    unset($idH[$k]);
253
                } elseif (is_array($idH[$k]['subrow'])) {
254
                    $idH[$k]['subrow'] = $this->unsetExcludedSections($idH[$k]['subrow']);
255
                }
256
            }
257
        }
258
        return $idH;
259
    }
260
261
    /**************************
262
     * Export
263
     *************************/
264
265
    /**
266
     * Sets the fields of record types to be included in the export
267
     *
268
     * @param array $recordTypesIncludeFields Keys are [recordname], values are an array of fields to be included in the export
269
     * @throws Exception if an array value is not type of array
270
     */
271
    public function setRecordTypesIncludeFields(array $recordTypesIncludeFields)
272
    {
273
        foreach ($recordTypesIncludeFields as $table => $fields) {
274
            if (!is_array($fields)) {
275
                throw new Exception('The include fields for record type ' . htmlspecialchars($table) . ' are not defined by an array.', 1391440658);
276
            }
277
            $this->setRecordTypeIncludeFields($table, $fields);
278
        }
279
    }
280
281
    /**
282
     * Sets the fields of a record type to be included in the export
283
     *
284
     * @param string $table The record type
285
     * @param array $fields The fields to be included
286
     */
287
    public function setRecordTypeIncludeFields($table, array $fields)
288
    {
289
        $this->recordTypesIncludeFields[$table] = $fields;
290
    }
291
292
    /**
293
     * Adds the record $row from $table.
294
     * No checking for relations done here. Pure data.
295
     *
296
     * @param string $table Table name
297
     * @param array $row Record row.
298
     * @param int $relationLevel (Internal) if the record is added as a relation, this is set to the "level" it was on.
299
     */
300
    public function export_addRecord($table, $row, $relationLevel = 0)
301
    {
302
        BackendUtility::workspaceOL($table, $row);
303
        if ($this->excludeDisabledRecords && !$this->isActive($table, $row['uid'])) {
304
            return;
305
        }
306
        if ((string)$table !== '' && is_array($row) && $row['uid'] > 0 && !$this->excludeMap[$table . ':' . $row['uid']]) {
307
            if ($this->checkPID($table === 'pages' ? $row['uid'] : $row['pid'])) {
308
                if (!isset($this->dat['records'][$table . ':' . $row['uid']])) {
309
                    // Prepare header info:
310
                    $row = $this->filterRecordFields($table, $row);
311
                    $headerInfo = [];
312
                    $headerInfo['uid'] = $row['uid'];
313
                    $headerInfo['pid'] = $row['pid'];
314
                    $headerInfo['title'] = GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($table, $row), 40);
315
                    $headerInfo['size'] = strlen(serialize($row));
316
                    if ($relationLevel) {
317
                        $headerInfo['relationLevel'] = $relationLevel;
318
                    }
319
                    // If record content is not too large in size, set the header content and add the rest:
320
                    if ($headerInfo['size'] < $this->maxRecordSize) {
321
                        // Set the header summary:
322
                        $this->dat['header']['records'][$table][$row['uid']] = $headerInfo;
323
                        // Create entry in the PID lookup:
324
                        $this->dat['header']['pid_lookup'][$row['pid']][$table][$row['uid']] = 1;
325
                        // Initialize reference index object:
326
                        $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class);
327
                        $refIndexObj->enableRuntimeCache();
328
                        // Yes to workspace overlays for exporting....
329
                        $refIndexObj->WSOL = true;
330
                        $relations = $refIndexObj->getRelations($table, $row);
331
                        $relations = $this->fixFileIDsInRelations($relations);
332
                        $relations = $this->removeSoftrefsHavingTheSameDatabaseRelation($relations);
333
                        // Data:
334
                        $this->dat['records'][$table . ':' . $row['uid']] = [];
335
                        $this->dat['records'][$table . ':' . $row['uid']]['data'] = $row;
336
                        $this->dat['records'][$table . ':' . $row['uid']]['rels'] = $relations;
337
                        // Add information about the relations in the record in the header:
338
                        $this->dat['header']['records'][$table][$row['uid']]['rels'] = $this->flatDBrels($this->dat['records'][$table . ':' . $row['uid']]['rels']);
339
                        // Add information about the softrefs to header:
340
                        $this->dat['header']['records'][$table][$row['uid']]['softrefs'] = $this->flatSoftRefs($this->dat['records'][$table . ':' . $row['uid']]['rels']);
341
                    } else {
342
                        $this->error('Record ' . $table . ':' . $row['uid'] . ' was larger than maxRecordSize (' . GeneralUtility::formatSize($this->maxRecordSize) . ')');
343
                    }
344
                } else {
345
                    $this->error('Record ' . $table . ':' . $row['uid'] . ' already added.');
346
                }
347
            } else {
348
                $this->error('Record ' . $table . ':' . $row['uid'] . ' was outside your DB mounts!');
349
            }
350
        }
351
    }
352
353
    /**
354
     * This changes the file reference ID from a hash based on the absolute file path
355
     * (coming from ReferenceIndex) to a hash based on the relative file path.
356
     *
357
     * @param array $relations
358
     * @return array
359
     */
360
    protected function fixFileIDsInRelations(array $relations)
361
    {
362
        foreach ($relations as $field => $relation) {
363
            if (isset($relation['type']) && $relation['type'] === 'file') {
364
                foreach ($relation['newValueFiles'] as $key => $fileRelationData) {
365
                    $absoluteFilePath = $fileRelationData['ID_absFile'];
366
                    if (GeneralUtility::isFirstPartOfStr($absoluteFilePath, PATH_site)) {
367
                        $relatedFilePath = PathUtility::stripPathSitePrefix($absoluteFilePath);
368
                        $relations[$field]['newValueFiles'][$key]['ID'] = md5($relatedFilePath);
369
                    }
370
                }
371
            }
372
            if ($relation['type'] === 'flex') {
373
                if (is_array($relation['flexFormRels']['file'])) {
374
                    foreach ($relation['flexFormRels']['file'] as $key => $subList) {
375
                        foreach ($subList as $subKey => $fileRelationData) {
376
                            $absoluteFilePath = $fileRelationData['ID_absFile'];
377
                            if (GeneralUtility::isFirstPartOfStr($absoluteFilePath, PATH_site)) {
378
                                $relatedFilePath = PathUtility::stripPathSitePrefix($absoluteFilePath);
379
                                $relations[$field]['flexFormRels']['file'][$key][$subKey]['ID'] = md5($relatedFilePath);
380
                            }
381
                        }
382
                    }
383
                }
384
            }
385
        }
386
        return $relations;
387
    }
388
389
    /**
390
     * Relations could contain db relations to sys_file records. Some configuration combinations of TCA and
391
     * SoftReferenceIndex create also softref relation entries for the identical file. This results
392
     * in double included files, one in array "files" and one in array "file_fal".
393
     * This function checks the relations for this double inclusions and removes the redundant softref relation.
394
     *
395
     * @param array $relations
396
     * @return array
397
     */
398
    protected function removeSoftrefsHavingTheSameDatabaseRelation($relations)
399
    {
400
        $fixedRelations = [];
401
        foreach ($relations as $field => $relation) {
402
            $newRelation = $relation;
403
            if (isset($newRelation['type']) && $newRelation['type'] === 'db') {
404
                foreach ($newRelation['itemArray'] as $key => $dbRelationData) {
405
                    if ($dbRelationData['table'] === 'sys_file') {
406
                        if (isset($newRelation['softrefs']['keys']['typolink'])) {
407
                            foreach ($newRelation['softrefs']['keys']['typolink'] as $softrefKey => $softRefData) {
408
                                if ($softRefData['subst']['type'] === 'file') {
409
                                    $file = ResourceFactory::getInstance()->retrieveFileOrFolderObject($softRefData['subst']['relFileName']);
410
                                    if ($file instanceof File) {
411
                                        if ($file->getUid() == $dbRelationData['id']) {
412
                                            unset($newRelation['softrefs']['keys']['typolink'][$softrefKey]);
413
                                        }
414
                                    }
415
                                }
416
                            }
417
                            if (empty($newRelation['softrefs']['keys']['typolink'])) {
418
                                unset($newRelation['softrefs']);
419
                            }
420
                        }
421
                    }
422
                }
423
            }
424
            $fixedRelations[$field] = $newRelation;
425
        }
426
        return $fixedRelations;
427
    }
428
429
    /**
430
     * This analyses the existing added records, finds all database relations to records and adds these records to the export file.
431
     * This function can be called repeatedly until it returns an empty array.
432
     * In principle it should not allow to infinite recursivity, but you better set a limit...
433
     * Call this BEFORE the ext_addFilesFromRelations (so files from added relations are also included of course)
434
     *
435
     * @param int $relationLevel Recursion level
436
     * @return array overview of relations found and added: Keys [table]:[uid], values array with table and id
437
     * @see export_addFilesFromRelations()
438
     */
439
    public function export_addDBRelations($relationLevel = 0)
440
    {
441
        // Traverse all "rels" registered for "records"
442
        if (!is_array($this->dat['records'])) {
443
            $this->error('There were no records available.');
444
            return [];
445
        }
446
        $addR = [];
447
        foreach ($this->dat['records'] as $k => $value) {
448
            if (!is_array($this->dat['records'][$k])) {
449
                continue;
450
            }
451
            foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) {
452
                // For all DB types of relations:
453
                if ($vR['type'] === 'db') {
454
                    foreach ($vR['itemArray'] as $fI) {
455
                        $this->export_addDBRelations_registerRelation($fI, $addR);
456
                    }
457
                }
458
                // For all flex/db types of relations:
459
                if ($vR['type'] === 'flex') {
460
                    // DB relations in flex form fields:
461
                    if (is_array($vR['flexFormRels']['db'])) {
462
                        foreach ($vR['flexFormRels']['db'] as $subList) {
463
                            foreach ($subList as $fI) {
464
                                $this->export_addDBRelations_registerRelation($fI, $addR);
465
                            }
466
                        }
467
                    }
468
                    // DB oriented soft references in flex form fields:
469
                    if (is_array($vR['flexFormRels']['softrefs'])) {
470
                        foreach ($vR['flexFormRels']['softrefs'] as $subList) {
471
                            foreach ($subList['keys'] as $spKey => $elements) {
472
                                foreach ($elements as $el) {
473
                                    if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
474
                                        list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
475
                                        $fI = [
476
                                            'table' => $tempTable,
477
                                            'id' => $tempUid
478
                                        ];
479
                                        $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
480
                                    }
481
                                }
482
                            }
483
                        }
484
                    }
485
                }
486
                // In any case, if there are soft refs:
487
                if (is_array($vR['softrefs']['keys'])) {
488
                    foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
489
                        foreach ($elements as $el) {
490
                            if ($el['subst']['type'] === 'db' && $this->includeSoftref($el['subst']['tokenID'])) {
491
                                list($tempTable, $tempUid) = explode(':', $el['subst']['recordRef']);
492
                                $fI = [
493
                                    'table' => $tempTable,
494
                                    'id' => $tempUid
495
                                ];
496
                                $this->export_addDBRelations_registerRelation($fI, $addR, $el['subst']['tokenID']);
497
                            }
498
                        }
499
                    }
500
                }
501
            }
502
        }
503
504
        // Now, if there were new records to add, do so:
505
        if (!empty($addR)) {
506
            foreach ($addR as $fI) {
507
                // Get and set record:
508
                $row = BackendUtility::getRecord($fI['table'], $fI['id']);
509
                if (is_array($row)) {
510
                    $this->export_addRecord($fI['table'], $row, $relationLevel + 1);
511
                }
512
                // Set status message
513
                // Relation pointers always larger than zero except certain "select" types with
514
                // negative values pointing to uids - but that is not supported here.
515
                if ($fI['id'] > 0) {
516
                    $rId = $fI['table'] . ':' . $fI['id'];
517
                    if (!isset($this->dat['records'][$rId])) {
518
                        $this->dat['records'][$rId] = 'NOT_FOUND';
519
                        $this->error('Relation record ' . $rId . ' was not found!');
520
                    }
521
                }
522
            }
523
        }
524
        // Return overview of relations found and added
525
        return $addR;
526
    }
527
528
    /**
529
     * Helper function for export_addDBRelations()
530
     *
531
     * @param array $fI Array with table/id keys to add
532
     * @param array $addR Add array, passed by reference to be modified
533
     * @param string $tokenID Softref Token ID, if applicable.
534
     * @see export_addDBRelations()
535
     */
536
    public function export_addDBRelations_registerRelation($fI, &$addR, $tokenID = '')
537
    {
538
        $rId = $fI['table'] . ':' . $fI['id'];
539
        if (
540
            isset($GLOBALS['TCA'][$fI['table']]) && !$this->isTableStatic($fI['table']) && !$this->isExcluded($fI['table'], $fI['id'])
541
            && (!$tokenID || $this->includeSoftref($tokenID)) && $this->inclRelation($fI['table'])
542
        ) {
543
            if (!isset($this->dat['records'][$rId])) {
544
                // Set this record to be included since it is not already.
545
                $addR[$rId] = $fI;
546
            }
547
        }
548
    }
549
550
    /**
551
     * This adds all files in relations.
552
     * Call this method AFTER adding all records including relations.
553
     *
554
     * @see export_addDBRelations()
555
     */
556
    public function export_addFilesFromRelations()
557
    {
558
        // Traverse all "rels" registered for "records"
559
        if (!is_array($this->dat['records'])) {
560
            $this->error('There were no records available.');
561
            return;
562
        }
563
        foreach ($this->dat['records'] as $k => $value) {
564
            if (!isset($this->dat['records'][$k]['rels']) || !is_array($this->dat['records'][$k]['rels'])) {
565
                continue;
566
            }
567
            foreach ($this->dat['records'][$k]['rels'] as $fieldname => $vR) {
568
                // For all file type relations:
569
                if ($vR['type'] === 'file') {
570
                    foreach ($vR['newValueFiles'] as $key => $fI) {
571
                        $this->export_addFile($fI, $k, $fieldname);
572
                        // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
573
                        unset($this->dat['records'][$k]['rels'][$fieldname]['newValueFiles'][$key]['ID_absFile']);
574
                    }
575
                }
576
                // For all flex type relations:
577
                if ($vR['type'] === 'flex') {
578
                    if (is_array($vR['flexFormRels']['file'])) {
579
                        foreach ($vR['flexFormRels']['file'] as $key => $subList) {
580
                            foreach ($subList as $subKey => $fI) {
581
                                $this->export_addFile($fI, $k, $fieldname);
582
                                // Remove the absolute reference to the file so it doesn't expose absolute paths from source server:
583
                                unset($this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['file'][$key][$subKey]['ID_absFile']);
584
                            }
585
                        }
586
                    }
587
                    // DB oriented soft references in flex form fields:
588
                    if (is_array($vR['flexFormRels']['softrefs'])) {
589
                        foreach ($vR['flexFormRels']['softrefs'] as $key => $subList) {
590
                            foreach ($subList['keys'] as $spKey => $elements) {
591
                                foreach ($elements as $subKey => $el) {
592
                                    if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
593
                                        // Create abs path and ID for file:
594
                                        $ID_absFile = GeneralUtility::getFileAbsFileName(PATH_site . $el['subst']['relFileName']);
595
                                        $ID = md5($el['subst']['relFileName']);
596
                                        if ($ID_absFile) {
597
                                            if (!$this->dat['files'][$ID]) {
598
                                                $fI = [
599
                                                    'filename' => PathUtility::basename($ID_absFile),
600
                                                    'ID_absFile' => $ID_absFile,
601
                                                    'ID' => $ID,
602
                                                    'relFileName' => $el['subst']['relFileName']
603
                                                ];
604
                                                $this->export_addFile($fI, '_SOFTREF_');
605
                                            }
606
                                            $this->dat['records'][$k]['rels'][$fieldname]['flexFormRels']['softrefs'][$key]['keys'][$spKey][$subKey]['file_ID'] = $ID;
607
                                        }
608
                                    }
609
                                }
610
                            }
611
                        }
612
                    }
613
                }
614
                // In any case, if there are soft refs:
615
                if (is_array($vR['softrefs']['keys'])) {
616
                    foreach ($vR['softrefs']['keys'] as $spKey => $elements) {
617
                        foreach ($elements as $subKey => $el) {
618
                            if ($el['subst']['type'] === 'file' && $this->includeSoftref($el['subst']['tokenID'])) {
619
                                // Create abs path and ID for file:
620
                                $ID_absFile = GeneralUtility::getFileAbsFileName(PATH_site . $el['subst']['relFileName']);
621
                                $ID = md5($el['subst']['relFileName']);
622
                                if ($ID_absFile) {
623
                                    if (!$this->dat['files'][$ID]) {
624
                                        $fI = [
625
                                            'filename' => PathUtility::basename($ID_absFile),
626
                                            'ID_absFile' => $ID_absFile,
627
                                            'ID' => $ID,
628
                                            'relFileName' => $el['subst']['relFileName']
629
                                        ];
630
                                        $this->export_addFile($fI, '_SOFTREF_');
631
                                    }
632
                                    $this->dat['records'][$k]['rels'][$fieldname]['softrefs']['keys'][$spKey][$subKey]['file_ID'] = $ID;
633
                                }
634
                            }
635
                        }
636
                    }
637
                }
638
            }
639
        }
640
    }
641
642
    /**
643
     * This adds all files from sys_file records
644
     */
645
    public function export_addFilesFromSysFilesRecords()
646
    {
647
        if (!isset($this->dat['header']['records']['sys_file']) || !is_array($this->dat['header']['records']['sys_file'])) {
648
            return;
649
        }
650
        foreach ($this->dat['header']['records']['sys_file'] as $sysFileUid => $_) {
651
            $recordData = $this->dat['records']['sys_file:' . $sysFileUid]['data'];
652
            $file = ResourceFactory::getInstance()->createFileObject($recordData);
653
            $this->export_addSysFile($file);
654
        }
655
    }
656
657
    /**
658
     * Adds a files content from a sys file record to the export memory
659
     *
660
     * @param File $file
661
     */
662
    public function export_addSysFile(File $file)
663
    {
664
        if ($file->getProperty('size') >= $this->maxFileSize) {
665
            $this->error('File ' . $file->getPublicUrl() . ' was larger (' . GeneralUtility::formatSize($file->getProperty('size')) . ') than the maxFileSize (' . GeneralUtility::formatSize($this->maxFileSize) . ')! Skipping.');
666
            return;
667
        }
668
        $fileContent = '';
669
        try {
670
            if (!$this->saveFilesOutsideExportFile) {
671
                $fileContent = $file->getContents();
672
            } else {
673
                $file->checkActionPermission('read');
674
            }
675
        } catch (\Exception $e) {
676
            $this->error('Error when trying to add file ' . $file->getCombinedIdentifier() . ': ' . $e->getMessage());
677
            return;
678
        }
679
        $fileUid = $file->getUid();
680
        $fileInfo = $file->getStorage()->getFileInfo($file);
681
        $fileSize = (int)$fileInfo['size'];
682
        if ($fileSize !== (int)$file->getProperty('size')) {
683
            $this->error('File size of ' . $file->getCombinedIdentifier() . ' is not up-to-date in index! File added with current size.');
684
            $this->dat['records']['sys_file:' . $fileUid]['data']['size'] = $fileSize;
685
        }
686
        $fileSha1 = $file->getStorage()->hashFile($file, 'sha1');
687
        if ($fileSha1 !== $file->getProperty('sha1')) {
688
            $this->error('File sha1 hash of ' . $file->getCombinedIdentifier() . ' is not up-to-date in index! File added on current sha1.');
689
            $this->dat['records']['sys_file:' . $fileUid]['data']['sha1'] = $fileSha1;
690
        }
691
692
        $fileRec = [];
693
        $fileRec['filesize'] = $fileSize;
694
        $fileRec['filename'] = $file->getProperty('name');
695
        $fileRec['filemtime'] = $file->getProperty('modification_date');
696
697
        // build unique id based on the storage and the file identifier
698
        $fileId = md5($file->getStorage()->getUid() . ':' . $file->getProperty('identifier_hash'));
699
700
        // Setting this data in the header
701
        $this->dat['header']['files_fal'][$fileId] = $fileRec;
702
703
        if (!$this->saveFilesOutsideExportFile) {
704
            // ... and finally add the heavy stuff:
705
            $fileRec['content'] = $fileContent;
706
        } else {
707
            GeneralUtility::upload_copy_move($file->getForLocalProcessing(false), $this->getTemporaryFilesPathForExport() . $file->getProperty('sha1'));
708
        }
709
        $fileRec['content_sha1'] = $fileSha1;
710
711
        $this->dat['files_fal'][$fileId] = $fileRec;
712
    }
713
714
    /**
715
     * Adds a files content to the export memory
716
     *
717
     * @param array $fI File information with three keys: "filename" = filename without path, "ID_absFile" = absolute filepath to the file (including the filename), "ID" = md5 hash of "ID_absFile". "relFileName" is optional for files attached to records, but mandatory for soft referenced files (since the relFileName determines where such a file should be stored!)
718
     * @param string $recordRef If the file is related to a record, this is the id on the form [table]:[id]. Information purposes only.
719
     * @param string $fieldname If the file is related to a record, this is the field name it was related to. Information purposes only.
720
     */
721
    public function export_addFile($fI, $recordRef = '', $fieldname = '')
722
    {
723
        if (!@is_file($fI['ID_absFile'])) {
724
            $this->error($fI['ID_absFile'] . ' was not a file! Skipping.');
725
            return;
726
        }
727
        if (filesize($fI['ID_absFile']) >= $this->maxFileSize) {
728
            $this->error($fI['ID_absFile'] . ' was larger (' . GeneralUtility::formatSize(filesize($fI['ID_absFile'])) . ') than the maxFileSize (' . GeneralUtility::formatSize($this->maxFileSize) . ')! Skipping.');
729
            return;
730
        }
731
        $fileInfo = stat($fI['ID_absFile']);
732
        $fileRec = [];
733
        $fileRec['filesize'] = $fileInfo['size'];
734
        $fileRec['filename'] = PathUtility::basename($fI['ID_absFile']);
735
        $fileRec['filemtime'] = $fileInfo['mtime'];
736
        //for internal type file_reference
737
        $fileRec['relFileRef'] = PathUtility::stripPathSitePrefix($fI['ID_absFile']);
738
        if ($recordRef) {
739
            $fileRec['record_ref'] = $recordRef . '/' . $fieldname;
740
        }
741
        if ($fI['relFileName']) {
742
            $fileRec['relFileName'] = $fI['relFileName'];
743
        }
744
        // Setting this data in the header
745
        $this->dat['header']['files'][$fI['ID']] = $fileRec;
746
        // ... and for the recordlisting, why not let us know WHICH relations there was...
747
        if ($recordRef && $recordRef !== '_SOFTREF_') {
748
            $refParts = explode(':', $recordRef, 2);
749
            if (!is_array($this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'])) {
750
                $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'] = [];
751
            }
752
            $this->dat['header']['records'][$refParts[0]][$refParts[1]]['filerefs'][] = $fI['ID'];
753
        }
754
        $fileMd5 = md5_file($fI['ID_absFile']);
755
        if (!$this->saveFilesOutsideExportFile) {
756
            // ... and finally add the heavy stuff:
757
            $fileRec['content'] = file_get_contents($fI['ID_absFile']);
758
        } else {
759
            GeneralUtility::upload_copy_move($fI['ID_absFile'], $this->getTemporaryFilesPathForExport() . $fileMd5);
760
        }
761
        $fileRec['content_md5'] = $fileMd5;
762
        $this->dat['files'][$fI['ID']] = $fileRec;
763
        // For soft references, do further processing:
764
        if ($recordRef === '_SOFTREF_') {
765
            // RTE files?
766
            if ($RTEoriginal = $this->getRTEoriginalFilename(PathUtility::basename($fI['ID_absFile']))) {
767
                $RTEoriginal_absPath = PathUtility::dirname($fI['ID_absFile']) . '/' . $RTEoriginal;
768
                if (@is_file($RTEoriginal_absPath)) {
769
                    $RTEoriginal_ID = md5($RTEoriginal_absPath);
770
                    $fileInfo = stat($RTEoriginal_absPath);
771
                    $fileRec = [];
772
                    $fileRec['filesize'] = $fileInfo['size'];
773
                    $fileRec['filename'] = PathUtility::basename($RTEoriginal_absPath);
774
                    $fileRec['filemtime'] = $fileInfo['mtime'];
775
                    $fileRec['record_ref'] = '_RTE_COPY_ID:' . $fI['ID'];
776
                    $this->dat['header']['files'][$fI['ID']]['RTE_ORIG_ID'] = $RTEoriginal_ID;
777
                    // Setting this data in the header
778
                    $this->dat['header']['files'][$RTEoriginal_ID] = $fileRec;
779
                    $fileMd5 = md5_file($RTEoriginal_absPath);
780
                    if (!$this->saveFilesOutsideExportFile) {
781
                        // ... and finally add the heavy stuff:
782
                        $fileRec['content'] = file_get_contents($RTEoriginal_absPath);
783
                    } else {
784
                        GeneralUtility::upload_copy_move($RTEoriginal_absPath, $this->getTemporaryFilesPathForExport() . $fileMd5);
785
                    }
786
                    $fileRec['content_md5'] = $fileMd5;
787
                    $this->dat['files'][$RTEoriginal_ID] = $fileRec;
788
                } else {
789
                    $this->error('RTE original file "' . PathUtility::stripPathSitePrefix($RTEoriginal_absPath) . '" was not found!');
790
                }
791
            }
792
            // Files with external media?
793
            // This is only done with files grabbed by a softreference parser since it is deemed improbable that hard-referenced files should undergo this treatment.
794
            $html_fI = pathinfo(PathUtility::basename($fI['ID_absFile']));
795
            if ($this->includeExtFileResources && GeneralUtility::inList($this->extFileResourceExtensions, strtolower($html_fI['extension']))) {
796
                $uniquePrefix = '###' . md5($GLOBALS['EXEC_TIME']) . '###';
797
                if (strtolower($html_fI['extension']) === 'css') {
798
                    $prefixedMedias = explode($uniquePrefix, preg_replace('/(url[[:space:]]*\\([[:space:]]*["\']?)([^"\')]*)(["\']?[[:space:]]*\\))/i', '\\1' . $uniquePrefix . '\\2' . $uniquePrefix . '\\3', $fileRec['content']));
799
                } else {
800
                    // html, htm:
801
                    $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
802
                    $prefixedMedias = explode($uniquePrefix, $htmlParser->prefixResourcePath($uniquePrefix, $fileRec['content'], [], $uniquePrefix));
803
                }
804
                $htmlResourceCaptured = false;
805
                foreach ($prefixedMedias as $k => $v) {
806
                    if ($k % 2) {
807
                        $EXTres_absPath = GeneralUtility::resolveBackPath(PathUtility::dirname($fI['ID_absFile']) . '/' . $v);
808
                        $EXTres_absPath = GeneralUtility::getFileAbsFileName($EXTres_absPath);
809
                        if ($EXTres_absPath && GeneralUtility::isFirstPartOfStr($EXTres_absPath, PATH_site . $this->fileadminFolderName . '/') && @is_file($EXTres_absPath)) {
810
                            $htmlResourceCaptured = true;
811
                            $EXTres_ID = md5($EXTres_absPath);
812
                            $this->dat['header']['files'][$fI['ID']]['EXT_RES_ID'][] = $EXTres_ID;
813
                            $prefixedMedias[$k] = '{EXT_RES_ID:' . $EXTres_ID . '}';
814
                            // Add file to memory if it is not set already:
815
                            if (!isset($this->dat['header']['files'][$EXTres_ID])) {
816
                                $fileInfo = stat($EXTres_absPath);
817
                                $fileRec = [];
818
                                $fileRec['filesize'] = $fileInfo['size'];
819
                                $fileRec['filename'] = PathUtility::basename($EXTres_absPath);
820
                                $fileRec['filemtime'] = $fileInfo['mtime'];
821
                                $fileRec['record_ref'] = '_EXT_PARENT_:' . $fI['ID'];
822
                                // Media relative to the HTML file.
823
                                $fileRec['parentRelFileName'] = $v;
824
                                // Setting this data in the header
825
                                $this->dat['header']['files'][$EXTres_ID] = $fileRec;
826
                                // ... and finally add the heavy stuff:
827
                                $fileRec['content'] = file_get_contents($EXTres_absPath);
828
                                $fileRec['content_md5'] = md5($fileRec['content']);
829
                                $this->dat['files'][$EXTres_ID] = $fileRec;
830
                            }
831
                        }
832
                    }
833
                }
834
                if ($htmlResourceCaptured) {
835
                    $this->dat['files'][$fI['ID']]['tokenizedContent'] = implode('', $prefixedMedias);
836
                }
837
            }
838
        }
839
    }
840
841
    /**
842
     * If saveFilesOutsideExportFile is enabled, this function returns the path
843
     * where the files referenced in the export are copied to.
844
     *
845
     * @return string
846
     * @throws \RuntimeException
847
     * @see setSaveFilesOutsideExportFile()
848
     */
849
    public function getTemporaryFilesPathForExport()
850
    {
851
        if (!$this->saveFilesOutsideExportFile) {
852
            throw new \RuntimeException('You need to set saveFilesOutsideExportFile to TRUE before you want to get the temporary files path for export.', 1401205213);
853
        }
854
        if ($this->temporaryFilesPathForExport === null) {
855
            $temporaryFolderName = $this->getTemporaryFolderName();
856
            $this->temporaryFilesPathForExport = $temporaryFolderName . '/';
857
        }
858
        return $this->temporaryFilesPathForExport;
859
    }
860
861
    /**
862
     * DB relations flattend to 1-dim array.
863
     * The list will be unique, no table/uid combination will appear twice.
864
     *
865
     * @param array $dbrels 2-dim Array of database relations organized by table key
866
     * @return array 1-dim array where entries are table:uid and keys are array with table/id
867
     */
868
    public function flatDBrels($dbrels)
869
    {
870
        $list = [];
871
        foreach ($dbrels as $dat) {
872
            if ($dat['type'] === 'db') {
873
                foreach ($dat['itemArray'] as $i) {
874
                    $list[$i['table'] . ':' . $i['id']] = $i;
875
                }
876
            }
877
            if ($dat['type'] === 'flex' && is_array($dat['flexFormRels']['db'])) {
878
                foreach ($dat['flexFormRels']['db'] as $subList) {
879
                    foreach ($subList as $i) {
880
                        $list[$i['table'] . ':' . $i['id']] = $i;
881
                    }
882
                }
883
            }
884
        }
885
        return $list;
886
    }
887
888
    /**
889
     * Soft References flattend to 1-dim array.
890
     *
891
     * @param array $dbrels 2-dim Array of database relations organized by table key
892
     * @return array 1-dim array where entries are arrays with properties of the soft link found and keys are a unique combination of field, spKey, structure path if applicable and token ID
893
     */
894
    public function flatSoftRefs($dbrels)
895
    {
896
        $list = [];
897
        foreach ($dbrels as $field => $dat) {
898
            if (is_array($dat['softrefs']['keys'])) {
899
                foreach ($dat['softrefs']['keys'] as $spKey => $elements) {
900
                    if (is_array($elements)) {
901
                        foreach ($elements as $subKey => $el) {
902
                            $lKey = $field . ':' . $spKey . ':' . $subKey;
903
                            $list[$lKey] = array_merge(['field' => $field, 'spKey' => $spKey], $el);
904
                            // Add file_ID key to header - slightly "risky" way of doing this because if the calculation
905
                            // changes for the same value in $this->records[...] this will not work anymore!
906
                            if ($el['subst'] && $el['subst']['relFileName']) {
907
                                $list[$lKey]['file_ID'] = md5(PATH_site . $el['subst']['relFileName']);
908
                            }
909
                        }
910
                    }
911
                }
912
            }
913
            if ($dat['type'] === 'flex' && is_array($dat['flexFormRels']['softrefs'])) {
914
                foreach ($dat['flexFormRels']['softrefs'] as $structurePath => $subSoftrefs) {
915
                    if (is_array($subSoftrefs['keys'])) {
916
                        foreach ($subSoftrefs['keys'] as $spKey => $elements) {
917
                            foreach ($elements as $subKey => $el) {
918
                                $lKey = $field . ':' . $structurePath . ':' . $spKey . ':' . $subKey;
919
                                $list[$lKey] = array_merge(['field' => $field, 'spKey' => $spKey, 'structurePath' => $structurePath], $el);
920
                                // Add file_ID key to header - slightly "risky" way of doing this because if the calculation
921
                                // changes for the same value in $this->records[...] this will not work anymore!
922
                                if ($el['subst'] && $el['subst']['relFileName']) {
923
                                    $list[$lKey]['file_ID'] = md5(PATH_site . $el['subst']['relFileName']);
924
                                }
925
                            }
926
                        }
927
                    }
928
                }
929
            }
930
        }
931
        return $list;
932
    }
933
934
    /**
935
     * If include fields for a specific record type are set, the data
936
     * are filtered out with fields are not included in the fields.
937
     *
938
     * @param string $table The record type to be filtered
939
     * @param array $row The data to be filtered
940
     * @return array The filtered record row
941
     */
942
    protected function filterRecordFields($table, array $row)
943
    {
944
        if (isset($this->recordTypesIncludeFields[$table])) {
945
            $includeFields = array_unique(array_merge(
946
                $this->recordTypesIncludeFields[$table],
947
                $this->defaultRecordIncludeFields
948
            ));
949
            $newRow = [];
950
            foreach ($row as $key => $value) {
951
                if (in_array($key, $includeFields)) {
952
                    $newRow[$key] = $value;
953
                }
954
            }
955
        } else {
956
            $newRow = $row;
957
        }
958
        return $newRow;
959
    }
960
961
    /**************************
962
     * File Output
963
     *************************/
964
965
    /**
966
     * This compiles and returns the data content for an exported file
967
     *
968
     * @param string $type Type of output; "xml" gives xml, otherwise serialized array, possibly compressed.
969
     * @return string The output file stream
970
     */
971
    public function compileMemoryToFileContent($type = '')
972
    {
973
        if ($type === 'xml') {
974
            $out = $this->createXML();
975
        } else {
976
            $compress = $this->doOutputCompress();
977
            $out = '';
978
            // adding header:
979
            $out .= $this->addFilePart(serialize($this->dat['header']), $compress);
0 ignored issues
show
Bug introduced by
serialize($this->dat['header']) of type string is incompatible with the type array expected by parameter $data of TYPO3\CMS\Impexp\Export::addFilePart(). ( Ignorable by Annotation )

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

979
            $out .= $this->addFilePart(/** @scrutinizer ignore-type */ serialize($this->dat['header']), $compress);
Loading history...
980
            // adding records:
981
            $out .= $this->addFilePart(serialize($this->dat['records']), $compress);
982
            // adding files:
983
            $out .= $this->addFilePart(serialize($this->dat['files']), $compress);
984
            // adding files_fal:
985
            $out .= $this->addFilePart(serialize($this->dat['files_fal']), $compress);
986
        }
987
        return $out;
988
    }
989
990
    /**
991
     * Creates XML string from input array
992
     *
993
     * @return string XML content
994
     */
995
    public function createXML()
996
    {
997
        // Options:
998
        $options = [
999
            'alt_options' => [
1000
                '/header' => [
1001
                    'disableTypeAttrib' => true,
1002
                    'clearStackPath' => true,
1003
                    'parentTagMap' => [
1004
                        'files' => 'file',
1005
                        'files_fal' => 'file',
1006
                        'records' => 'table',
1007
                        'table' => 'rec',
1008
                        'rec:rels' => 'relations',
1009
                        'relations' => 'element',
1010
                        'filerefs' => 'file',
1011
                        'pid_lookup' => 'page_contents',
1012
                        'header:relStaticTables' => 'static_tables',
1013
                        'static_tables' => 'tablename',
1014
                        'excludeMap' => 'item',
1015
                        'softrefCfg' => 'softrefExportMode',
1016
                        'extensionDependencies' => 'extkey',
1017
                        'softrefs' => 'softref_element'
1018
                    ],
1019
                    'alt_options' => [
1020
                        '/pagetree' => [
1021
                            'disableTypeAttrib' => true,
1022
                            'useIndexTagForNum' => 'node',
1023
                            'parentTagMap' => [
1024
                                'node:subrow' => 'node'
1025
                            ]
1026
                        ],
1027
                        '/pid_lookup/page_contents' => [
1028
                            'disableTypeAttrib' => true,
1029
                            'parentTagMap' => [
1030
                                'page_contents' => 'table'
1031
                            ],
1032
                            'grandParentTagMap' => [
1033
                                'page_contents/table' => 'item'
1034
                            ]
1035
                        ]
1036
                    ]
1037
                ],
1038
                '/records' => [
1039
                    'disableTypeAttrib' => true,
1040
                    'parentTagMap' => [
1041
                        'records' => 'tablerow',
1042
                        'tablerow:data' => 'fieldlist',
1043
                        'tablerow:rels' => 'related',
1044
                        'related' => 'field',
1045
                        'field:itemArray' => 'relations',
1046
                        'field:newValueFiles' => 'filerefs',
1047
                        'field:flexFormRels' => 'flexform',
1048
                        'relations' => 'element',
1049
                        'filerefs' => 'file',
1050
                        'flexform:db' => 'db_relations',
1051
                        'flexform:file' => 'file_relations',
1052
                        'flexform:softrefs' => 'softref_relations',
1053
                        'softref_relations' => 'structurePath',
1054
                        'db_relations' => 'path',
1055
                        'file_relations' => 'path',
1056
                        'path' => 'element',
1057
                        'keys' => 'softref_key',
1058
                        'softref_key' => 'softref_element'
1059
                    ],
1060
                    'alt_options' => [
1061
                        '/records/tablerow/fieldlist' => [
1062
                            'useIndexTagForAssoc' => 'field'
1063
                        ]
1064
                    ]
1065
                ],
1066
                '/files' => [
1067
                    'disableTypeAttrib' => true,
1068
                    'parentTagMap' => [
1069
                        'files' => 'file'
1070
                    ]
1071
                ],
1072
                '/files_fal' => [
1073
                    'disableTypeAttrib' => true,
1074
                    'parentTagMap' => [
1075
                        'files_fal' => 'file'
1076
                    ]
1077
                ]
1078
            ]
1079
        ];
1080
        // Creating XML file from $outputArray:
1081
        $charset = $this->dat['header']['charset'] ?: 'utf-8';
1082
        $XML = '<?xml version="1.0" encoding="' . $charset . '" standalone="yes" ?>' . LF;
1083
        $XML .= GeneralUtility::array2xml($this->dat, '', 0, 'T3RecordDocument', 0, $options);
1084
        return $XML;
1085
    }
1086
1087
    /**
1088
     * Returns TRUE if the output should be compressed.
1089
     *
1090
     * @return bool TRUE if compression is possible AND requested.
1091
     */
1092
    public function doOutputCompress()
1093
    {
1094
        return $this->compress && !$this->dontCompress;
1095
    }
1096
1097
    /**
1098
     * Returns a content part for a filename being build.
1099
     *
1100
     * @param array $data Data to store in part
1101
     * @param bool $compress Compress file?
1102
     * @return string Content stream.
1103
     */
1104
    public function addFilePart($data, $compress = false)
1105
    {
1106
        if ($compress) {
1107
            $data = gzcompress($data);
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type string expected by parameter $data of gzcompress(). ( Ignorable by Annotation )

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

1107
            $data = gzcompress(/** @scrutinizer ignore-type */ $data);
Loading history...
1108
        }
1109
        return md5($data) . ':' . ($compress ? '1' : '0') . ':' . str_pad(strlen($data), 10, '0', STR_PAD_LEFT) . ':' . $data . ':';
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type array; however, parameter $string of strlen() does only seem to accept string, 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

1109
        return md5($data) . ':' . ($compress ? '1' : '0') . ':' . str_pad(strlen(/** @scrutinizer ignore-type */ $data), 10, '0', STR_PAD_LEFT) . ':' . $data . ':';
Loading history...
Bug introduced by
It seems like $data can also be of type array; however, parameter $str of md5() does only seem to accept string, 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

1109
        return md5(/** @scrutinizer ignore-type */ $data) . ':' . ($compress ? '1' : '0') . ':' . str_pad(strlen($data), 10, '0', STR_PAD_LEFT) . ':' . $data . ':';
Loading history...
Bug introduced by
Are you sure $data of type string|array can be used in concatenation? ( Ignorable by Annotation )

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

1109
        return md5($data) . ':' . ($compress ? '1' : '0') . ':' . str_pad(strlen($data), 10, '0', STR_PAD_LEFT) . ':' . /** @scrutinizer ignore-type */ $data . ':';
Loading history...
1110
    }
1111
}
1112