• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / handlebars / 15461925183

05 Jun 2025 08:11AM UTC coverage: 90.937% (-1.9%) from 92.804%
15461925183

push

github

web-flow
Merge pull request #434 from CPS-IT/feature/typo3-v13

[!!!][FEATURE] Add support for TYPO3 v13.4, drop support for TYPO3 v12.4

69 of 86 new or added lines in 9 files covered. (80.23%)

2 existing lines in 1 file now uncovered.

883 of 971 relevant lines covered (90.94%)

5.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.56
/Classes/Renderer/Template/BaseTemplateResolver.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "handlebars".
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace Fr\Typo3Handlebars\Renderer\Template;
19

20
use Fr\Typo3Handlebars\Exception;
21
use TYPO3\CMS\Core;
22

23
/**
24
 * BaseTemplateResolver
25
 *
26
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
27
 * @license GPL-2.0-or-later
28
 */
29
abstract class BaseTemplateResolver implements TemplateResolver
30
{
31
    protected const DEFAULT_FILE_EXTENSIONS = ['hbs', 'handlebars', 'html'];
32

33
    /**
34
     * @var list<string>
35
     */
36
    protected array $supportedFileExtensions = self::DEFAULT_FILE_EXTENSIONS;
37

38
    public function supports(string $fileExtension): bool
14✔
39
    {
40
        return \in_array($fileExtension, $this->supportedFileExtensions, true);
14✔
41
    }
42

43
    protected function resolveFilename(string $path, ?string $rootPath = null, ?string $extension = null): string
27✔
44
    {
45
        if ($rootPath !== null) {
27✔
46
            $filename = $rootPath . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
14✔
47
        } else {
48
            $filename = $path;
21✔
49
        }
50

51
        if ($extension !== null && !$this->supports(pathinfo($filename, PATHINFO_EXTENSION))) {
27✔
52
            $filename .= '.' . $extension;
11✔
53
        }
54

55
        $resolvedFilename = Core\Utility\GeneralUtility::getFileAbsFileName($filename);
27✔
56

57
        if ($resolvedFilename === '' && Core\Utility\PathUtility::isAllowedAdditionalPath($filename)) {
27✔
UNCOV
58
            return $filename;
×
59
        }
60

61
        return $resolvedFilename;
27✔
62
    }
63

64
    /**
65
     * @return array{list<string>, list<string>}
66
     * @throws Exception\RootPathIsMalicious
67
     * @throws Exception\RootPathIsNotResolvable
68
     */
69
    protected function resolveTemplatePaths(TemplatePaths $templatePaths): array
31✔
70
    {
71
        return [
31✔
72
            $this->normalizeRootPaths($templatePaths->getTemplateRootPaths()),
31✔
73
            $this->normalizeRootPaths($templatePaths->getPartialRootPaths()),
31✔
74
        ];
31✔
75
    }
76

77
    /**
78
     * @param string[] $rootPaths
79
     * @return list<string>
80
     * @throws Exception\RootPathIsMalicious
81
     * @throws Exception\RootPathIsNotResolvable
82
     */
83
    protected function normalizeRootPaths(array $rootPaths): array
31✔
84
    {
85
        $normalizedRootPaths = [];
31✔
86

87
        ksort($rootPaths);
31✔
88

89
        foreach ($rootPaths as $rootPath) {
31✔
90
            /* @phpstan-ignore function.alreadyNarrowedType */
91
            if (!\is_string($rootPath)) {
31✔
92
                throw new Exception\RootPathIsMalicious($rootPath);
2✔
93
            }
94

95
            $normalizedRootPath = rtrim($rootPath, DIRECTORY_SEPARATOR);
30✔
96
            $normalizedRootPath = Core\Utility\GeneralUtility::getFileAbsFileName($normalizedRootPath);
30✔
97

98
            if ($normalizedRootPath === '') {
30✔
99
                if (!Core\Utility\PathUtility::isAllowedAdditionalPath($rootPath)) {
2✔
100
                    throw new Exception\RootPathIsNotResolvable($rootPath);
2✔
101
                }
102

UNCOV
103
                $normalizedRootPath = $rootPath;
×
104
            }
105

106
            $normalizedRootPaths[] = $normalizedRootPath;
29✔
107
        }
108

109
        return $normalizedRootPaths;
29✔
110
    }
111

112
    /**
113
     * @param string[] $supportedFileExtensions
114
     * @return list<string>
115
     */
116
    protected function resolveSupportedFileExtensions(array $supportedFileExtensions): array
33✔
117
    {
118
        if ($supportedFileExtensions === []) {
33✔
119
            $supportedFileExtensions = self::DEFAULT_FILE_EXTENSIONS;
1✔
120
        }
121

122
        return array_values(
33✔
123
            \array_unique(
33✔
124
                array_map($this->normalizeFileExtension(...), $supportedFileExtensions),
33✔
125
            ),
33✔
126
        );
33✔
127
    }
128

129
    /**
130
     * @throws Exception\FileExtensionIsInvalid
131
     * @throws Exception\FileExtensionIsMalicious
132
     */
133
    protected function normalizeFileExtension(mixed $fileExtension): string
33✔
134
    {
135
        if (!\is_string($fileExtension)) {
33✔
136
            throw new Exception\FileExtensionIsMalicious($fileExtension);
2✔
137
        }
138
        if (preg_match('/^[\w\-.]+$/', $fileExtension) !== 1) {
32✔
139
            throw new Exception\FileExtensionIsInvalid($fileExtension);
2✔
140
        }
141

142
        return ltrim(trim($fileExtension), '.');
31✔
143
    }
144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc