Completed
Push — master ( d03d2a...1a1551 )
by Tim
24:35 queued 09:33
created
Classes/Manipulation/RemoveGenerator.php 2 patches
Indentation   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -6,16 +6,16 @@
 block discarded – undo
6 6
 
7 7
 class RemoveGenerator implements ManipulationInterface
8 8
 {
9
-    /**
10
-     * @param string $html          The original HTML
11
-     * @param array  $configuration Configuration
12
-     *
13
-     * @return string the manipulated HTML
14
-     */
15
-    public function manipulate(string $html, array $configuration = []): string
16
-    {
17
-        $regex = '<meta name=["\']?generator["\']? [^>]+>';
9
+	/**
10
+	 * @param string $html          The original HTML
11
+	 * @param array  $configuration Configuration
12
+	 *
13
+	 * @return string the manipulated HTML
14
+	 */
15
+	public function manipulate(string $html, array $configuration = []): string
16
+	{
17
+		$regex = '<meta name=["\']?generator["\']? [^>]+>';
18 18
 
19
-        return (string) preg_replace('/' . $regex . '/is', '', $html);
20
-    }
19
+		return (string) preg_replace('/' . $regex . '/is', '', $html);
20
+	}
21 21
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -16,6 +16,6 @@
 block discarded – undo
16 16
     {
17 17
         $regex = '<meta name=["\']?generator["\']? [^>]+>';
18 18
 
19
-        return (string) preg_replace('/' . $regex . '/is', '', $html);
19
+        return (string) preg_replace('/'.$regex.'/is', '', $html);
20 20
     }
21 21
 }
Please login to merge, or discard this patch.
Classes/Middleware/SvgStoreMiddleware.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -11,20 +11,20 @@
 block discarded – undo
11 11
 
12 12
 class SvgStoreMiddleware extends AbstractMiddleware
13 13
 {
14
-    public function __construct(protected SvgStoreService $svgStoreService) {}
14
+	public function __construct(protected SvgStoreService $svgStoreService) {}
15 15
 
16
-    /**
17
-     * Search/Extract/Merge SVGs @ HTML output.
18
-     */
19
-    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
-    {
21
-        $response = $handler->handle($request);
16
+	/**
17
+	 * Search/Extract/Merge SVGs @ HTML output.
18
+	 */
19
+	public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
+	{
21
+		$response = $handler->handle($request);
22 22
 
23
-        if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['svgstore.']['enabled'] ?? false) {
24
-            $processedHtml = $this->svgStoreService->process((string) $response->getBody());
25
-            $response = $response->withBody($this->getStringStream($processedHtml));
26
-        }
23
+		if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['svgstore.']['enabled'] ?? false) {
24
+			$processedHtml = $this->svgStoreService->process((string) $response->getBody());
25
+			$response = $response->withBody($this->getStringStream($processedHtml));
26
+		}
27 27
 
28
-        return $response;
29
-    }
28
+		return $response;
29
+	}
30 30
 }
Please login to merge, or discard this patch.
Classes/Middleware/CleanHtmlMiddleware.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -11,23 +11,23 @@
 block discarded – undo
11 11
 
12 12
 class CleanHtmlMiddleware extends AbstractMiddleware
13 13
 {
14
-    public function __construct(protected CleanHtmlService $cleanHtmlService) {}
14
+	public function __construct(protected CleanHtmlService $cleanHtmlService) {}
15 15
 
16
-    /**
17
-     * Clean the HTML output.
18
-     */
19
-    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
-    {
21
-        $response = $handler->handle($request);
16
+	/**
17
+	 * Clean the HTML output.
18
+	 */
19
+	public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
+	{
21
+		$response = $handler->handle($request);
22 22
 
23
-        if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['sourceopt.']['enabled'] ?? false) {
24
-            $processedHtml = $this->cleanHtmlService->clean(
25
-                (string) $response->getBody(),
26
-                (array) $GLOBALS['TSFE']->config['config']['sourceopt.']
27
-            );
28
-            $response = $response->withBody($this->getStringStream($processedHtml));
29
-        }
23
+		if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['sourceopt.']['enabled'] ?? false) {
24
+			$processedHtml = $this->cleanHtmlService->clean(
25
+				(string) $response->getBody(),
26
+				(array) $GLOBALS['TSFE']->config['config']['sourceopt.']
27
+			);
28
+			$response = $response->withBody($this->getStringStream($processedHtml));
29
+		}
30 30
 
31
-        return $response;
32
-    }
31
+		return $response;
32
+	}
33 33
 }
Please login to merge, or discard this patch.
Classes/Middleware/RegExRepMiddleware.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -11,20 +11,20 @@
 block discarded – undo
11 11
 
12 12
 class RegExRepMiddleware extends AbstractMiddleware
13 13
 {
14
-    public function __construct(protected RegExRepService $regExRepService) {}
14
+	public function __construct(protected RegExRepService $regExRepService) {}
15 15
 
16
-    /**
17
-     * RegEx search & replace @ HTML output.
18
-     */
19
-    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
-    {
21
-        $response = $handler->handle($request);
16
+	/**
17
+	 * RegEx search & replace @ HTML output.
18
+	 */
19
+	public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
20
+	{
21
+		$response = $handler->handle($request);
22 22
 
23
-        if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['replacer.'] ?? false) {
24
-            $processedHtml = $this->regExRepService->process((string) $response->getBody());
25
-            $response = $response->withBody($this->getStringStream($processedHtml));
26
-        }
23
+		if ($this->responseIsAlterable($response) && $GLOBALS['TSFE']->config['config']['replacer.'] ?? false) {
24
+			$processedHtml = $this->regExRepService->process((string) $response->getBody());
25
+			$response = $response->withBody($this->getStringStream($processedHtml));
26
+		}
27 27
 
28
-        return $response;
29
-    }
28
+		return $response;
29
+	}
30 30
 }
Please login to merge, or discard this patch.
Classes/Service/RegExRepService.php 2 patches
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -16,46 +16,46 @@
 block discarded – undo
16 16
  */
17 17
 class RegExRepService implements SingletonInterface
