Completed
Push — master ( c9f9df...5e296f )
by Marc
02:57
created

TemplatePaths   C

Complexity

Total Complexity 74

Size/Duplication

Total Lines 704
Duplicated Lines 3.13 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 22
loc 704
rs 5
c 0
b 0
f 0
wmc 74
lcom 1
cbo 0

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 3
A toArray() 0 8 1
A setTemplatePathAndFilename() 0 4 1
A setLayoutPathAndFilename() 0 4 1
A getTemplateRootPaths() 0 4 1
A setTemplateRootPaths() 0 5 1
A getLayoutRootPaths() 0 4 1
A setLayoutRootPaths() 0 5 1
A getPartialRootPaths() 0 4 1
A setPartialRootPaths() 0 5 1
A getFormat() 0 4 1
A setFormat() 0 4 1
B resolveTemplateFileForControllerAndActionAndFormat() 0 20 6
A resolveAvailableTemplateFiles() 0 8 2
A resolveAvailablePartialFiles() 0 4 1
A resolveAvailableLayoutFiles() 0 4 1
A resolveFilesInFolders() 0 8 2
A resolveFilesInFolder() 0 5 2
A fillFromConfigurationArray() 0 8 1
A fillDefaultsByPackageName() 0 7 1
B sanitizePath() 0 16 5
A sanitizePaths() 0 4 1
A ensureAbsolutePath() 0 4 4
A ensureAbsolutePaths() 0 4 1
A ensureSuffixedPath() 0 4 1
B extractPathArrays() 0 24 5
A getPackagePath() 0 4 1
A getLayoutIdentifier() 0 7 1
A getLayoutSource() 0 5 1
A getTemplateIdentifier() 0 10 2
A setTemplateSource() 0 4 1
B getTemplateSource() 0 26 6
A createIdentifierForFile() 0 5 2
A getLayoutPathAndFilename() 11 11 2
A getPartialIdentifier() 0 10 2
A getPartialSource() 0 5 1
A getPartialPathAndFilename() 11 11 2
B resolveFileInPaths() 0 23 4
A clearResolvedIdentifiersAndTemplates() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TemplatePaths often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TemplatePaths, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace TYPO3Fluid\Fluid\View;
3
4
/*
5
 * This file belongs to the package "TYPO3 Fluid".
6
 * See LICENSE.txt that was shipped with this package.
7
 */
8
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
9
10
/**
11
 * Template Paths Holder
12
 *
13
 * Class used to hold and resolve template files
14
 * and paths in multiple supported ways.
15
 *
16
 * The purpose of this class is to homogenise the
17
 * API that is used when working with template
18
 * paths coming from TypoScript, as well as serve
19
 * as a way to quickly generate default template-,
20
 * layout- and partial root paths by package.
21
 *
22
 * The constructor accepts two different types of
23
 * input - anything not of those types is silently
24
 * ignored:
25
 *
26
 * - a `string` input is assumed a package name
27
 *   and will call the `fillDefaultsByPackageName`
28
 *   value filling method.
29
 * - an `array` input is assumed a TypoScript-style
30
 *   array of root paths in one or more of the
31
 *   supported structures and will call the
32
 *   `fillFromTypoScriptArray` method.
33
 *
34
 * Either method can also be called after instance
35
 * is created, but both will overwrite any paths
36
 * you have previously configured.
37
 */
