diff --git a/Classes/Manipulation/RemoveComments.php b/Classes/Manipulation/RemoveComments.php index 3187419..6cb085b 100644 --- a/Classes/Manipulation/RemoveComments.php +++ b/Classes/Manipulation/RemoveComments.php @@ -19,22 +19,20 @@ class RemoveComments implements ManipulationInterface */ public function manipulate(string $html, array $configuration = []): string { - if (isset($configuration['keep.'])) { + if (isset($configuration['keep.'])) {// DEPRECATED $this->whiteListCommentsPatterns = $configuration['keep.']; } - // match all comments, styles and scripts - $matches = []; - preg_match_all( - '/(?s)(()|(<[ \n\r]*style[^>]*>.*?<[ \n\r]*\/style[^>]*>)|(<[ \n\r]*script[^>]*>.*?<[ \n\r]*\/script[^>]*>))/im', - $html, - $matches + return preg_replace_callback( + '/\s*/', + function (array $match): string { + if ($this->keepComment($match[1])) { + return $match[0]; + } + return ''; + }, + $html ); - foreach ($matches[0] as $tag) { - if (false === $this->keepComment($tag)) { - $html = str_replace($tag, '', $html); - } - } return $html; } @@ -42,25 +40,17 @@ public function manipulate(string $html, array $configuration = []): string /** * Check if a comment is defined to be kept in a pattern whiteListOfComments. */ - protected function keepComment(string $commentHtml): bool + protected function keepComment(string $comment): bool { - // if not even a comment, skip this - if (!preg_match('/^\<\!\-\-(.*?)\-\-\>$/usi', $commentHtml)) { - return true; - } - // if not defined in white list if (!empty($this->whiteListCommentsPatterns)) { - $commentHtml = str_replace('', '', $commentHtml); - $commentHtml = trim($commentHtml); + $comment = trim($comment); foreach ($this->whiteListCommentsPatterns as $pattern) { - if (!empty($pattern) && preg_match($pattern, $commentHtml)) { + if (preg_match($pattern, $comment)) { return true; } } } - return false; } } diff --git a/Classes/Manipulation/RemoveGenerator.php b/Classes/Manipulation/RemoveGenerator.php index 9a108a8..714a05e 100644 --- a/Classes/Manipulation/RemoveGenerator.php +++ b/Classes/Manipulation/RemoveGenerator.php @@ -16,6 +16,6 @@ public function manipulate(string $html, array $configuration = []): string { $regex = ']+>'; - return (string) preg_replace('/' . $regex . '/is', '', $html); + return (string) preg_replace('/' . $regex . '/i', '', $html); } } diff --git a/Classes/Middleware/AbstractMiddleware.php b/Classes/Middleware/AbstractMiddleware.php index 158afd7..39de605 100644 --- a/Classes/Middleware/AbstractMiddleware.php +++ b/Classes/Middleware/AbstractMiddleware.php @@ -10,9 +10,24 @@ use TYPO3\CMS\Core\Http\NullResponse; use TYPO3\CMS\Core\Http\Stream; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; abstract class AbstractMiddleware implements MiddlewareInterface { + /** @var ConfigurationManagerInterface */ + protected $configurationManager; + + /** + * Injects the Configuration Manager and loads the settings + * + * @param ConfigurationManagerInterface $configurationManager An instance of the Configuration Manager + */ + public function injectConfigurationManager( + ConfigurationManagerInterface $configurationManager + ): void { + $this->configurationManager = $configurationManager; + } + protected function responseIsAlterable(ResponseInterface $response): bool { if ($response instanceof NullResponse) { @@ -34,6 +49,19 @@ protected function responseIsAlterable(ResponseInterface $response): bool return true; } + protected function getConfiguration(ServerRequestInterface $request): array + { + if (isset($GLOBALS['TSFE']->config['config']['sourceopt.'])) {// DEPRECATED + return $GLOBALS['TSFE']->config['config']['sourceopt.']; + } + + if ($request->getAttribute('site')->getSettings()->get('sourceopt')) { + return $request->getAttribute('site')->getSettings()->get('sourceopt'); + } + + throw new \Exception('SiteConfig:SourceOpt ist missing'); + } + protected function getStringStream(string $content): StreamInterface { $body = new Stream('php://temp', 'rw'); diff --git a/Classes/Resource/SvgFileRepository.php b/Classes/Resource/SvgFileRepository.php index 0b06144..7bc4a0e 100644 --- a/Classes/Resource/SvgFileRepository.php +++ b/Classes/Resource/SvgFileRepository.php @@ -23,6 +23,7 @@ public function findAllByStorageUids(array $storageUids): \Traversable return $queryBuilder ->select('sys_file.storage', 'sys_file.identifier', 'sys_file.sha1') + ->addSelectLiteral('COUNT(*) AS ' . $queryBuilder->quoteIdentifier('count')) ->from('sys_file') ->innerJoin( 'sys_file', diff --git a/Classes/Service/CleanHtmlService.php b/Classes/Service/CleanHtmlService.php index 9722fa0..ffe5ca5 100644 --- a/Classes/Service/CleanHtmlService.php +++ b/Classes/Service/CleanHtmlService.php @@ -56,11 +56,14 @@ public function setVariables(array $config): void $this->headerComment = $config['headerComment']; } - if (isset($config['formatHtml']) && is_numeric($config['formatHtml'])) { + if (isset($config['formatHtml']) && is_numeric($config['formatHtml'])) {// DEPRECATED $this->formatType = (int) $config['formatHtml']; } + if (isset($config['formatHtml.']['enabled']) && is_numeric($config['formatHtml.']['enabled']) && 0 <= $config['formatHtml.']['enabled']) { + $this->formatType = (int) $config['formatHtml.']['enabled']; + } - if (isset($config['formatHtml.']['tabSize']) && is_numeric($config['formatHtml.']['tabSize'])) { + if (isset($config['formatHtml.']['tabSize']) && is_numeric($config['formatHtml.']['tabSize']) && 0 < $config['formatHtml.']['tabSize']) { $this->tab = str_pad('', (int) $config['formatHtml.']['tabSize'], ' '); } @@ -68,7 +71,7 @@ public function setVariables(array $config): void $this->debugComment = (bool) $config['formatHtml.']['debugComment']; } - if (isset($config['dropEmptySpaceChar']) && (bool) $config['dropEmptySpaceChar']) { + if (isset($config['dropEmptySpaceChar']) && (bool) $config['dropEmptySpaceChar']) {// DEPRECATED ?! $this->emptySpaceChar = ''; } } @@ -136,7 +139,7 @@ public function clean(string $html, array $config = []): string ); } - if ($this->formatType > 0) { + if ($this->formatType) { $html = $this->formatHtml($html); } diff --git a/Classes/Service/SvgStoreService.php b/Classes/Service/SvgStoreService.php index cb6704a..f3a795d 100644 --- a/Classes/Service/SvgStoreService.php +++ b/Classes/Service/SvgStoreService.php @@ -201,6 +201,8 @@ private function populateCache(): bool $file['path'] = '/' . $storageArr[$file['storage']] . $file['identifier']; // ^[/] $file['defs'] = $this->addFileToSpriteArr($file['sha1'], $file['path']); +// TODO ; if(--$file['count']) INLINE @
+ if (null !== $file['defs']) { $this->svgFileArr[$file['path']] = $file['defs']; } @@ -232,8 +234,8 @@ function (array $match): string { $svg = preg_replace('/\s{2,}/', ' ', $svg); // shrink whitespace } - $svg = preg_replace('/<([a-z\-]+)\s*(\/|>\s*<\/\1)>\s*|\s+(?=\/>)/i', '', $svg); // remove emtpy TAGs & shorten endings - $svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify TAG syntax + $svg = preg_replace('/<([a-z]+)\s*(\/|>\s*<\/\1)>\s*|\s+(?=\/>)/i', '', $svg); // remove emtpy TAGs & shorten endings + $svg = preg_replace('/<(([a-z]+)\s[^>]+?)\s*>\s*<\/\2>/i', '<$1/>', $svg); // shorten/minify TAG syntax if (!is_dir($this->sitePath . $this->outputDir)) { GeneralUtility::mkdir_deep($this->sitePath . $this->outputDir); diff --git a/Configuration/Sets/SourceOpt/settings.definitions.yaml b/Configuration/Sets/SourceOpt/settings.definitions.yaml index 0f9b2da..6a5cbca 100644 --- a/Configuration/Sets/SourceOpt/settings.definitions.yaml +++ b/Configuration/Sets/SourceOpt/settings.definitions.yaml @@ -1,6 +1,6 @@ categories: sourceopt: - label: 'SourceOpt' + label: 'SourceOpt[imization]' sourceopt.cleaner: label: 'Cleaner' parent: sourceopt @@ -13,58 +13,70 @@ categories: settings: sourceopt.enabled: + category: sourceopt.cleaner label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.enabled - default: true type: bool - category: sourceopt.cleaner + default: true sourceopt.removeGenerator: + category: sourceopt.cleaner label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.removeGenerator - default: true type: bool - category: sourceopt.cleaner + default: true sourceopt.removeComments: + category: sourceopt.cleaner label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.removeComments - default: true type: bool + default: true + sourceopt.keepComments: category: sourceopt.cleaner - sourceopt.removeComments.keep.10: - label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.removeCommentsKeep10 - default: '/^TYPO3SEARCH_/usi' - type: string - category: sourceopt.cleaner + label: 'Spare these listed comments [RegEx]' + type: stringlist + default: + - '/^TYPO3SEARCH_/usi' sourceopt.headerComment: - label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.headerComment - default: '' - type: string category: sourceopt.cleaner - sourceopt.formatHtml: - label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.formatHtml - default: '4' + label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.headerComment type: string + default: '' + sourceopt.formatHtml.enabled: category: sourceopt.formater - # enum: - # '0': Disable - # '1': No line break at all (code in one line) - # '2': Minimalistic line breaks (structure defining box-elements) - # '3': Aesthetic line breaks (important box-elements) - # '4': Logic line breaks (all box-elements) + label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.formatHtml + type: bool + default: true sourceopt.formatHtml.tabSize: + category: sourceopt.formater label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.formatHtmlTabSize - default: '' - type: string + type: int + default: 4 + sourceopt.formatHtml.indentation: category: sourceopt.formater + label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.formatHtmlIndentation + type: int + default: 2 sourceopt.formatHtml.debugComment: + category: sourceopt.formater label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.formatHtmlDebugComment default: false type: bool - category: sourceopt.formater svgstore.enabled: + category: sourceopt.svgstore label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:svgstore.enabled - default: true type: bool + default: true + svgstore.renderMode: category: sourceopt.svgstore + label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:svgstore.renderMode + type: select + default: symbol + items: + - label: 'Reference all SVGs (default)' + value: symbol + - label: 'Inline single-use SVGs (optimize traffic)' + value: distinct + - label: 'Inline all SVGs at HTML (not recommended)' + value: inline svgstore.fileSize: + category: sourceopt.svgstore label: LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:svgstore.fileSize - default: 50000 type: int - category: sourceopt.svgstore + default: 50000 diff --git a/Configuration/Sets/SourceOpt/setup.typoscript b/Configuration/Sets/SourceOpt/setup.typoscript deleted file mode 100644 index 540cfca..0000000 --- a/Configuration/Sets/SourceOpt/setup.typoscript +++ /dev/null @@ -1 +0,0 @@ -@import 'EXT:sourceopt/Configuration/TypoScript/setup.typoscript' diff --git a/Configuration/TypoScript/constants.typoscript b/Configuration/TypoScript/constants.typoscript index 2bbdb11..62944cf 100644 --- a/Configuration/TypoScript/constants.typoscript +++ b/Configuration/TypoScript/constants.typoscript @@ -7,9 +7,6 @@ sourceopt.removeGenerator = 1 # cat=plugin.sourceopt/enable/50; type=boolean; label=LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.removeComments sourceopt.removeComments = 1 -# cat=plugin.sourceopt/typo/55; type=string; label=LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.removeCommentsKeep10 -sourceopt.removeComments.keep.10 = /^TYPO3SEARCH_/usi - # cat=plugin.sourceopt/typo/70; type=string; label=LLL:EXT:sourceopt/Resources/Private/Language/locallang.xlf:sourceopt.headerComment sourceopt.headerComment = diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index 3ea2ad4..6b6bc4f 100644 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -13,6 +13,9 @@ config.sourceopt { removeGenerator = {$sourceopt.removeGenerator} removeComments = {$sourceopt.removeComments} removeComments.keep { + # for typo-search + 10 = /^TYPO3SEARCH_/usi + # TYPO3 specific comment 87 = /This website is powered by TYPO3/usi @@ -32,9 +35,6 @@ config.sourceopt { # Google pagespeed 104 = /^gps(e|s)/usi - - # for typo-search - 10 = {$sourceopt.removeComments.keep.10} } } config.svgstore { diff --git a/README.md b/README.md index 48b9bd8..db66a45 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Version -#### >= 5.2.5 +#### >= 5.3 https://github.com/lochmueller/sourceopt/blob/173f7bd2a44b546844961ced1f0831371badd620/composer.json#L8-L9 #### <= 5.2.0 (legacy) @@ -59,8 +59,8 @@ Note: The following features are executed in reverse order | sourceopt.removeComments.keep.10 | string | Spare TYPO3SEARCH-Comments from removal | /^TYPO3SEARCH_/usi | | sourceopt.headerComment | string | Your additional (appended) header comment | `[empty]` | | sourceopt.formatHtml | integer | Formats the code beautiful and easy readable. New lines and tabs are used in the usual way of illustrating the structure of an XML code.
**Options**https://github.com/lochmueller/sourceopt/blob/2346673ee51d2b64308e1ddb1433cea2f37eafcb/Classes/Service/CleanHtmlService.php#L156-L161
| 4 | -| sourceopt.formatHtml.tabSize | integer | Defines the size of the tabs used for formating. If blank one tab is used. If a number is specified the specified number of blank spaces is used instead. This will only work together with `formatHtml` | `[empty]` | -| sourceopt.formatHtml.debugComment | boolean | Includes a comment at the end of the html source code that points the numbers of open html tags. This will only work together with `formatHtml` | `[empty]` +| sourceopt.formatHtml.tabSize | integer | Defines the amount of spaces used for formating. If blank one tab is used. If a number is specified the specified number of blank spaces is used instead. This will only work together with `formatHtml` | 0 | +| sourceopt.formatHtml.debugComment | boolean | Includes a comment at the end of the HTML source code that points the numbers of open html tags. This will only work together with `formatHtml` | 0 ### RegEx Replace diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 1d4a212..c28ae89 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -43,6 +43,9 @@ Formatting Tab-Size: Spezify number of blank-space for formatting. If empty a tab will be used. + + Formatting `count`: Spezify number of blank-space for indentation. If empty a tab will be used. + Show debugging comment: A comment at the end of the html code that points out the number of open html tags. diff --git a/Tests/UnitTests.xml b/Tests/UnitTests.xml index 2595430..c971005 100644 --- a/Tests/UnitTests.xml +++ b/Tests/UnitTests.xml @@ -12,11 +12,11 @@ stopOnIncomplete="false" stopOnSkipped="false" verbose="false" -> + > Unit/ - ./Unit/AbstractUnitTest.php + ./Unit/AbstractUnitTest.php diff --git a/composer.json b/composer.json index e80e5fe..9202374 100644 --- a/composer.json +++ b/composer.json @@ -13,11 +13,6 @@ "HTML\\Sourceopt\\": "Classes/" } }, - "replace": { - "maxserv/replacecontent": "*", - "typo3-ter/replacecontent": "*", - "typo3-ter/sourceopt": "self.version" - }, "require-dev": { "typo3/testing-framework": "^8.2", "friendsofphp/php-cs-fixer": "^3.3", @@ -29,6 +24,15 @@ "TYPO3\\CMS\\Core\\Tests\\": ".Build/vendor/typo3/cms/typo3/sysext/core/Tests/" } }, + "suggest": { + "lochmueller/staticfilecache": "Ultra-fast TYPO3 caching via static HTML file delivery", + "webcoast/deferred-image-processing": "Faster TYPO3 processing via on-demand image generation" + }, + "replace": { + "maxserv/replacecontent": "*", + "typo3-ter/replacecontent": "*", + "typo3-ter/sourceopt": "self.version" + }, "config": { "vendor-dir": ".Build/vendor", "bin-dir": ".Build/bin",