18 18
 {
19
-    public function process(string $html): string
20
-    {
21
-        $config = $GLOBALS['TSFE']->config['config']['replacer.'];
22
-
23
-        foreach (['search.', 'replace.'] as $section) {
24
-            if (!isset($config[$section]) || !\is_array($config[$section])) {
25
-                throw new \Exception('missing entry @ config.replacer.' . $section);
26
-            }
27
-
28
-            if (preg_match_all('/"([\w\-]+)\.";/', serialize(array_keys($config[$section])), $matches)) {
29
-                $cObj ??= $GLOBALS['TSFE']->cObj ?? GeneralUtility::makeInstance(ContentObjectRenderer::class);
30
-
31
-                foreach ($matches[1] as $key) {
32
-                    $config[$section][$key] = $cObj
33
-                        ->stdWrap(
34
-                            $config[$section][$key],
35
-                            $config[$section][$key . '.']
36
-                        )
37
-                    ;
38
-                    unset($config[$section][$key . '.']); // keep!
39
-                }
40
-            }
41
-
42
-            ksort($config[$section], \SORT_NATURAL); // safety
43
-        }
44
-
45
-        if (Environment::getContext()->isDevelopment()) {
46
-            foreach ($config['search.'] as $key => $val) {
47
-                if (false === @preg_match($val, '')) {
48
-                    throw new \Exception(preg_last_error_msg() . ' : please check your regex syntax @ ' . "{$key} = {$val}");
49
-                }
50
-            }
51
-        }
52
-
53
-        $arrIntersectKeysCnt = 2 * \count(array_intersect_key($config['search.'], $config['replace.']));
54
-
55
-        if ((bool) (\count($config['search.']) + \count($config['replace.']) - $arrIntersectKeysCnt)) {
56
-            throw new \Exception('config.replacer requests have diverged');
57
-        }
58
-
59
-        return preg_replace($config['search.'], $config['replace.'], $html);
60
-    }
19
+	public function process(string $html): string
20
+	{
21
+		$config = $GLOBALS['TSFE']->config['config']['replacer.'];
22
+
23
+		foreach (['search.', 'replace.'] as $section) {
24
+			if (!isset($config[$section]) || !\is_array($config[$section])) {
25
+				throw new \Exception('missing entry @ config.replacer.' . $section);
26
+			}
27
+
28
+			if (preg_match_all('/"([\w\-]+)\.";/', serialize(array_keys($config[$section])), $matches)) {
29
+				$cObj ??= $GLOBALS['TSFE']->cObj ?? GeneralUtility::makeInstance(ContentObjectRenderer::class);
30
+
31
+				foreach ($matches[1] as $key) {
32
+					$config[$section][$key] = $cObj
33
+						->stdWrap(
34
+							$config[$section][$key],
35
+							$config[$section][$key . '.']
36
+						)
37
+					;
38
+					unset($config[$section][$key . '.']); // keep!
39
+				}
40
+			}
41
+
42
+			ksort($config[$section], \SORT_NATURAL); // safety
43
+		}
44
+
45
+		if (Environment::getContext()->isDevelopment()) {
46
+			foreach ($config['search.'] as $key => $val) {
47
+				if (false === @preg_match($val, '')) {
48
+					throw new \Exception(preg_last_error_msg() . ' : please check your regex syntax @ ' . "{$key} = {$val}");
49
+				}
50
+			}
51
+		}
52
+
53
+		$arrIntersectKeysCnt = 2 * \count(array_intersect_key($config['search.'], $config['replace.']));
54
+
55
+		if ((bool) (\count($config['search.']) + \count($config['replace.']) - $arrIntersectKeysCnt)) {
56
+			throw new \Exception('config.replacer requests have diverged');
57
+		}
58
+
59
+		return preg_replace($config['search.'], $config['replace.'], $html);
60
+	}
61 61
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -22,7 +22,7 @@  discard block
 block discarded – undo
22 22
 
23 23
         foreach (['search.', 'replace.'] as $section) {
24 24
             if (!isset($config[$section]) || !\is_array($config[$section])) {
25
-                throw new \Exception('missing entry @ config.replacer.' . $section);
25
+                throw new \Exception('missing entry @ config.replacer.'.$section);
26 26
             }
27 27
 
28 28
             if (preg_match_all('/"([\w\-]+)\.";/', serialize(array_keys($config[$section])), $matches)) {
@@ -32,10 +32,10 @@  discard block
 block discarded – undo
32 32
                     $config[$section][$key] = $cObj
33 33
                         ->stdWrap(
34 34
                             $config[$section][$key],
35
-                            $config[$section][$key . '.']
35
+                            $config[$section][$key.'.']
36 36
                         )
37 37
                     ;
38
-                    unset($config[$section][$key . '.']); // keep!
38
+                    unset($config[$section][$key.'.']); // keep!
39 39
                 }
40 40
             }
41 41
 
@@ -45,7 +45,7 @@  discard block
 block discarded – undo
45 45
         if (Environment::getContext()->isDevelopment()) {
46 46
             foreach ($config['search.'] as $key => $val) {
47 47
                 if (false === @preg_match($val, '')) {
48
-                    throw new \Exception(preg_last_error_msg() . ' : please check your regex syntax @ ' . "{$key} = {$val}");
48
+                    throw new \Exception(preg_last_error_msg().' : please check your regex syntax @ '."{$key} = {$val}");
49 49
                 }
50 50
             }
51 51
         }
Please login to merge, or discard this patch.
Classes/Service/SvgStoreService.php 2 patches
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -17,235 +17,235 @@
 block discarded – undo
17 17
  */
18 18
 class SvgStoreService implements \TYPO3\CMS\Core\SingletonInterface
19 19
 {
20
-    /**
21
-     * SVG-Sprite relativ storage directory.
22
-     */
23
-    protected string $outputDir = '/typo3temp/assets/svg/';
24
-
25
-    /**
26
-     * TYPO3 absolute path to public web.
27
-     */
28
-    protected string $sitePath = '';
29
-
30
-    /**
31
-     * Final TYPO3 Frontend-Cache object.
32
-     */
33
-    protected FrontendInterface $svgCache;
34
-
35
-    /**
36
-     * Cached SVG-Sprite relativ file path.
37
-     */
38
-    protected string $spritePath = '';
39
-
40
-    /**
41
-     * Cached used SVG files (incl. defs).
42
-     */
43
-    protected array $svgFileArr = [];
44
-
45
-    /**
46
-     * Final SVG-Sprite Vectors.
47
-     */
48
-    protected array $svgs = [];
49
-
50
-    /**
51
-     * Final SVG-Sprite Styles.
52
-     */
53
-    protected array $styl = []; // ToFix ; https://stackoverflow.com/questions/39583880/external-svg-fails-to-apply-internal-css
54
-
55
-    /**
56
-     * Final SVG-Sprite Objects.
57
-     */
58
-    protected array $defs = []; // ToFix ; https://bugs.chromium.org/p/chromium/issues/detail?id=751733#c14
59
-
60
-    public function __construct()
61
-    {
62
-        $this->sitePath = \TYPO3\CMS\Core\Core\Environment::getPublicPath(); // [^/]$
63
-        $this->svgCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('svgstore');
64
-
65
-        $this->spritePath = $this->svgCache->get('spritePath') ?: '';
66
-        $this->svgFileArr = $this->svgCache->get('svgFileArr') ?: [];
67
-
68
-        if (empty($this->spritePath) && !$this->populateCache()) {
69
-            throw new \Exception('could not write file: ' . $this->sitePath . $this->spritePath);
70
-        }
71
-
72
-        if (!file_exists($this->sitePath . $this->spritePath)) {
73
-            throw new \Exception('file does not exists: ' . $this->sitePath . $this->spritePath);
74
-        }
75
-    }
76
-
77
-    public function process(string $html): string
78
-    {
79
-        if (empty($this->svgFileArr)) {
80
-            return $html;
81
-        }
82
-
83
-        if ($GLOBALS['TSFE']->config['config']['disableAllHeaderCode'] ?? false) {
84
-            $dom = ['head' => '', 'body' => $html];
85
-        } elseif (!preg_match('/(?<head>.+?<\/head>)(?<body>.+)/s', $html, $dom)) {
86
-            return $html;
87
-        }
88
-
89
-        // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes
90
-        $dom['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?:https?:)?(?:\/\/[^\/]+?)?(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?!\s*<\/picture>)/s', function (array $match): string { // ^[/]
91
-            if (!isset($this->svgFileArr[$match['src']])) { // check usage
92
-                return $match[0];
93
-            }
94
-            $attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap|crossorigin|decoding|fetchpriority|referrerpolicy)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
95
-
96
-            return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['src']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['src']));
97
-        }, $dom['body']);
98
-
99
-        // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#attributes
100
-        $dom['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function (array $match): string { // ^[/]
101
-            if (!isset($this->svgFileArr[$match['data']])) { // check usage
102
-                return $match[0];
103
-            }
104
-            $attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
105
-
106
-            return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['data']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['data']));
107
-        }, $dom['body']);
108
-
109
-        return $dom['head'] . $dom['body'];
110
-    }
111
-
112
-    private function convertFilePath(string $path): string
113
-    {
114
-        return preg_replace('/.svg$|[^\w\-]/', '', str_replace('/', '-', ltrim($path, '/'))); // ^[^/]
115
-    }
116
-
117
-    private function addFileToSpriteArr(string $hash, string $path, array $attr = []): ?array
118
-    {
119
-        if (!file_exists($this->sitePath . $path)) {
120
-            return null;
121
-        }
122
-
123
-        $svg = file_get_contents($this->sitePath . $path);
124
-
125
-        if (preg_match('/(?:;base64|i:a?i?pgf)/', $svg)) { // noop!
126
-            return null;
127
-        }
128
-
129
-        if (preg_match('/<(?:style|defs)|url\(/', $svg)) {
130
-            return null; // check links @ __construct
131
-        }
132
-
133
-        // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
134
-        $svg = preg_replace('/^.*?<svg|\s*(<\/svg>)(?!.*\1).*$|xlink:|\s(?:(?:version|xmlns)|(?:[a-z\-]+\:[a-z\-]+))="[^"]*"/s', '', $svg); // cleanup
135
-
136
-        // $svg = preg_replace('/(?<=(?:id|class)=")/', $hash.'__', $svg); // extend  IDs
137
-        // $svg = preg_replace('/(?<=href="|url\()#/', $hash.'__', $svg); // recover IDs
138
-
139
-        // $svg = preg_replace_callback('/<style[^>]*>(?<styl>.+?)<\/style>|<defs[^>]*>(?<defs>.+?)<\/defs>/s', function(array $match) use($hash): string {
140
-        //
141
-        //    if(isset($match['styl']))
142
-        //    {
143
-        //        $this->styl[] = preg_replace('/\s*(\.|#){1}(.+?)\s*\{/', '$1'.$hash.'__$2{', $match['styl']); // patch CSS # https://mathiasbynens.be/notes/css-escapes
144
-        //    }
145
-        //    if(isset($match['defs']))
146
-        //    {
147
-        //        $this->defs[] = trim($match['defs']);
148
-        //    }
149
-        //    return '';
150
-        // }, $svg);
151
-
152
-        // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg#attributes
153
-        $svg = preg_replace_callback('/([^>]*)\s*(?=>)/s', function (array $match) use (&$attr): string {
154
-            if (false === preg_match_all('/(?!\s)(?<attr>[a-z\-]+)="\s*(?<value>[^"]+)\s*"/i', $match[1], $matches)) {
155
-                return $match[0];
156
-            }
157
-            foreach ($matches['attr'] as $index => $attribute) {
158
-                switch ($attribute) {
159
-                    case 'id':
160
-                    case 'width':
161
-                    case 'height':
162
-                        unset($matches[0][$index]);
163
-                        break;
164
-
165
-                    case 'viewBox':
166
-                        if (false !== preg_match('/(?<minX>[-+]?[\d\.]+)\s(?<minY>[-+]?[\d\.]+)\s\+?(?<width>[\d\.]+)\s\+?(?<height>[\d\.]+)/', $matches['value'][$index], $match)) {
167
-                            $attr[] = \sprintf('%s="%s %s %s %s"', $attribute, $match['minX'], $match['minY'], $match['width'], $match['height']); // save!
168
-                        }
169
-                }
170
-            }
171
-
172
-            return implode(' ', $matches[0]);
173
-        }, $svg, 1);
174
-
175
-        if (empty($attr)) {
176
-            return null;
177
-        }
178
-
179
-        $this->svgs[] = \sprintf('id="%s" %s', $this->convertFilePath($path), $svg); // prepend ID
180
-
181
-        return ['attr' => implode(' ', $attr), 'hash' => $hash];
182
-    }
183
-
184
-    private function populateCache(): bool
185
-    {
186
-        $storageArr = GeneralUtility::makeInstance(StorageRepository::class)->findByStorageType('Local');
187
-        foreach ($storageArr as $storage) {
188
-            $storageConfig = $storage->getConfiguration();
189
-            if (!\is_array($storageConfig) || !isset($storageConfig['pathType'], $storageConfig['basePath'])) {
190
-                continue;
191
-            }
192
-            if ('relative' == $storageConfig['pathType']) {
193
-                $storageArr[$storage->getUid()] = rtrim($storageConfig['basePath'], '/'); // [^/]$
194
-            }
195
-        }
196
-        unset($storageArr[0]); // keep!
197
-
198
-        $fileArr = GeneralUtility::makeInstance(SvgFileRepository::class)->findAllByStorageUids(array_keys($storageArr));
199
-        foreach ($fileArr as $file) {
200
-            $file['path'] = '/' . $storageArr[$file['storage']] . $file['identifier']; // ^[/]
201
-            $file['defs'] = $this->addFileToSpriteArr($file['sha1'], $file['path']);
202
-
203
-            if (null !== $file['defs']) {
204
-                $this->svgFileArr[$file['path']] = $file['defs'];
205
-            }
206
-        }
207
-
208
-        if (empty($this->svgFileArr)) {
209
-            return true;
210
-        }
211
-
212
-        $svg = preg_replace_callback(
213
-            '/<use(?<pre>.*?)(?:xlink:)?href="(?<href>\/.+?\.svg)(?:#[^"]*?)?"(?<post>.*?)[\s\/]*>(?:<\/use>)?/s',
214
-            function (array $match): string {
215
-                if (!isset($this->svgFileArr[$match['href']])) { // check usage
216
-                    return $match[0];
217
-                }
218
-
219
-                return \sprintf('<use%s href="#%s"/>', $match['pre'] . $match['post'], $this->convertFilePath($match['href']));
220
-            },
221
-            '<svg xmlns="http://www.w3.org/2000/svg">'
222
-            // ."\n<style>\n".implode("\n", $this->styl)."\n</style>"
223
-            // ."\n<defs>\n".implode("\n", $this->defs)."\n</defs>"
224
-            . "\n<symbol " . implode("</symbol>\n<symbol ", $this->svgs) . "</symbol>\n"
225
-            . '</svg>'
226
-        );
227
-
228
-        if ($GLOBALS['TSFE']->config['config']['sourceopt.']['formatHtml'] ?? false) {
229
-            $svg = preg_replace('/(?<=>)\s+(?=<)/', '', $svg); // remove emptiness
230
-            $svg = preg_replace('/[\t\v]/', ' ', $svg); // prepare shrinkage
231
-            $svg = preg_replace('/\s{2,}/', ' ', $svg); // shrink whitespace
232
-        }
233
-
234
-        $svg = preg_replace('/<([a-z\-]+)\s*(\/|>\s*<\/\1)>\s*|\s+(?=\/>)/i', '', $svg); // remove emtpy TAGs & shorten endings
235
-        $svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify TAG syntax
236
-
237
-        if (!is_dir($this->sitePath . $this->outputDir)) {
238
-            GeneralUtility::mkdir_deep($this->sitePath . $this->outputDir);
239
-        }
240
-
241
-        $this->spritePath = $this->outputDir . hash('sha1', serialize($this->svgFileArr)) . '.svg';
242
-        if (false === file_put_contents($this->sitePath . $this->spritePath, $svg)) {
243
-            return false;
244
-        }
245
-
246
-        $this->svgCache->set('spritePath', $this->spritePath);
247
-        $this->svgCache->set('svgFileArr', $this->svgFileArr);
248
-
249
-        return true;
250
-    }
20
+	/**
21
+	 * SVG-Sprite relativ storage directory.
22
+	 */
23
+	protected string $outputDir = '/typo3temp/assets/svg/';
24
+
25
+	/**
26
+	 * TYPO3 absolute path to public web.
27
+	 */
28
+	protected string $sitePath = '';
29
+
30
+	/**
31
+	 * Final TYPO3 Frontend-Cache object.
32
+	 */
33
+	protected FrontendInterface $svgCache;
34
+
35
+	/**
36
+	 * Cached SVG-Sprite relativ file path.
37
+	 */
38
+	protected string $spritePath = '';
39
+
40
+	/**
41
+	 * Cached used SVG files (incl. defs).
42
+	 */
43
+	protected array $svgFileArr = [];
44
+
45
+	/**
46
+	 * Final SVG-Sprite Vectors.
47
+	 */
48
+	protected array $svgs = [];
49
+
50
+	/**
51
+	 * Final SVG-Sprite Styles.
52
+	 */
53
+	protected array $styl = []; // ToFix ; https://stackoverflow.com/questions/39583880/external-svg-fails-to-apply-internal-css
54
+
55
+	/**
56
+	 * Final SVG-Sprite Objects.
57
+	 */
58
+	protected array $defs = []; // ToFix ; https://bugs.chromium.org/p/chromium/issues/detail?id=751733#c14
59
+
60
+	public function __construct()
61
+	{
62
+		$this->sitePath = \TYPO3\CMS\Core\Core\Environment::getPublicPath(); // [^/]$
63
+		$this->svgCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('svgstore');
64
+
65
+		$this->spritePath = $this->svgCache->get('spritePath') ?: '';
66
+		$this->svgFileArr = $this->svgCache->get('svgFileArr') ?: [];
67
+
68
+		if (empty($this->spritePath) && !$this->populateCache()) {
69
+			throw new \Exception('could not write file: ' . $this->sitePath . $this->spritePath);
70
+		}
71
+
72
+		if (!file_exists($this->sitePath . $this->spritePath)) {
73
+			throw new \Exception('file does not exists: ' . $this->sitePath . $this->spritePath);
74
+		}
75
+	}
76
+
77
+	public function process(string $html): string
78
+	{
79
+		if (empty($this->svgFileArr)) {
80
+			return $html;
81
+		}
82
+
83
+		if ($GLOBALS['TSFE']->config['config']['disableAllHeaderCode'] ?? false) {
84
+			$dom = ['head' => '', 'body' => $html];
85
+		} elseif (!preg_match('/(?<head>.+?<\/head>)(?<body>.+)/s', $html, $dom)) {
86
+			return $html;
87
+		}
88
+
89
+		// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes
90
+		$dom['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?:https?:)?(?:\/\/[^\/]+?)?(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?!\s*<\/picture>)/s', function (array $match): string { // ^[/]
91
+			if (!isset($this->svgFileArr[$match['src']])) { // check usage
92
+				return $match[0];
93
+			}
94
+			$attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap|crossorigin|decoding|fetchpriority|referrerpolicy)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
95
+
96
+			return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['src']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['src']));
97
+		}, $dom['body']);
98
+
99
+		// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#attributes
100
+		$dom['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function (array $match): string { // ^[/]
101
+			if (!isset($this->svgFileArr[$match['data']])) { // check usage
102
+				return $match[0];
103
+			}
104
+			$attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
105
+
106
+			return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['data']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['data']));
107
+		}, $dom['body']);
108
+
109
+		return $dom['head'] . $dom['body'];
110
+	}
111
+
112
+	private function convertFilePath(string $path): string
113
+	{
114
+		return preg_replace('/.svg$|[^\w\-]/', '', str_replace('/', '-', ltrim($path, '/'))); // ^[^/]
115
+	}
116
+
117
+	private function addFileToSpriteArr(string $hash, string $path, array $attr = []): ?array
118
+	{
119
+		if (!file_exists($this->sitePath . $path)) {
120
+			return null;
121
+		}
122
+
123
+		$svg = file_get_contents($this->sitePath . $path);
124
+
125
+		if (preg_match('/(?:;base64|i:a?i?pgf)/', $svg)) { // noop!
126
+			return null;
127
+		}
128
+
129
+		if (preg_match('/<(?:style|defs)|url\(/', $svg)) {
130
+			return null; // check links @ __construct
131
+		}
132
+
133
+		// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
134
+		$svg = preg_replace('/^.*?<svg|\s*(<\/svg>)(?!.*\1).*$|xlink:|\s(?:(?:version|xmlns)|(?:[a-z\-]+\:[a-z\-]+))="[^"]*"/s', '', $svg); // cleanup
135
+
136
+		// $svg = preg_replace('/(?<=(?:id|class)=")/', $hash.'__', $svg); // extend  IDs
137
+		// $svg = preg_replace('/(?<=href="|url\()#/', $hash.'__', $svg); // recover IDs
138
+
139
+		// $svg = preg_replace_callback('/<style[^>]*>(?<styl>.+?)<\/style>|<defs[^>]*>(?<defs>.+?)<\/defs>/s', function(array $match) use($hash): string {
140
+		//
141
+		//    if(isset($match['styl']))
142
+		//    {
143
+		//        $this->styl[] = preg_replace('/\s*(\.|#){1}(.+?)\s*\{/', '$1'.$hash.'__$2{', $match['styl']); // patch CSS # https://mathiasbynens.be/notes/css-escapes
144
+		//    }
145
+		//    if(isset($match['defs']))
146
+		//    {
147
+		//        $this->defs[] = trim($match['defs']);
148
+		//    }
149
+		//    return '';
150
+		// }, $svg);
151
+
152
+		// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg#attributes
153
+		$svg = preg_replace_callback('/([^>]*)\s*(?=>)/s', function (array $match) use (&$attr): string {
154
+			if (false === preg_match_all('/(?!\s)(?<attr>[a-z\-]+)="\s*(?<value>[^"]+)\s*"/i', $match[1], $matches)) {
155
+				return $match[0];
156
+			}
157
+			foreach ($matches['attr'] as $index => $attribute) {
158
+				switch ($attribute) {
159
+					case 'id':
160
+					case 'width':
161
+					case 'height':
162
+						unset($matches[0][$index]);
163
+						break;
164
+
165
+					case 'viewBox':
166
+						if (false !== preg_match('/(?<minX>[-+]?[\d\.]+)\s(?<minY>[-+]?[\d\.]+)\s\+?(?<width>[\d\.]+)\s\+?(?<height>[\d\.]+)/', $matches['value'][$index], $match)) {
167
+							$attr[] = \sprintf('%s="%s %s %s %s"', $attribute, $match['minX'], $match['minY'], $match['width'], $match['height']); // save!
168
+						}
169
+				}
170
+			}
171
+
172
+			return implode(' ', $matches[0]);
173
+		}, $svg, 1);
174
+
175
+		if (empty($attr)) {
176
+			return null;
177
+		}
178
+
179
+		$this->svgs[] = \sprintf('id="%s" %s', $this->convertFilePath($path), $svg); // prepend ID
180
+
181
+		return ['attr' => implode(' ', $attr), 'hash' => $hash];
182
+	}
183
+
184
+	private function populateCache(): bool
185
+	{
186
+		$storageArr = GeneralUtility::makeInstance(StorageRepository::class)->findByStorageType('Local');
187
+		foreach ($storageArr as $storage) {
188
+			$storageConfig = $storage->getConfiguration();
189
+			if (!\is_array($storageConfig) || !isset($storageConfig['pathType'], $storageConfig['basePath'])) {
190
+				continue;
191
+			}
192
+			if ('relative' == $storageConfig['pathType']) {
193
+				$storageArr[$storage->getUid()] = rtrim($storageConfig['basePath'], '/'); // [^/]$
194
+			}
195
+		}
196
+		unset($storageArr[0]); // keep!
197
+
198
+		$fileArr = GeneralUtility::makeInstance(SvgFileRepository::class)->findAllByStorageUids(array_keys($storageArr));
199
+		foreach ($fileArr as $file) {
200
+			$file['path'] = '/' . $storageArr[$file['storage']] . $file['identifier']; // ^[/]
201
+			$file['defs'] = $this->addFileToSpriteArr($file['sha1'], $file['path']);
202
+
203
+			if (null !== $file['defs']) {
204
+				$this->svgFileArr[$file['path']] = $file['defs'];
205
+			}
206
+		}
207
+
208
+		if (empty($this->svgFileArr)) {
209
+			return true;
210
+		}
211
+
212
+		$svg = preg_replace_callback(
213
+			'/<use(?<pre>.*?)(?:xlink:)?href="(?<href>\/.+?\.svg)(?:#[^"]*?)?"(?<post>.*?)[\s\/]*>(?:<\/use>)?/s',
214
+			function (array $match): string {
215
+				if (!isset($this->svgFileArr[$match['href']])) { // check usage
216
+					return $match[0];
217
+				}
218
+
219
+				return \sprintf('<use%s href="#%s"/>', $match['pre'] . $match['post'], $this->convertFilePath($match['href']));
220
+			},
221
+			'<svg xmlns="http://www.w3.org/2000/svg">'
222
+			// ."\n<style>\n".implode("\n", $this->styl)."\n</style>"
223
+			// ."\n<defs>\n".implode("\n", $this->defs)."\n</defs>"
224
+			. "\n<symbol " . implode("</symbol>\n<symbol ", $this->svgs) . "</symbol>\n"
225
+			. '</svg>'
226
+		);
227
+
228
+		if ($GLOBALS['TSFE']->config['config']['sourceopt.']['formatHtml'] ?? false) {
229
+			$svg = preg_replace('/(?<=>)\s+(?=<)/', '', $svg); // remove emptiness
230
+			$svg = preg_replace('/[\t\v]/', ' ', $svg); // prepare shrinkage
231
+			$svg = preg_replace('/\s{2,}/', ' ', $svg); // shrink whitespace
232
+		}
233
+
234
+		$svg = preg_replace('/<([a-z\-]+)\s*(\/|>\s*<\/\1)>\s*|\s+(?=\/>)/i', '', $svg); // remove emtpy TAGs & shorten endings
235
+		$svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify TAG syntax
236
+
237
+		if (!is_dir($this->sitePath . $this->outputDir)) {
238
+			GeneralUtility::mkdir_deep($this->sitePath . $this->outputDir);
239
+		}
240
+
241
+		$this->spritePath = $this->outputDir . hash('sha1', serialize($this->svgFileArr)) . '.svg';
242
+		if (false === file_put_contents($this->sitePath . $this->spritePath, $svg)) {
243
+			return false;
244
+		}
245
+
246
+		$this->svgCache->set('spritePath', $this->spritePath);
247
+		$this->svgCache->set('svgFileArr', $this->svgFileArr);
248
+
249
+		return true;
250
+	}
251 251
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -66,11 +66,11 @@  discard block
 block discarded – undo