38
class TemplatePaths
39
{
40
41
    const DEFAULT_FORMAT = 'html';
42
    const DEFAULT_TEMPLATES_DIRECTORY = 'Resources/Private/Templates/';
43
    const DEFAULT_LAYOUTS_DIRECTORY = 'Resources/Private/Layouts/';
44
    const DEFAULT_PARTIALS_DIRECTORY = 'Resources/Private/Partials/';
45
    const CONFIG_TEMPLATEROOTPATHS = 'templateRootPaths';
46
    const CONFIG_LAYOUTROOTPATHS = 'layoutRootPaths';
47
    const CONFIG_PARTIALROOTPATHS = 'partialRootPaths';
48
    const CONFIG_FORMAT = 'format';
49
    const NAME_TEMPLATES = 'templates';
50
    const NAME_LAYOUTS = 'layouts';
51
    const NAME_PARTIALS = 'partials';
52
53
    /**
54
     * Holds already resolved identifiers for template files
55
     *
56
     * @var array
57
     */
58
    protected static $resolvedIdentifiers = [
59
        self::NAME_TEMPLATES => [],
60
        self::NAME_LAYOUTS => [],
61
        self::NAME_PARTIALS => []
62
    ];
63
64
    /**
65
     * Holds already resolved identifiers for template files
66
     *
67
     * @var array
68
     */
69
    protected static $resolvedFiles = [
70
        self::NAME_TEMPLATES => [],
71
        self::NAME_LAYOUTS => [],
72
        self::NAME_PARTIALS => []
73
    ];
74
75
    /**
76
     * @var array
77
     */
78
    protected $templateRootPaths = [];
79
80
    /**
81
     * @var array
82
     */
83
    protected $layoutRootPaths = [];
84
85
    /**
86
     * @var array
87
     */
88
    protected $partialRootPaths = [];
89
90
    /**
91
     * @var string
92
     */
93
    protected $templatePathAndFilename = null;
94
95
    /**
96
     * @var string
97
     */
98
    protected $layoutPathAndFilename = null;
99
100
    /**
101
     * @var string|NULL
102
     */
103
    protected $templateSource = null;
104
105
    /**
106
     * @var string
107
     */
108
    protected $format = self::DEFAULT_FORMAT;
109
110
    /**
111
     * @param string|NULL $packageNameOrArray
112
     */
113
    public function __construct($packageNameOrArray = null)
114
    {
115
        $this->clearResolvedIdentifiersAndTemplates();
116
        if (is_array($packageNameOrArray)) {
117
            $this->fillFromConfigurationArray($packageNameOrArray);
118
        } elseif (!empty($packageNameOrArray)) {
119
            $this->fillDefaultsByPackageName($packageNameOrArray);
120
        }
121
    }
122
123
    /**
124
     * @return array
125
     */
126
    public function toArray()
127
    {
128
        return [
129
            self::CONFIG_TEMPLATEROOTPATHS => $this->sanitizePaths($this->getTemplateRootPaths()),
130
            self::CONFIG_LAYOUTROOTPATHS => $this->sanitizePaths($this->getLayoutRootPaths()),
131
            self::CONFIG_PARTIALROOTPATHS => $this->sanitizePaths($this->getPartialRootPaths())
132
        ];
133
    }
134
135
    /**
136
     * @param string $templatePathAndFilename
137
     * @return void
138
     */
139
    public function setTemplatePathAndFilename($templatePathAndFilename)
140
    {
141
        $this->templatePathAndFilename = (string) $this->sanitizePath($templatePathAndFilename);
142
    }
143
144
    /**
145
     * @param string $layoutPathAndFilename
146
     * @return void
147
     */
148
    public function setLayoutPathAndFilename($layoutPathAndFilename)
149
    {
150
        $this->layoutPathAndFilename = $layoutPathAndFilename;
151
    }
152
153
    /**
154
     * @return array
155
     */
156
    public function getTemplateRootPaths()
157
    {
158
        return $this->templateRootPaths;
159
    }
160
161
    /**
162
     * @param array $templateRootPaths
163
     * @return void
164
     */
165
    public function setTemplateRootPaths(array $templateRootPaths)
166
    {
167
        $this->templateRootPaths = (array) $this->sanitizePaths($templateRootPaths);
168
        $this->clearResolvedIdentifiersAndTemplates(self::NAME_TEMPLATES);
169
    }
170
171
    /**
172
     * @return array
173
     */
174
    public function getLayoutRootPaths()
175
    {
176
        return $this->layoutRootPaths;
177
    }
178
179
    /**
180
     * @param array $layoutRootPaths
181
     * @return void
182
     */
183
    public function setLayoutRootPaths(array $layoutRootPaths)
184
    {
185
        $this->layoutRootPaths = (array) $this->sanitizePaths($layoutRootPaths);
186
        $this->clearResolvedIdentifiersAndTemplates(self::NAME_LAYOUTS);
187
    }
188
189
    /**
190
     * @return array
191
     */
192
    public function getPartialRootPaths()
193
    {
194
        return $this->partialRootPaths;
195
    }
196
197
    /**
198
     * @param array $partialRootPaths
199
     * @return void
200
     */
201
    public function setPartialRootPaths(array $partialRootPaths)
202
    {
203
        $this->partialRootPaths = (array) $this->sanitizePaths($partialRootPaths);
204
        $this->clearResolvedIdentifiersAndTemplates(self::NAME_PARTIALS);
205
    }
206
207
    /**
208
     * @return string
209
     */
210
    public function getFormat()
211
    {
212
        return $this->format;
213
    }
214
215
    /**
216
     * @param string $format
217
     * @return void
218
     */
219
    public function setFormat($format)
220
    {
221
        $this->format = $format;
222
    }
223
224
    /**
225
     * Attempts to resolve an absolute filename
226
     * of a template (i.e. `templateRootPaths`)
227
     * using a controller name, action and format.
228
     *
229
     * Works _backwards_ through template paths in
230
     * order to achieve an "overlay"-type behavior
231
     * where the last paths added are the first to
232
     * be checked and the first path added acts as
233
     * fallback if no other paths have the file.
234
     *
235
     * If the file does not exist in any path,
236
     * including fallback path, `NULL` is returned.
237
     *
238
     * Path configurations filled from TypoScript
239
     * is automatically recorded in the right
240
     * order (see `fillFromTypoScriptArray`), but
241
     * when manually setting the paths that should
242
     * be checked, you as user must be aware of
243
     * this reverse behavior (which you should
244
     * already be, given that it is the same way
245
     * TypoScript path configurations work).
246
     *
247
     * @param string $controller
248
     * @param string $action
249
     * @param string $format
250
     * @return string|NULL
251
     * @api
252
     */
253
    public function resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format = self::DEFAULT_FORMAT)
