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