66 66
         $this->svgFileArr = $this->svgCache->get('svgFileArr') ?: [];
67 67
 
68 68
         if (empty($this->spritePath) && !$this->populateCache()) {
69
-            throw new \Exception('could not write file: ' . $this->sitePath . $this->spritePath);
69
+            throw new \Exception('could not write file: '.$this->sitePath.$this->spritePath);
70 70
         }
71 71
 
72
-        if (!file_exists($this->sitePath . $this->spritePath)) {
73
-            throw new \Exception('file does not exists: ' . $this->sitePath . $this->spritePath);
72
+        if (!file_exists($this->sitePath.$this->spritePath)) {
73
+            throw new \Exception('file does not exists: '.$this->sitePath.$this->spritePath);
74 74
         }
75 75
     }
76 76
 
@@ -87,26 +87,26 @@  discard block
 block discarded – undo
87 87
         }
88 88
 
89 89
         // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes
90
-        $dom['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?:https?:)?(?:\/\/[^\/]+?)?(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?!\s*<\/picture>)/s', function (array $match): string { // ^[/]
90
+        $dom['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?:https?:)?(?:\/\/[^\/]+?)?(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?!\s*<\/picture>)/s', function(array $match): string { // ^[/]
91 91
             if (!isset($this->svgFileArr[$match['src']])) { // check usage
92 92
                 return $match[0];
93 93
             }
94
-            $attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap|crossorigin|decoding|fetchpriority|referrerpolicy)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
94
+            $attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap|crossorigin|decoding|fetchpriority|referrerpolicy)="[^"]*"/', '', $match['pre'].$match['post']); // cleanup
95 95
 
96 96
             return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['src']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['src']));