254
    {
255
        if ($this->templatePathAndFilename !== null) {
256
            return $this->templatePathAndFilename;
257
        }
258
        $controller = str_replace('\\', '/', $controller);
259
        $action = ucfirst($action);
260
        $identifier = $controller . '/' . $action . '.' . $format;
261
        if (!array_key_exists($identifier, self::$resolvedFiles['templates'])) {
262
            $templateRootPaths = $this->getTemplateRootPaths();
263
            foreach ([$controller . '/' . $action, $action] as $possibleRelativePath) {
264
                try {
265
                    return self::$resolvedFiles['templates'][$identifier] = $this->resolveFileInPaths($templateRootPaths, $possibleRelativePath, $format);
266
                } catch (InvalidTemplateResourceException $error) {
267
                    self::$resolvedFiles['templates'][$identifier] = null;
268
                }
269
            }
270
        }
271
        return isset(self::$resolvedFiles[self::NAME_TEMPLATES][$identifier]) ? self::$resolvedFiles[self::NAME_TEMPLATES][$identifier] : null;
272
    }
273
274
    /**
275
     * @param string|NULL $controllerName
276
     * @param string $format
277
     * @return array
278
     */
279
    public function resolveAvailableTemplateFiles($controllerName, $format = self::DEFAULT_FORMAT)
280
    {
281
        $paths = $this->getTemplateRootPaths();
282
        foreach ($paths as $index => $path) {
283
            $paths[$index] = rtrim($path . $controllerName, '/') . '/';
284
        }
285
        return $this->resolveFilesInFolders($paths, $format);
286
    }
287
288
    /**
289
     * @param string $format
290
     * @return array
291
     */
292
    public function resolveAvailablePartialFiles($format = self::DEFAULT_FORMAT)
293
    {
294
        return $this->resolveFilesInFolders($this->getPartialRootPaths(), $format);
295
    }
296
297
    /**
298
     * @param string $format
299
     * @return array
300
     */
301
    public function resolveAvailableLayoutFiles($format = self::DEFAULT_FORMAT)
302
    {
303
        return $this->resolveFilesInFolders($this->getLayoutRootPaths(), $format);
304
    }
305
306
    /**
307
     * @param array $folders
308
     * @param string $format
309
     * @return array
310
     */
311
    protected function resolveFilesInFolders(array $folders, $format)
312
    {
313
        $files = [];
314
        foreach ($folders as $folder) {
315
            $files = array_merge($files, $this->resolveFilesInFolder($folder, $format));
316
        }
317
        return array_values($files);
318
    }
319
320
    /**
321
     * @param string $folder
322
     * @param string $format
323
     * @return array
324
     */
325
    protected function resolveFilesInFolder($folder, $format)
326
    {
327
        $files = glob($folder . '*.' . $format);
328
        return !$files ? [] : $files;
329
    }
330
331
    /**
332
     * Fills path arrays based on a traditional
333
     * TypoScript array which may contain one or
334
     * more of the supported structures, in order
335
     * of priority:
336
     *
337
     * - `plugin.tx_yourext.view.templateRootPath` and siblings.
338
     * - `plugin.tx_yourext.view.templateRootPaths` and siblings.
339
     * - `plugin.tx_yourext.view.overlays.otherextension.templateRootPath` and siblings.
340
     *
341
     * The paths are treated as follows, using the
342
     * `template`-type paths as an example:
343
     *
344
     * - If `templateRootPath` is defined, it gets
345
     *   used as the _first_ path in the internal
346
     *   paths array.
347
     * - If `templateRootPaths` is defined, all
348
     *   values from it are _appended_ to the
349
     *   internal paths array.
350
     * - If `overlays.*` exists in the array it is
351
     *   iterated, each `templateRootPath` entry
352
     *   from it _appended_ to the internal array.
353
     *
354
     * The result is that after filling, the path
355
     * arrays will contain one or more entries in
356
     * the order described above, depending on how
357
     * many of the possible configurations were
358
     * present in the input array.
359
     *
360
     * Will replace any currently configured paths.
361
     *
362
     * @param array $paths
363
     * @return void
364
     * @api
365
     */
366
    public function fillFromConfigurationArray(array $paths)
367
    {
368
        list ($templateRootPaths, $layoutRootPaths, $partialRootPaths, $format) = $this->extractPathArrays($paths);
369
        $this->setTemplateRootPaths($templateRootPaths);
370
        $this->setLayoutRootPaths($layoutRootPaths);
371
        $this->setPartialRootPaths($partialRootPaths);
372
        $this->setFormat($format);
373
    }
374
375
    /**
376
     * Fills path arrays with default expected paths
377
     * based on package name (converted to extension
378
     * key automatically).
379
     *
380
     * Will replace any currently configured paths.
381
     *
382
     * @param string $packageName
383
     * @return void
384
     * @api
385
     */
386
    public function fillDefaultsByPackageName($packageName)
387
    {
388
        $path = $this->getPackagePath($packageName);
389
        $this->setTemplateRootPaths([$path . self::DEFAULT_TEMPLATES_DIRECTORY]);
390
        $this->setLayoutRootPaths([$path . self::DEFAULT_LAYOUTS_DIRECTORY]);
391
        $this->setPartialRootPaths([$path . self::DEFAULT_PARTIALS_DIRECTORY]);
392
    }
393
394
    /**
395
     * Sanitize a path, ensuring it is absolute and
396
     * if a directory, suffixed by a trailing slash.
397
     *
398
     * @param string|array $path
399
     * @return string
400
     */
401
    protected function sanitizePath($path)
402
    {
403
        if (is_array($path)) {
404
            $paths = array_map([$this, 'sanitizePath'], $path);
405
            return array_unique($paths);
406
        } elseif (strpos($path, 'php://') === 0) {
407
            return $path;
408
        } elseif (!empty($path)) {
409
            $path = str_replace(['\\', '//'], '/', (string) $path);
410
            $path = (string) $this->ensureAbsolutePath($path);
411
            if (is_dir($path)) {
412
                $path = $this->ensureSuffixedPath($path);
413
            }
414
        }
415
        return $path;
416
    }
417
418
    /**
419
     * Sanitize paths passing each through sanitizePath().
420
     *
421
     * @param array $paths
422
     * @return array
423
     */
424
    protected function sanitizePaths(array $paths)
425
    {
426
        return array_unique(array_map([$this, 'sanitizePath'], $paths));
427
    }
428
429
    /**
430
     * Guarantees that $reference is turned into a
431
     * correct, absolute path.
432
     *
433
     * @param string $path
434
     * @return string
435
     */
436
    protected function ensureAbsolutePath($path)
437
    {
438
        return ((!empty($path) && $path{0} !== '/' && $path{1} !== ':') ? $this->sanitizePath(realpath($path)) : $path);
439
    }
440
441
    /**
442
     * Guarantees that array $reference with paths
443
     * are turned into correct, absolute paths
444
     *
445
     * @param array $reference
446
     * @return array
447
     */
448
    protected function ensureAbsolutePaths(array $reference)
449
    {
450
        return array_map([$this, 'ensureAbsolutePath'], $reference);
451
    }
452
453
    /**
454
     * @param string $path
455
     * @return string
456
     */
457
    protected function ensureSuffixedPath($path)
458
    {
459
        return rtrim($path, '/') . '/';
460
    }
461
462
    /**
463
     * Extract an array of three arrays of paths, one
464
     * for each of the types of Fluid file resources.
465
     * Accepts one or both of the singular and plural
466
     * path definitions in the input - returns the
467
     * combined collections of paths based on both
468
     * the singular and plural entries with the singular
469
     * entries being recorded first and plurals second.
470
     *
471
     * Adds legacy singular name as last option, if set.
472
     *
473
     * @param array $paths
474
     * @return array
475
     */
476
    protected function extractPathArrays(array $paths)
477
    {
478
        $format = self::DEFAULT_FORMAT;
479
        // pre-processing: if special parameters exist, extract them:
480
        if (isset($paths[self::CONFIG_FORMAT])) {
481
            $format = $paths[self::CONFIG_FORMAT];
482
        }
483
        $pathParts = [
484
            self::CONFIG_TEMPLATEROOTPATHS,
485
            self::CONFIG_LAYOUTROOTPATHS,
486
            self::CONFIG_PARTIALROOTPATHS
487
        ];
488
        $pathCollections = [];
489
        foreach ($pathParts as $pathPart) {
490
            $partPaths = [];
491
            if (isset($paths[$pathPart]) && is_array($paths[$pathPart])) {
492
                $partPaths = array_merge($partPaths, array_values($paths[$pathPart]));
493
            }
494
            $pathCollections[] = array_values(array_unique(array_map([$this, 'ensureSuffixedPath'], $partPaths)));
495
        }
496
        $pathCollections = array_map([$this, 'ensureAbsolutePaths'], $pathCollections);
497
        $pathCollections[] = $format;
498
        return $pathCollections;
499
    }
500
501
    /**
502
     * @param string $packageName
503
     * @return string
504
     */
505
    protected function getPackagePath($packageName)
0 ignored issues
show
Unused Code introduced by
The parameter $packageName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
506
    {
507
        return '';
508
    }
509
510
    /**
511
     * Returns a unique identifier for the resolved layout file.
512
     * This identifier is based on the template path and last modification date
513
     *
514
     * @param string $layoutName The name of the layout
515
     * @return string layout identifier
516
     */
517
    public function getLayoutIdentifier($layoutName = 'Default')
518
    {
519
        $filePathAndFilename = $this->getLayoutPathAndFilename($layoutName);
520
        $layoutName = str_replace('.', '_', $layoutName);
521
        $prefix = 'layout_' . $layoutName;
522
        return $this->createIdentifierForFile($filePathAndFilename, $prefix);
523
    }
524
525
    /**
526
     * Resolve the path and file name of the layout file, based on
527
     * $this->layoutPathAndFilename and $this->layoutPathAndFilenamePattern.
528
     *
529
     * In case a layout has already been set with setLayoutPathAndFilename(),
530
     * this method returns that path, otherwise a path and filename will be
531
     * resolved using the layoutPathAndFilenamePattern.
532
     *
533
     * @param string $layoutName Name of the layout to use. If none given, use "Default"
534
     * @return string Path and filename of layout file
535
     * @throws InvalidTemplateResourceException
536
     */
537
    public function getLayoutSource($layoutName = 'Default')
538
    {
539
        $layoutPathAndFilename = $this->getLayoutPathAndFilename($layoutName);
540
        return file_get_contents($layoutPathAndFilename, FILE_TEXT);
541
    }
542
543
    /**
544
     * Returns a unique identifier for the resolved template file
545
     * This identifier is based on the template path and last modification date
546
     *
547
     * @param string $controller
548
     * @param string $action Name of the action. If NULL, will be taken from request.
549
     * @return string template identifier
550
     */