97 97
         }, $dom['body']);
98 98
 
99 99
         // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#attributes
100
-        $dom['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function (array $match): string { // ^[/]
100
+        $dom['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function(array $match): string { // ^[/]
101 101
             if (!isset($this->svgFileArr[$match['data']])) { // check usage
102 102
                 return $match[0];
103 103
             }
104
-            $attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $match['pre'] . $match['post']); // cleanup
104
+            $attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $match['pre'].$match['post']); // cleanup
105 105
 
106 106
             return \sprintf('<svg %s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$match['data']]['attr'], trim($attr), $this->spritePath, $this->convertFilePath($match['data']));
107 107
         }, $dom['body']);
108 108
 
109
-        return $dom['head'] . $dom['body'];
109
+        return $dom['head'].$dom['body'];
110 110
     }
111 111
 
112 112
     private function convertFilePath(string $path): string
@@ -116,11 +116,11 @@  discard block
 block discarded – undo
116 116
 
117 117
     private function addFileToSpriteArr(string $hash, string $path, array $attr = []): ?array
118 118
     {
119
-        if (!file_exists($this->sitePath . $path)) {
119
+        if (!file_exists($this->sitePath.$path)) {
120 120
             return null;
121 121
         }
122 122
 
123
-        $svg = file_get_contents($this->sitePath . $path);
123
+        $svg = file_get_contents($this->sitePath.$path);
124 124
 
125 125
         if (preg_match('/(?:;base64|i:a?i?pgf)/', $svg)) { // noop!
126 126
             return null;
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
         // }, $svg);
151 151
 
152 152
         // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg#attributes
153
-        $svg = preg_replace_callback('/([^>]*)\s*(?=>)/s', function (array $match) use (&$attr): string {
153
+        $svg = preg_replace_callback('/([^>]*)\s*(?=>)/s', function(array $match) use (&$attr): string {
154 154
             if (false === preg_match_all('/(?!\s)(?<attr>[a-z\-]+)="\s*(?<value>[^"]+)\s*"/i', $match[1], $matches)) {
155 155
                 return $match[0];
156 156
             }
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
 
198 198
         $fileArr = GeneralUtility::makeInstance(SvgFileRepository::class)->findAllByStorageUids(array_keys($storageArr));
199 199
         foreach ($fileArr as $file) {
200
-            $file['path'] = '/' . $storageArr[$file['storage']] . $file['identifier']; // ^[/]
200
+            $file['path'] = '/'.$storageArr[$file['storage']].$file['identifier']; // ^[/]
201 201
             $file['defs'] = $this->addFileToSpriteArr($file['sha1'], $file['path']);
202 202
 
203 203
             if (null !== $file['defs']) {
@@ -211,17 +211,17 @@  discard block
 block discarded – undo
211 211
 
212 212
         $svg = preg_replace_callback(
213 213
             '/<use(?<pre>.*?)(?:xlink:)?href="(?<href>\/.+?\.svg)(?:#[^"]*?)?"(?<post>.*?)[\s\/]*>(?:<\/use>)?/s',
214
-            function (array $match): string {
214
+            function(array $match): string {
215 215
                 if (!isset($this->svgFileArr[$match['href']])) { // check usage
216 216
                     return $match[0];
217 217
                 }
218 218
 
219
-                return \sprintf('<use%s href="#%s"/>', $match['pre'] . $match['post'], $this->convertFilePath($match['href']));
219
+                return \sprintf('<use%s href="#%s"/>', $match['pre'].$match['post'], $this->convertFilePath($match['href']));
220 220
             },
221 221
             '<svg xmlns="http://www.w3.org/2000/svg">'
222 222
             // ."\n<style>\n".implode("\n", $this->styl)."\n</style>"
223 223
             // ."\n<defs>\n".implode("\n", $this->defs)."\n</defs>"
224
-            . "\n<symbol " . implode("</symbol>\n<symbol ", $this->svgs) . "</symbol>\n"
224
+            . "\n<symbol ".implode("</symbol>\n<symbol ", $this->svgs)."</symbol>\n"
225 225
             . '</svg>'
226 226
         );
227 227
 
@@ -234,12 +234,12 @@  discard block
 block discarded – undo
234 234
         $svg = preg_replace('/<([a-z\-]+)\s*(\/|>\s*<\/\1)>\s*|\s+(?=\/>)/i', '', $svg); // remove emtpy TAGs & shorten endings
235 235
         $svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify TAG syntax
236 236
 
237
-        if (!is_dir($this->sitePath . $this->outputDir)) {
238
-            GeneralUtility::mkdir_deep($this->sitePath . $this->outputDir);
237
+        if (!is_dir($this->sitePath.$this->outputDir)) {
238
+            GeneralUtility::mkdir_deep($this->sitePath.$this->outputDir);
239 239
         }
240 240
 
241
-        $this->spritePath = $this->outputDir . hash('sha1', serialize($this->svgFileArr)) . '.svg';
242
-        if (false === file_put_contents($this->sitePath . $this->spritePath, $svg)) {
241
+        $this->spritePath = $this->outputDir.hash('sha1', serialize($this->svgFileArr)).'.svg';
242
+        if (false === file_put_contents($this->sitePath.$this->spritePath, $svg)) {
243 243
             return false;
244 244
         }
245 245
 
Please login to merge, or discard this patch.
Classes/Service/CleanHtmlService.php 2 patches
Indentation   +370 added lines, -370 removed lines patch added patch discarded remove patch
@@ -17,374 +17,374 @@
 block discarded – undo
17 17
  */
18 18
 class CleanHtmlService implements SingletonInterface
19 19
 {
20
-    /**
21
-     * Enable Debug comment in footer.
22
-     */
23
-    protected bool $debugComment = false;
24
-
25
-    /**
26
-     * Format Type.
27
-     */
28
-    protected int $formatType = 0;
29
-
30
-    /**
31
-     * Tab character.
32
-     */
33
-    protected string $tab = "\t";
34
-
35
-    /**
36
-     * Newline character.
37
-     */
38
-    protected string $newline = "\n";
39
-
40
-    /**
41
-     * Configured extra header comment.
42
-     */
43
-    protected string $headerComment = '';
44
-
45
-    /**
46
-     * Empty space char.
47
-     */
48
-    protected string $emptySpaceChar = ' ';
49
-
50
-    /**
51
-     * Set variables based on given config.
52
-     */
53
-    public function setVariables(array $config): void
54
-    {
55
-        if (isset($config['headerComment']) && !empty($config['headerComment'])) {
56
-            $this->headerComment = $config['headerComment'];
57
-        }
58
-
59
-        if (isset($config['formatHtml']) && is_numeric($config['formatHtml'])) {
60
-            $this->formatType = (int) $config['formatHtml'];
61
-        }
62
-
63
-        if (isset($config['formatHtml.']['tabSize']) && is_numeric($config['formatHtml.']['tabSize'])) {
64
-            $this->tab = str_pad('', (int) $config['formatHtml.']['tabSize'], ' ');
65
-        }
66
-
67
-        if (isset($config['formatHtml.']['debugComment'])) {
68
-            $this->debugComment = (bool) $config['formatHtml.']['debugComment'];
69
-        }
70
-
71
-        if (isset($config['dropEmptySpaceChar']) && (bool) $config['dropEmptySpaceChar']) {
72
-            $this->emptySpaceChar = '';
73
-        }
74
-    }
75
-
76
-    /**
77
-     * Clean given HTML with formatter.
78
-     */
79
-    public function clean(string $html, array $config = []): string
80
-    {
81
-        if (!empty($config)) {
82
-            $this->setVariables($config);
83
-        }
84
-
85
-        // convert line-breaks to UNIX
86
-        $this->convNlOs($html);
87
-
88
-        $manipulations = [];
89
-
90
-        if (isset($config['removeGenerator']) && (bool) $config['removeGenerator']) {
91
-            $manipulations['removeGenerator'] = GeneralUtility::makeInstance(RemoveGenerator::class);
92
-        }
93
-
94
-        if (isset($config['removeComments']) && (bool) $config['removeComments']) {
95
-            $manipulations['removeComments'] = GeneralUtility::makeInstance(RemoveComments::class);
96
-        }
97
-
98
-        if (!empty($this->headerComment)) {
99
-            $this->includeHeaderComment($html);
100
-        }
101
-
102
-        foreach ($manipulations as $key => $manipulation) {
103
-            /** @var ManipulationInterface $manipulation */
104
-            $configuration = isset($config[$key . '.']) && \is_array($config[$key . '.']) ? $config[$key . '.'] : [];
105
-            $html = $manipulation->manipulate($html, $configuration);
106
-        }
107
-
108
-        // cleanup HTML5 self-closing elements
109
-        if (!isset($GLOBALS['TSFE']->config['config']['doctype'])
110
-            || 'x' !== substr($GLOBALS['TSFE']->config['config']['doctype'], 0, 1)) {
111
-            $html = preg_replace(
112
-                '/<((?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)\s[^>]+?)\s*\\\?\/>/',
113
-                '<$1>',
114
-                $html
115
-            );
116
-        }
117
-
118
-        if ($this->formatType > 0) {
119
-            $html = $this->formatHtml($html);
120
-        }
121
-
122
-        // remove white space after line ending
123
-        $this->rTrimLines($html);
124
-
125
-        // recover line-breaks
126
-        if (Environment::isWindows()) {
127
-            $html = str_replace($this->newline, "\r\n", $html);
128
-        }
129
-
130
-        return (string) $html;
131
-    }
132
-
133
-    /**
134
-     * Formats the (X)HTML code:
135
-     *  - taps according to the hirarchy of the tags
136
-     *  - removes empty spaces between tags
137
-     *  - removes linebreaks within tags (spares where necessary: pre, textarea, comments, ..)
138
-     *  choose from five options:
139
-     *    0 => off
140
-     *    1 => no line break at all  (code in one line)
141
-     *    2 => minimalistic line breaks (structure defining box-elements)
142
-     *    3 => aesthetic line breaks (important box-elements)
143
-     *    4 => logic line breaks (all box-elements)
144
-     *    5 => max line breaks (all elements).
145
-     */
146
-    protected function formatHtml(string $html): string
147
-    {
148
-        // Save original formated pre, textarea, comments, styles and scripts & replace them with markers
149
-        preg_match_all(
150
-            '/(?s)((<!--.*?-->)|(<[ \n\r]*pre[^>]*>.*?<[ \n\r]*\/pre[^>]*>)|(<[ \n\r]*textarea[^>]*>.*?<[ \n\r]*\/textarea[^>]*>)|(<[ \n\r]*style[^>]*>.*?<[ \n\r]*\/style[^>]*>)|(<[ \n\r]*script[^>]*>.*?<[ \n\r]*\/script[^>]*>))/im',
151
-            $html,
152
-            $matches
153
-        );
154
-        $noFormat = $matches[0]; // do not format these block elements
155
-        for ($i = 0; $i < \count($noFormat); ++$i) {
156
-            $html = str_replace($noFormat[$i], "\n<!-- ELEMENT {$i} -->", $html);
157
-        }
158
-
159
-        // define box elements for formatting
160
-        $trueBoxElements = 'address|blockquote|center|dir|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|isindex|menu|noframes|noscript|ol|p|pre|table|ul|article|aside|details|figcaption|figure|footer|header|hgroup|menu|nav|section';
161
-        $functionalBoxElements = 'dd|dt|frameset|li|tbody|td|tfoot|th|thead|tr|colgroup';
162
-        $usableBoxElements = 'applet|button|del|iframe|ins|map|object|script';
163
-        $imagineBoxElements = 'html|body|head|meta|title|link|script|base|!--';
164
-        $allBoxLikeElements = '(?>' . $trueBoxElements . '|' . $functionalBoxElements . '|' . $usableBoxElements . '|' . $imagineBoxElements . ')';
165
-        $esteticBoxLikeElements = '(?>html|head|body|meta name|title|div|table|h1|h2|h3|h4|h5|h6|p|form|pre|center|!--)';
166
-        $structureBoxLikeElements = '(?>html|head|body|div|!--)';
167
-
168
-        // split html into it's elements
169
-        $htmlArrayTemp = preg_split(
170
-            '/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/',
171
-            $html,
172
-            -1,
173
-            \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY
174
-        );
175
-
176
-        if (false === $htmlArrayTemp) {
177
-            // Restore saved comments, styles and scripts
178
-            for ($i = 0; $i < \count($noFormat); ++$i) {
179
-                $html = str_replace("<!-- ELEMENT {$i} -->", $noFormat[$i], $html);
180
-            }
181
-
182
-            return $html;
183
-        }
184
-        // remove empty lines
185
-        $htmlArray = [''];
186
-        $index = 1;
187
-        for ($x = 0; $x < \count($htmlArrayTemp); ++$x) {
188
-            $text = trim($htmlArrayTemp[$x]);
189
-            $htmlArray[$index] = '' !== $text ? $htmlArrayTemp[$x] : $this->emptySpaceChar;
190
-            ++$index;
191
-        }
192
-
193
-        // rebuild html
194
-        $html = '';
195
-        $tabs = 0;
196
-        for ($x = 0; $x < \count($htmlArray); ++$x) {
197
-            $htmlArrayBefore = $htmlArray[$x - 1] ?? '';
198
-            $htmlArrayCurrent = $htmlArray[$x] ?? '';
199
-
200
-            // check if the element should stand in a new line
201
-            $newline = false;
202
-            if ('<?xml' == substr($htmlArrayBefore, 0, 5)) {
203
-                $newline = true;
204
-            } elseif (2 == $this->formatType && ( // minimalistic line break
205
-                // this element has a line break before itself
206
-                preg_match(
207
-                    '/<' . $structureBoxLikeElements . '(.*)>/Usi',
208
-                    $htmlArrayCurrent
209
-                ) || preg_match(
210
-                    '/<' . $structureBoxLikeElements . '(.*) \/>/Usi',
211
-                    $htmlArrayCurrent
212
-                ) // one element before is a element that has a line break after
213
-                || preg_match(
214
-                    '/<\/' . $structureBoxLikeElements . '(.*)>/Usi',
215
-                    $htmlArrayBefore
216
-                ) || '<!--' == substr(
217
-                    $htmlArrayBefore,
218
-                    0,
219
-                    4
220
-                ) || preg_match('/<' . $structureBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
221
-            ) {
222
-                $newline = true;
223
-            } elseif (3 == $this->formatType && ( // aestetic line break
224
-                // this element has a line break before itself
225
-                preg_match(
226
-                    '/<' . $esteticBoxLikeElements . '(.*)>/Usi',
227
-                    $htmlArrayCurrent
228
-                ) || preg_match(
229
-                    '/<' . $esteticBoxLikeElements . '(.*) \/>/Usi',
230
-                    $htmlArrayCurrent
231
-                ) // one element before is a element that has a line break after
232
-                || preg_match('/<\/' . $esteticBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
233
-                    $htmlArrayBefore,
234
-                    0,
235
-                    4
236
-                ) || preg_match('/<' . $esteticBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
237
-            ) {
238
-                $newline = true;
239
-            } elseif ($this->formatType >= 4 && ( // logical line break
240
-                // this element has a line break before itself
241
-                preg_match(
242
-                    '/<' . $allBoxLikeElements . '(.*)>/Usi',
243
-                    $htmlArrayCurrent
244
-                ) || preg_match(
245
-                    '/<' . $allBoxLikeElements . '(.*) \/>/Usi',
246
-                    $htmlArrayCurrent
247
-                ) // one element before is a element that has a line break after
248
-                || preg_match('/<\/' . $allBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
249
-                    $htmlArrayBefore,
250
-                    0,
251
-                    4
252
-                ) || preg_match('/<' . $allBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
253
-            ) {
254
-                $newline = true;
255
-            }
256
-
257
-            // count down a tab
258
-            if ('</' == substr($htmlArrayCurrent, 0, 2)) {
259
-                --$tabs;
260
-            }
261
-
262
-            // add tabs and line breaks in front of the current tag
263
-            if ($newline) {
264
-                $html .= $this->newline;
265
-                for ($y = 0; $y < $tabs; ++$y) {
266
-                    $html .= $this->tab;
267
-                }
268
-            }
269
-
270
-            // remove white spaces and line breaks and add current tag to the html-string
271
-            if ('<![CDATA[' == substr($htmlArrayCurrent, 0, 9) // remove multiple white space in CDATA / XML
272
-                || '<?xml' == substr($htmlArrayCurrent, 0, 5)
273
-            ) {
274
-                $html .= $this->killWhiteSpace($htmlArrayCurrent);
275
-            } else { // remove all line breaks
276
-                $html .= $this->killLineBreaks($htmlArrayCurrent);
277
-            }
278
-
279
-            // count up a tab
280
-            if ('<' == substr($htmlArrayCurrent, 0, 1) && '/' != substr($htmlArrayCurrent, 1, 1)) {
281
-                if (' ' !== substr($htmlArrayCurrent, 1, 1)
282
-                    && 'img' !== substr($htmlArrayCurrent, 1, 3)
283
-                    && 'source' !== substr($htmlArrayCurrent, 1, 6)
284
-                    && 'br' !== substr($htmlArrayCurrent, 1, 2)
285
-                    && 'hr' !== substr($htmlArrayCurrent, 1, 2)
286
-                    && 'input' !== substr($htmlArrayCurrent, 1, 5)
287
-                    && 'link' !== substr($htmlArrayCurrent, 1, 4)
288
-                    && 'meta' !== substr($htmlArrayCurrent, 1, 4)
289
-                    && 'col ' !== substr($htmlArrayCurrent, 1, 4)
290
-                    && 'frame' !== substr($htmlArrayCurrent, 1, 5)
291
-                    && 'isindex' !== substr($htmlArrayCurrent, 1, 7)
292
-                    && 'param' !== substr($htmlArrayCurrent, 1, 5)
293
-                    && 'area' !== substr($htmlArrayCurrent, 1, 4)
294
-                    && 'base' !== substr($htmlArrayCurrent, 1, 4)
295
-                    && '<!' !== substr($htmlArrayCurrent, 0, 2)
296
-                    && '<?xml' !== substr($htmlArrayCurrent, 0, 5)
297
-                ) {
298
-                    ++$tabs;
299
-                }
300
-            }
301
-        }
302
-
303
-        // Remove empty lines
304
-        if ($this->formatType > 1) {
305
-            $this->removeEmptyLines($html);
306
-        }
307
-
308
-        // Restore saved comments, styles and scripts
309
-        for ($i = 0; $i < \count($noFormat); ++$i) {
310
-            $html = str_replace("<!-- ELEMENT {$i} -->", $noFormat[$i], $html);
311
-        }
312
-
313
-        // include debug comment at the end
314
-        if (0 != $tabs && true === $this->debugComment) {
315
-            $html .= "<!-- {$tabs} open elements found -->";
316
-        }
317
-
318
-        return $html;
319
-    }
320
-
321
-    /**
322
-     * Remove ALL line breaks and multiple white space.
323
-     */
324
-    protected function killLineBreaks(string $html): string
325
-    {
326
-        $html = str_replace($this->newline, '', $html);
327
-
328
-        return preg_replace('/\s\s+/u', ' ', $html);
329
-        // ? return preg_replace('/\n|\s+(\s)/u', '$1', $html);
330
-    }
331
-
332
-    /**
333
-     * Remove multiple white space, keeps line breaks.
334
-     */
335
-    protected function killWhiteSpace(string $html): string
336
-    {
337
-        $temp = explode($this->newline, $html);
338
-        for ($i = 0; $i < \count($temp); ++$i) {
339
-            if (!trim($temp[$i])) {
340
-                unset($temp[$i]);
341
-                continue;
342
-            }
343
-
344
-            $temp[$i] = trim($temp[$i]);
345
-            $temp[$i] = preg_replace('/\s\s+/', ' ', $temp[$i]);
346
-        }
347
-
348
-        return implode($this->newline, $temp);
349
-    }
350
-
351
-    /**
352
-     * Remove white space at the end of lines, keeps other white space and line breaks.
353
-     */
354
-    protected function rTrimLines(string &$html): void
355
-    {
356
-        $html = preg_replace('/\s+$/m', '', $html);
357
-    }
358
-
359
-    /**
360
-     * Convert newlines according to the current OS.
361
-     */
362
-    protected function convNlOs(string &$html): void
363
-    {
364
-        $html = preg_replace("(\r\n|\r)", $this->newline, $html);
365
-    }
366
-
367
-    /**
368
-     * Remove empty lines.
369
-     */
370
-    protected function removeEmptyLines(string &$html): void
371
-    {
372
-        $temp = explode($this->newline, $html);
373
-        $result = [];
374
-        for ($i = 0; $i < \count($temp); ++$i) {
375
-            if ('' == trim($temp[$i])) {
376
-                continue;
377
-            }
378
-            $result[] = $temp[$i];
379
-        }
380
-        $html = implode($this->newline, $result);
381
-    }
382
-
383
-    /**
384
-     * Include configured header comment in HTML content block.
385
-     */
386
-    public function includeHeaderComment(string &$html): void
387
-    {
388
-        $html = preg_replace('/^(-->)$/m', "\n\t" . $this->headerComment . "\n$1", $html);
389
-    }
20
+	/**
21
+	 * Enable Debug comment in footer.
22
+	 */
23
+	protected bool $debugComment = false;
24
+
25
+	/**
26
+	 * Format Type.
27
+	 */
28
+	protected int $formatType = 0;
29
+
30
+	/**
31
+	 * Tab character.
32
+	 */
33
+	protected string $tab = "\t";
34
+
35
+	/**
36
+	 * Newline character.
37
+	 */
38
+	protected string $newline = "\n";
39
+
40
+	/**
41
+	 * Configured extra header comment.
42
+	 */
43
+	protected string $headerComment = '';
44
+
45
+	/**
46
+	 * Empty space char.
47
+	 */
48
+	protected string $emptySpaceChar = ' ';
49
+
50
+	/**
51
+	 * Set variables based on given config.
52
+	 */
53
+	public function setVariables(array $config): void
54
+	{
55
+		if (isset($config['headerComment']) && !empty($config['headerComment'])) {
56
+			$this->headerComment = $config['headerComment'];
57
+		}
58
+
59
+		if (isset($config['formatHtml']) && is_numeric($config['formatHtml'])) {
60
+			$this->formatType = (int) $config['formatHtml'];
61
+		}
62
+
63
+		if (isset($config['formatHtml.']['tabSize']) && is_numeric($config['formatHtml.']['tabSize'])) {
64
+			$this->tab = str_pad('', (int) $config['formatHtml.']['tabSize'], ' ');
65
+		}
66
+
67
+		if (isset($config['formatHtml.']['debugComment'])) {
68
+			$this->debugComment = (bool) $config['formatHtml.']['debugComment'];
69
+		}
70
+
71
+		if (isset($config['dropEmptySpaceChar']) && (bool) $config['dropEmptySpaceChar']) {
72
+			$this->emptySpaceChar = '';
73
+		}
74
+	}
75
+
76
+	/**
77
+	 * Clean given HTML with formatter.
78
+	 */
79
+	public function clean(string $html, array $config = []): string
80
+	{
81
+		if (!empty($config)) {
82
+			$this->setVariables($config);
83
+		}
84
+
85
+		// convert line-breaks to UNIX
86
+		$this->convNlOs($html);
87
+
88
+		$manipulations = [];
89
+
90
+		if (isset($config['removeGenerator']) && (bool) $config['removeGenerator']) {
91
+			$manipulations['removeGenerator'] = GeneralUtility::makeInstance(RemoveGenerator::class);
92
+		}
93
+
94
+		if (isset($config['removeComments']) && (bool) $config['removeComments']) {
95
+			$manipulations['removeComments'] = GeneralUtility::makeInstance(RemoveComments::class);
96
+		}
97
+
98
+		if (!empty($this->headerComment)) {
99
+			$this->includeHeaderComment($html);
100
+		}
101
+
102
+		foreach ($manipulations as $key => $manipulation) {
103
+			/** @var ManipulationInterface $manipulation */
104
+			$configuration = isset($config[$key . '.']) && \is_array($config[$key . '.']) ? $config[$key . '.'] : [];
105
+			$html = $manipulation->manipulate($html, $configuration);
106
+		}
107
+
108
+		// cleanup HTML5 self-closing elements
109
+		if (!isset($GLOBALS['TSFE']->config['config']['doctype'])
110
+			|| 'x' !== substr($GLOBALS['TSFE']->config['config']['doctype'], 0, 1)) {
111
+			$html = preg_replace(
112
+				'/<((?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)\s[^>]+?)\s*\\\?\/>/',
113
+				'<$1>',
114
+				$html
115
+			);
116
+		}
117
+
118
+		if ($this->formatType > 0) {
119
+			$html = $this->formatHtml($html);
120
+		}
121
+
122
+		// remove white space after line ending
123
+		$this->rTrimLines($html);
124
+
125
+		// recover line-breaks
126
+		if (Environment::isWindows()) {
127
+			$html = str_replace($this->newline, "\r\n", $html);
128
+		}
129
+
130
+		return (string) $html;
131
+	}
132
+
133
+	/**
134
+	 * Formats the (X)HTML code:
135
+	 *  - taps according to the hirarchy of the tags
136
+	 *  - removes empty spaces between tags
137
+	 *  - removes linebreaks within tags (spares where necessary: pre, textarea, comments, ..)
138
+	 *  choose from five options:
139
+	 *    0 => off
140
+	 *    1 => no line break at all  (code in one line)
141
+	 *    2 => minimalistic line breaks (structure defining box-elements)
142
+	 *    3 => aesthetic line breaks (important box-elements)
143
+	 *    4 => logic line breaks (all box-elements)
144
+	 *    5 => max line breaks (all elements).
145
+	 */
146
+	protected function formatHtml(string $html): string
147
+	{
148
+		// Save original formated pre, textarea, comments, styles and scripts & replace them with markers
149
+		preg_match_all(
150
+			'/(?s)((<!--.*?-->)|(<[ \n\r]*pre[^>]*>.*?<[ \n\r]*\/pre[^>]*>)|(<[ \n\r]*textarea[^>]*>.*?<[ \n\r]*\/textarea[^>]*>)|(<[ \n\r]*style[^>]*>.*?<[ \n\r]*\/style[^>]*>)|(<[ \n\r]*script[^>]*>.*?<[ \n\r]*\/script[^>]*>))/im',
151
+			$html,
152
+			$matches
153
+		);
154
+		$noFormat = $matches[0]; // do not format these block elements
155
+		for ($i = 0; $i < \count($noFormat); ++$i) {
156
+			$html = str_replace($noFormat[$i], "\n<!-- ELEMENT {$i} -->", $html);
157
+		}
158
+
159
+		// define box elements for formatting
160
+		$trueBoxElements = 'address|blockquote|center|dir|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|isindex|menu|noframes|noscript|ol|p|pre|table|ul|article|aside|details|figcaption|figure|footer|header|hgroup|menu|nav|section';
161
+		$functionalBoxElements = 'dd|dt|frameset|li|tbody|td|tfoot|th|thead|tr|colgroup';
162
+		$usableBoxElements = 'applet|button|del|iframe|ins|map|object|script';
163
+		$imagineBoxElements = 'html|body|head|meta|title|link|script|base|!--';
164
+		$allBoxLikeElements = '(?>' . $trueBoxElements . '|' . $functionalBoxElements . '|' . $usableBoxElements . '|' . $imagineBoxElements . ')';
165
+		$esteticBoxLikeElements = '(?>html|head|body|meta name|title|div|table|h1|h2|h3|h4|h5|h6|p|form|pre|center|!--)';
166
+		$structureBoxLikeElements = '(?>html|head|body|div|!--)';
167
+
168
+		// split html into it's elements
169
+		$htmlArrayTemp = preg_split(
170
+			'/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/',
171
+			$html,
172
+			-1,
173
+			\PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY
174
+		);
175
+
176
+		if (false === $htmlArrayTemp) {
177
+			// Restore saved comments, styles and scripts
178
+			for ($i = 0; $i < \count($noFormat); ++$i) {
179
+				$html = str_replace("<!-- ELEMENT {$i} -->", $noFormat[$i], $html);
180
+			}
181
+
182
+			return $html;
183
+		}
184
+		// remove empty lines
185
+		$htmlArray = [''];
186
+		$index = 1;
187
+		for ($x = 0; $x < \count($htmlArrayTemp); ++$x) {
188
+			$text = trim($htmlArrayTemp[$x]);
189
+			$htmlArray[$index] = '' !== $text ? $htmlArrayTemp[$x] : $this->emptySpaceChar;
190
+			++$index;
191
+		}
192
+
193
+		// rebuild html
194
+		$html = '';
195
+		$tabs = 0;
196
+		for ($x = 0; $x < \count($htmlArray); ++$x) {
197
+			$htmlArrayBefore = $htmlArray[$x - 1] ?? '';
198
+			$htmlArrayCurrent = $htmlArray[$x] ?? '';
199
+
200
+			// check if the element should stand in a new line
201
+			$newline = false;
202
+			if ('<?xml' == substr($htmlArrayBefore, 0, 5)) {
203
+				$newline = true;
204
+			} elseif (2 == $this->formatType && ( // minimalistic line break
205
+				// this element has a line break before itself
206
+				preg_match(
207
+					'/<' . $structureBoxLikeElements . '(.*)>/Usi',
208
+					$htmlArrayCurrent
209
+				) || preg_match(
210
+					'/<' . $structureBoxLikeElements . '(.*) \/>/Usi',
211
+					$htmlArrayCurrent
212
+				) // one element before is a element that has a line break after
213
+				|| preg_match(
214
+					'/<\/' . $structureBoxLikeElements . '(.*)>/Usi',
215
+					$htmlArrayBefore
216
+				) || '<!--' == substr(
217
+					$htmlArrayBefore,
218
+					0,
219
+					4
220
+				) || preg_match('/<' . $structureBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
221
+			) {
222
+				$newline = true;
223
+			} elseif (3 == $this->formatType && ( // aestetic line break
224
+				// this element has a line break before itself
225
+				preg_match(
226
+					'/<' . $esteticBoxLikeElements . '(.*)>/Usi',
227
+					$htmlArrayCurrent
228
+				) || preg_match(
229
+					'/<' . $esteticBoxLikeElements . '(.*) \/>/Usi',
230
+					$htmlArrayCurrent
231
+				) // one element before is a element that has a line break after
232
+				|| preg_match('/<\/' . $esteticBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
233
+					$htmlArrayBefore,
234
+					0,
235
+					4
236
+				) || preg_match('/<' . $esteticBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
237
+			) {
238
+				$newline = true;
239
+			} elseif ($this->formatType >= 4 && ( // logical line break
240
+				// this element has a line break before itself
241
+				preg_match(
242
+					'/<' . $allBoxLikeElements . '(.*)>/Usi',
243
+					$htmlArrayCurrent
244
+				) || preg_match(
245
+					'/<' . $allBoxLikeElements . '(.*) \/>/Usi',
246
+					$htmlArrayCurrent
247
+				) // one element before is a element that has a line break after
248
+				|| preg_match('/<\/' . $allBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
249
+					$htmlArrayBefore,
250
+					0,
251
+					4
252
+				) || preg_match('/<' . $allBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
253
+			) {
254
+				$newline = true;
255
+			}
256
+
257
+			// count down a tab
258
+			if ('</' == substr($htmlArrayCurrent, 0, 2)) {
259
+				--$tabs;
260
+			}
261
+
262
+			// add tabs and line breaks in front of the current tag
263
+			if ($newline) {
264
+				$html .= $this->newline;
265
+				for ($y = 0; $y < $tabs; ++$y) {
266
+					$html .= $this->tab;
267
+				}
268
+			}
269
+
270
+			// remove white spaces and line breaks and add current tag to the html-string
271
+			if ('<![CDATA[' == substr($htmlArrayCurrent, 0, 9) // remove multiple white space in CDATA / XML
272
+				|| '<?xml' == substr($htmlArrayCurrent, 0, 5)
273
+			) {
274
+				$html .= $this->killWhiteSpace($htmlArrayCurrent);
275
+			} else { // remove all line breaks
276
+				$html .= $this->killLineBreaks($htmlArrayCurrent);
277
+			}
278
+
279
+			// count up a tab
280
+			if ('<' == substr($htmlArrayCurrent, 0, 1) && '/' != substr($htmlArrayCurrent, 1, 1)) {
281
+				if (' ' !== substr($htmlArrayCurrent, 1, 1)
282
+					&& 'img' !== substr($htmlArrayCurrent, 1, 3)
283
+					&& 'source' !== substr($htmlArrayCurrent, 1, 6)
284
+					&& 'br' !== substr($htmlArrayCurrent, 1, 2)
285
+					&& 'hr' !== substr($htmlArrayCurrent, 1, 2)
286
+					&& 'input' !== substr($htmlArrayCurrent, 1, 5)
287
+					&& 'link' !== substr($htmlArrayCurrent, 1, 4)
288
+					&& 'meta' !== substr($htmlArrayCurrent, 1, 4)
289
+					&& 'col ' !== substr($htmlArrayCurrent, 1, 4)
290
+					&& 'frame' !== substr($htmlArrayCurrent, 1, 5)
291
+					&& 'isindex' !== substr($htmlArrayCurrent, 1, 7)
292
+					&& 'param' !== substr($htmlArrayCurrent, 1, 5)
293
+					&& 'area' !== substr($htmlArrayCurrent, 1, 4)
294
+					&& 'base' !== substr($htmlArrayCurrent, 1, 4)
295
+					&& '<!' !== substr($htmlArrayCurrent, 0, 2)
296
+					&& '<?xml' !== substr($htmlArrayCurrent, 0, 5)
297
+				) {
298
+					++$tabs;
299
+				}
300
+			}
301
+		}
302
+
303
+		// Remove empty lines
304
+		if ($this->formatType > 1) {
305
+			$this->removeEmptyLines($html);
306
+		}
307
+
308
+		// Restore saved comments, styles and scripts
309
+		for ($i = 0; $i < \count($noFormat); ++$i) {
310
+			$html = str_replace("<!-- ELEMENT {$i} -->", $noFormat[$i], $html);
311
+		}
312
+
313
+		// include debug comment at the end
314
+		if (0 != $tabs && true === $this->debugComment) {
315
+			$html .= "<!-- {$tabs} open elements found -->";
316
+		}
317
+
318
+		return $html;
319
+	}
320
+
321
+	/**
322
+	 * Remove ALL line breaks and multiple white space.
323
+	 */
324
+	protected function killLineBreaks(string $html): string
325
+	{
326
+		$html = str_replace($this->newline, '', $html);
327
+
328
+		return preg_replace('/\s\s+/u', ' ', $html);
329
+		// ? return preg_replace('/\n|\s+(\s)/u', '$1', $html);
330
+	}
331
+
332
+	/**
333
+	 * Remove multiple white space, keeps line breaks.
334
+	 */
335
+	protected function killWhiteSpace(string $html): string
336
+	{
337
+		$temp = explode($this->newline, $html);
338
+		for ($i = 0; $i < \count($temp); ++$i) {
339
+			if (!trim($temp[$i])) {
340
+				unset($temp[$i]);
341
+				continue;
342
+			}
343
+
344
+			$temp[$i] = trim($temp[$i]);
345
+			$temp[$i] = preg_replace('/\s\s+/', ' ', $temp[$i]);
346
+		}
347
+
348
+		return implode($this->newline, $temp);
349
+	}
350
+
351
+	/**
352
+	 * Remove white space at the end of lines, keeps other white space and line breaks.
353
+	 */
354
+	protected function rTrimLines(string &$html): void
355
+	{
356
+		$html = preg_replace('/\s+$/m', '', $html);
357
+	}
358
+
359
+	/**
360
+	 * Convert newlines according to the current OS.
361
+	 */
362
+	protected function convNlOs(string &$html): void
363
+	{
364
+		$html = preg_replace("(\r\n|\r)", $this->newline, $html);
365
+	}
366
+
367
+	/**
368
+	 * Remove empty lines.
369
+	 */
370
+	protected function removeEmptyLines(string &$html): void
371
+	{
372
+		$temp = explode($this->newline, $html);
373
+		$result = [];
374
+		for ($i = 0; $i < \count($temp); ++$i) {
375
+			if ('' == trim($temp[$i])) {
376
+				continue;
377
+			}
378
+			$result[] = $temp[$i];
379
+		}
380
+		$html = implode($this->newline, $result);
381
+	}
382
+
383
+	/**
384
+	 * Include configured header comment in HTML content block.
385
+	 */
386
+	public function includeHeaderComment(string &$html): void
387
+	{
388
+		$html = preg_replace('/^(-->)$/m', "\n\t" . $this->headerComment . "\n$1", $html);
389
+	}
390 390
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
 
102 102
         foreach ($manipulations as $key => $manipulation) {
103 103
             /** @var ManipulationInterface $manipulation */
104
-            $configuration = isset($config[$key . '.']) && \is_array($config[$key . '.']) ? $config[$key . '.'] : [];
104
+            $configuration = isset($config[$key.'.']) && \is_array($config[$key.'.']) ? $config[$key.'.'] : [];
105 105
             $html = $manipulation->manipulate($html, $configuration);
106 106
         }
107 107
 
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
         $functionalBoxElements = 'dd|dt|frameset|li|tbody|td|tfoot|th|thead|tr|colgroup';
162 162
         $usableBoxElements = 'applet|button|del|iframe|ins|map|object|script';
163 163
         $imagineBoxElements = 'html|body|head|meta|title|link|script|base|!--';
164
-        $allBoxLikeElements = '(?>' . $trueBoxElements . '|' . $functionalBoxElements . '|' . $usableBoxElements . '|' . $imagineBoxElements . ')';
164
+        $allBoxLikeElements = '(?>'.$trueBoxElements.'|'.$functionalBoxElements.'|'.$usableBoxElements.'|'.$imagineBoxElements.')';
165 165
         $esteticBoxLikeElements = '(?>html|head|body|meta name|title|div|table|h1|h2|h3|h4|h5|h6|p|form|pre|center|!--)';
166 166
         $structureBoxLikeElements = '(?>html|head|body|div|!--)';
167 167
 
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
             '/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/',
171 171
             $html,
172 172
             -1,
173
-            \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY
173
+            \PREG_SPLIT_DELIM_CAPTURE|\PREG_SPLIT_NO_EMPTY
174 174
         );
175 175
 
176 176
         if (false === $htmlArrayTemp) {
@@ -204,52 +204,52 @@  discard block
 block discarded – undo
204 204
             } elseif (2 == $this->formatType && ( // minimalistic line break
205 205
                 // this element has a line break before itself
206 206
                 preg_match(
207
-                    '/<' . $structureBoxLikeElements . '(.*)>/Usi',
207
+                    '/<'.$structureBoxLikeElements.'(.*)>/Usi',
208 208
                     $htmlArrayCurrent
209 209
                 ) || preg_match(
210
-                    '/<' . $structureBoxLikeElements . '(.*) \/>/Usi',
210
+                    '/<'.$structureBoxLikeElements.'(.*) \/>/Usi',
211 211
                     $htmlArrayCurrent
212 212
                 ) // one element before is a element that has a line break after
213 213
                 || preg_match(
214
-                    '/<\/' . $structureBoxLikeElements . '(.*)>/Usi',
214
+                    '/<\/'.$structureBoxLikeElements.'(.*)>/Usi',
215 215
                     $htmlArrayBefore
216 216
                 ) || '<!--' == substr(
217 217
                     $htmlArrayBefore,
218 218
                     0,
219 219
                     4
220
-                ) || preg_match('/<' . $structureBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
220
+                ) || preg_match('/<'.$structureBoxLikeElements.'(.*) \/>/Usi', $htmlArrayBefore))
221 221
             ) {
222 222
                 $newline = true;
223 223
             } elseif (3 == $this->formatType && ( // aestetic line break
224 224
                 // this element has a line break before itself
225 225
                 preg_match(
226
-                    '/<' . $esteticBoxLikeElements . '(.*)>/Usi',
226
+                    '/<'.$esteticBoxLikeElements.'(.*)>/Usi',
227 227
                     $htmlArrayCurrent
228 228
                 ) || preg_match(
229
-                    '/<' . $esteticBoxLikeElements . '(.*) \/>/Usi',
229
+                    '/<'.$esteticBoxLikeElements.'(.*) \/>/Usi',
230 230
                     $htmlArrayCurrent
231 231
                 ) // one element before is a element that has a line break after
232
-                || preg_match('/<\/' . $esteticBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
232
+                || preg_match('/<\/'.$esteticBoxLikeElements.'(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
233 233
                     $htmlArrayBefore,
234 234
                     0,
235 235
                     4
236
-                ) || preg_match('/<' . $esteticBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
236
+                ) || preg_match('/<'.$esteticBoxLikeElements.'(.*) \/>/Usi', $htmlArrayBefore))
237 237
             ) {
238 238
                 $newline = true;
239 239
             } elseif ($this->formatType >= 4 && ( // logical line break
240 240
                 // this element has a line break before itself
241 241
                 preg_match(
242
-                    '/<' . $allBoxLikeElements . '(.*)>/Usi',
242
+                    '/<'.$allBoxLikeElements.'(.*)>/Usi',
243 243
                     $htmlArrayCurrent
244 244
                 ) || preg_match(
245
-                    '/<' . $allBoxLikeElements . '(.*) \/>/Usi',
245
+                    '/<'.$allBoxLikeElements.'(.*) \/>/Usi',
246 246
                     $htmlArrayCurrent
247 247
                 ) // one element before is a element that has a line break after
248
-                || preg_match('/<\/' . $allBoxLikeElements . '(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
248
+                || preg_match('/<\/'.$allBoxLikeElements.'(.*)>/Usi', $htmlArrayBefore) || '<!--' == substr(
249 249
                     $htmlArrayBefore,
250 250
                     0,
251 251
                     4
252
-                ) || preg_match('/<' . $allBoxLikeElements . '(.*) \/>/Usi', $htmlArrayBefore))
252
+                ) || preg_match('/<'.$allBoxLikeElements.'(.*) \/>/Usi', $htmlArrayBefore))
253 253
             ) {
254 254
                 $newline = true;
255 255
             }
@@ -385,6 +385,6 @@  discard block
 block discarded – undo
385 385
      */
386 386
     public function includeHeaderComment(string &$html): void
387 387
     {
388
-        $html = preg_replace('/^(-->)$/m', "\n\t" . $this->headerComment . "\n$1", $html);
388
+        $html = preg_replace('/^(-->)$/m', "\n\t".$this->headerComment."\n$1", $html);
389 389
     }
390 390
 }
Please login to merge, or discard this patch.