551
    public function getTemplateIdentifier($controller = 'Default', $action = 'Default')
552
    {
553
        $format = $this->getFormat();
554
        if ($this->templateSource !== null) {
555
            return 'source_' . sha1($this->templateSource) . '_' . $controller . '_' . $action . '_' . $format;
556
        }
557
        $templatePathAndFilename = $this->resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format);
558
        $prefix = $controller . '_action_' . $action;
559
        return $this->createIdentifierForFile($templatePathAndFilename, $prefix);
560
    }
561
562
    /**
563
     * @param mixed $source
564
     */
565
    public function setTemplateSource($source)
566
    {
567
        $this->templateSource = $source;
568
    }
569
570
    /**
571
     * Resolve the template path and filename for the given action. If $actionName
572
     * is NULL, looks into the current request.
573
     *
574
     * @param string $controller
575
     * @param string $action Name of the action. If NULL, will be taken from request.
576
     * @return string Full path to template
577
     * @throws InvalidTemplateResourceException
578
     */
579
    public function getTemplateSource($controller = 'Default', $action = 'Default')
580
    {
581
        if (is_string($this->templateSource)) {
582
            return $this->templateSource;
583
        } elseif (is_resource($this->templateSource)) {
584
            rewind($this->templateSource);
585
            return $this->templateSource = stream_get_contents($this->templateSource);
586
        }
587
        $format = $this->getFormat();
588
        $templateReference = $this->resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format);
589
        if (!file_exists($templateReference) && $templateReference !== 'php://stdin') {
590
            throw new InvalidTemplateResourceException(
591
                sprintf(
592
                    'Tried resolving a template file for controller action "%s->%s" in format ".%s", but none of the paths ' .
593
                    'contained the expected template file (%s). The following paths were checked: %s',
594
                    $controller,
595
                    $action,
596
                    $format,
597
                    $templateReference === null ? $controller . '/' . ucfirst($action) . '.' . $format : $templateReference,
598
                    implode(',', $this->getTemplateRootPaths())
599
                ),
600
                1257246929
601
            );
602
        }
603
        return file_get_contents($templateReference, FILE_TEXT);
604
    }
605
606
    /**
607
     * Returns a unique identifier for the given file in the format
608
     * <PackageKey>_<SubPackageKey>_<ControllerName>_<prefix>_<SHA1>
609
     * The SH1 hash is a checksum that is based on the file path and last modification date
610
     *
611
     * @param string $pathAndFilename
612
     * @param string $prefix
613
     * @return string
614
     */
615
    protected function createIdentifierForFile($pathAndFilename, $prefix)
616
    {
617
        $templateModifiedTimestamp = $pathAndFilename !== 'php://stdin' ? filemtime($pathAndFilename) : 0;
618
        return sprintf('%s_%s', $prefix, sha1($pathAndFilename . '|' . $templateModifiedTimestamp));
619
    }
620
621
    /**
622
     * Resolve the path and file name of the layout file, based on
623
     * $this->options['layoutPathAndFilename'] and $this->options['layoutPathAndFilenamePattern'].
624
     *
625
     * In case a layout has already been set with setLayoutPathAndFilename(),
626
     * this method returns that path, otherwise a path and filename will be
627
     * resolved using the layoutPathAndFilenamePattern.
628
     *
629
     * @param string $layoutName Name of the layout to use. If none given, use "Default"
630
     * @return string Path and filename of layout files
631
     * @throws Exception\InvalidTemplateResourceException
632
     */
633 View Code Duplication
    public function getLayoutPathAndFilename($layoutName = 'Default')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
634
    {
635
        $format = $this->getFormat();
636
        $layoutName = ucfirst($layoutName);
637
        $layoutKey = $layoutName . '.' . $format;
638
        if (!array_key_exists($layoutKey, self::$resolvedFiles[self::NAME_LAYOUTS])) {
639
            $paths = $this->getLayoutRootPaths();
640
            self::$resolvedFiles[self::NAME_LAYOUTS][$layoutKey] = $this->resolveFileInPaths($paths, $layoutName, $format);
641
        }
642
        return self::$resolvedFiles[self::NAME_LAYOUTS][$layoutKey];
643
    }
644
645
    /**
646
     * Returns a unique identifier for the resolved partial file.
647
     * This identifier is based on the template path and last modification date
648
     *
649
     * @param string $partialName The name of the partial
650
     * @return string partial identifier
651
     */
652
    public function getPartialIdentifier($partialName)
653
    {
654
        $partialKey = $partialName . '.' . $this->getFormat();
655
        if (!array_key_exists($partialKey, self::$resolvedIdentifiers[self::NAME_PARTIALS])) {
656
            $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
657
            $prefix = 'partial_' . $partialName;
658
            self::$resolvedIdentifiers[self::NAME_PARTIALS][$partialKey] = $this->createIdentifierForFile($partialPathAndFilename, $prefix);
659
        }
660
        return self::$resolvedIdentifiers[self::NAME_PARTIALS][$partialKey];
661
    }
662
663
    /**
664
     * Figures out which partial to use.
665
     *
666
     * @param string $partialName The name of the partial
667
     * @return string contents of the partial template
668
     * @throws InvalidTemplateResourceException
669
     */
670
    public function getPartialSource($partialName)
671
    {
672
        $partialPathAndFilename = $this->getPartialPathAndFilename($partialName);
673
        return file_get_contents($partialPathAndFilename, FILE_TEXT);
674
    }
675
676
    /**
677
     * Resolve the partial path and filename based on $this->options['partialPathAndFilenamePattern'].
678
     *
679
     * @param string $partialName The name of the partial
680
     * @return string the full path which should be used. The path definitely exists.
681
     * @throws InvalidTemplateResourceException
682
     */
683 View Code Duplication
    public function getPartialPathAndFilename($partialName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
684
    {
685
        $format = $this->getFormat();
686
        $partialKey = $partialName . '.' . $format;
687
        if (!array_key_exists($partialKey, self::$resolvedFiles[self::NAME_PARTIALS])) {
688
            $paths = $this->getPartialRootPaths();
689
            $partialName = ucfirst($partialName);
690
            self::$resolvedFiles[self::NAME_PARTIALS][$partialKey] = $this->resolveFileInPaths($paths, $partialName, $format);
691
        }
692
        return self::$resolvedFiles[self::NAME_PARTIALS][$partialKey];
693
    }
694
695
    /**
696
     * @param array $paths
697
     * @param string $relativePathAndFilename
698
     * @return string
699
     * @throws \TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException
700
     */
701
    protected function resolveFileInPaths(array $paths, $relativePathAndFilename, $format = self::DEFAULT_FORMAT)
702
    {
703
        $tried = [];
704
        // Note about loop: iteration with while + array_pop causes paths to be checked in opposite
705
        // order, which is intentional. Paths are considered overlays, e.g. adding a path to the
706
        // array means you want that path checked first.
707
        while (null !== ($path = array_pop($paths))) {
708
            $pathAndFilenameWithoutFormat = $path . $relativePathAndFilename;
709
            $pathAndFilename = $pathAndFilenameWithoutFormat . '.' . $format;
710
            if (is_file($pathAndFilename)) {
711
                return $pathAndFilename;
712
            }
713
            $tried[] = $pathAndFilename;
714
            if (is_file($pathAndFilenameWithoutFormat)) {
715
                return $pathAndFilenameWithoutFormat;
716
            }
717
            $tried[] = $pathAndFilenameWithoutFormat;
718
        }
719
        throw new InvalidTemplateResourceException(
720
            'The Fluid template files "' . implode('", "', $tried) . '" could not be loaded.',
721
            1225709595
722
        );
723
    }
724
725
    /**
726
     * @param string|NULL $type
727
     * @return void
728
     */
729
    protected function clearResolvedIdentifiersAndTemplates($type = null)
730
    {
731
        if ($type !== null) {
732
            self::$resolvedIdentifiers[$type] = self::$resolvedFiles[$type] = [];
733
        } else {
734
            self::$resolvedIdentifiers = self::$resolvedFiles = [
735
                self::NAME_TEMPLATES => [],
736
                self::NAME_LAYOUTS => [],
737
                self::NAME_PARTIALS => []
738
            ];
739
        }
740
    }
741
}
742