Completed
Push — master ( 9392bc...c748b8 )
by Tim
25s queued 25s
created
Classes/Service/SvgStoreService.php 1 patch
Indentation   +189 added lines, -189 removed lines patch added patch discarded remove patch
@@ -14,193 +14,193 @@
 block discarded – undo
14 14
  */
15 15
 class SvgStoreService implements SingletonInterface
16 16
 {
17
-    /**
18
-     * SVG-Sprite storage DIR.
19
-     *
20
-     * @var string
21
-     */
22
-    protected $outputDir = '/typo3temp/assets/svg/'; // fallback
23
-
24
-    public function __construct()
25
-    {
26
-        //$this->styl = []; # https://stackoverflow.com/questions/39583880/external-svg-fails-to-apply-internal-css
27
-        //$this->defs = []; # https://bugs.chromium.org/p/chromium/issues/detail?id=751733#c14
28
-        $this->svgs = [];
29
-
30
-        $this->sitePath = \TYPO3\CMS\Core\Core\Environment::getPublicPath(); // [^/]$
31
-
32
-        if (isset($GLOBALS['TSFE']->config['config']['svgstore.']['outputDir']) && !empty($GLOBALS['TSFE']->config['config']['svgstore.']['outputDir'])) {
33
-            $this->outputDir = '/typo3temp/'.$GLOBALS['TSFE']->config['config']['svgstore.']['outputDir']; // TODO; please beautify
34
-        }
35
-
36
-        $this->connPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
37
-        $this->svgCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('svgstore');
38
-    }
39
-
40
-    public function process(string $html): string
41
-    {
42
-        $this->spritePath = $this->svgCache->get('spritePath');
43
-        $this->svgFileArr = $this->svgCache->get('svgFileArr');
44
-
45
-        if (empty($this->spritePath) && !$this->populateCache()) {
46
-            throw new \Exception('could not write file: '.$this->sitePath.$this->spritePath);
47
-        }
48
-
49
-        if (!file_exists($this->sitePath.$this->spritePath)) {
50
-            throw new \Exception('file does not exists: '.$this->sitePath.$this->spritePath);
51
-        }
52
-
53
-        if (!preg_match('/(?<head>.+?<\/head>)(?<body>.+)/s', $html, $html) && 5 == \count($html)) {
54
-            throw new \Exception('fix HTML!');
55
-        }
56
-
57
-        // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes
58
-        $html['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>/s', function (array $matches): string { // ^[/]
59
-            if (!isset($this->svgFileArr[$matches['src']])) { // check usage
60
-                return $matches[0];
61
-            }
62
-            $attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap)="[^"]*"/', '', $matches['pre'].$matches['post']); // cleanup
63
-
64
-            return sprintf('<svg%s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$matches['src']]['attr'], $attr, $this->spritePath, $this->convertFilePath($matches['src']));
65
-        }, $html['body']);
66
-
67
-        // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#attributes
68
-        $html['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function (array $matches): string { // ^[/]
69
-            if (!isset($this->svgFileArr[$matches['data']])) { // check usage
70
-                return $matches[0];
71
-            }
72
-            $attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $matches['pre'].$matches['post']); // cleanup
73
-
74
-            return sprintf('<svg%s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$matches['src']]['attr'], $attr, $this->spritePath, $this->convertFilePath($matches['data']));
75
-        }, $html['body']);
76
-
77
-        return $html['head'].$html['body'];
78
-    }
79
-
80
-    private function convertFilePath(string $path): string
81
-    {
82
-        return preg_replace('/.svg$|[^\w\-]/', '', str_replace('/', '-', ltrim($path, '/'))); // ^[^/]
83
-    }
84
-
85
-    private function addFileToSpriteArr(string $hash, string $path): ?array
86
-    {
87
-        if (1 === preg_match('/;base64/', $svg = file_get_contents($this->sitePath.$path))) { // noop!
88
-            return null;
89
-        }
90
-
91
-        if (1 === preg_match('/<(?:style|defs|url\()/', $svg)) {
92
-            return null; // check links @ __construct
93
-        }
94
-
95
-        $svg = preg_replace('/<\/svg>.*|xlink:|\s(?:(?:version|xmlns)|(?:[a-z\-]+\:[a-z\-]+))="[^"]*"/s', '', $svg); // clean !?: \s+(?<atr>[\w\-]+)=["\'](?<val>[^"\']+)["\']
96
-
97
-        //$svg = preg_replace('/((?:id|class)=")/', '$1'.$hash.'__', $svg); // extend  IDs
98
-        //$svg = preg_replace('/(href="|url\()#/', '$1#'.$hash.'__', $svg); // recover IDs
99
-
100
-        //$svg = preg_replace_callback('/<style[^>]*>(?<styl>.+?)<\/style>|<defs[^>]*>(?<defs>.+?)<\/defs>/s', function(array $matches) use($hash): string {
101
-        //    if(isset($matches['styl']))
102
-        //    {
103
-        //        $this->styl[] = preg_replace('/\s*(\.|#){1}(.+?)\s*\{/', '$1'.$hash.'__$2{', $matches['styl']); // patch CSS # https://mathiasbynens.be/notes/css-escapes
104
-        //    }
105
-        //    if(isset($matches['defs']))
106
-        //    {
107
-        //        $this->defs[] = trim($matches['defs']);
108
-        //    }
109
-        //    return '';
110
-        //}, $svg);
111
-
112
-        $this->svgs[] = preg_replace('/.*<svg((?:(?!id=)[^>])+)(?:id="[^"]*")?([^>]*>)/s', 'id="'.$this->convertFilePath($path).'"$1$2', $svg, 1); // change ID;
113
-
114
-        return preg_match('/\s+viewBox="\s*([+-]?[\d\.]+(?:\s+[+-]?[\d\.]+){3})\s*"/', $svg, $match) ? ['attr' => ' viewBox="'.preg_replace('/\s+/', ' ', $match[1]).'"', 'hash' => $hash] : null;
115
-    }
116
-
117
-    private function populateCache(): bool
118
-    {
119
-        $storageArr = $this->getStorageArrayFromDB();
120
-        $svgFileArr = $this->getSvgFilesArrayFromDB(array_keys($storageArr));
121
-
122
-        $this->svgFileArr = [];
123
-        foreach ($svgFileArr as $index => $row) {
124
-            if (!$this->svgFileArr[($row['path'] = '/'.$storageArr[$row['storage']].$row['identifier'])] = $this->addFileToSpriteArr($row['sha1'], $row['path'])) { // ^[/]
125
-                unset($this->svgFileArr[$row['path']]);
126
-            }
127
-        }
128
-
129
-        $svg = preg_replace_callback(
130
-            '/<use(?<pre>.*?)(?:xlink:)?href="(?<href>\/.+?\.svg)#[^"]+"(?<post>.*?)[\s\/]*>(?:<\/use>)?/s',
131
-            function (array $matches): string {
132
-                return sprintf('<use%s href="#%s"/>', $matches['pre'].$matches['post'], $this->convertFilePath($matches['href']));
133
-            },
134
-            '<svg xmlns="http://www.w3.org/2000/svg">'
135
-            //."\n<style>\n".implode("\n", $this->styl)."\n</style>"
136
-            //."\n<defs>\n".implode("\n", $this->defs)."\n</defs>"
137
-            ."\n<symbol ".implode("</symbol>\n<symbol ", $this->svgs)."</symbol>\n"
138
-            .'</svg>'
139
-        );
140
-
141
-        //unset($this->styl); // save MEM
142
-        //unset($this->defs); // save MEM
143
-        unset($this->svgs); // save MEM
144
-
145
-        if (\is_int($var = $GLOBALS['TSFE']->config['config']['sourceopt.']['formatHtml']) && 1 == $var) {
146
-            $svg = preg_replace('/[\n\r\t\v\0]|\s{2,}/', '', $svg);
147
-        }
148
-
149
-        $svg = preg_replace('/<([a-z]+)\s*(\/|>\s*<\/\1)>\s*/i', '', $svg); // remove emtpy
150
-        $svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify
151
-
152
-        if (!is_dir($this->sitePath.$this->outputDir)) {
153
-            GeneralUtility::mkdir_deep($this->sitePath.$this->outputDir);
154
-        }
155
-
156
-        $this->spritePath = $this->outputDir.hash('sha1', serialize($this->svgFileArr)).'.svg';
157
-        if (false === file_put_contents($this->sitePath.$this->spritePath, $svg)) {
158
-            return false;
159
-        }
160
-        unset($svg); // save MEM
161
-
162
-        $this->svgCache->set('svgFileArr', $this->svgFileArr);
163
-        $this->svgCache->set('spritePath', $this->spritePath);
164
-
165
-        return true;
166
-    }
167
-
168
-    private function getStorageArrayFromDB(): array
169
-    {
170
-        $storageResources = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class)->findAll();
171
-        foreach ($storageResources as $storage) {
172
-            if ('relative' == $storage->getConfiguration()['pathType']) {
173
-                $storageResources[$storage->getUid()] = rtrim($storage->getConfiguration()['basePath'], '/'); // [^/]$
174
-            }
175
-        }
176
-        unset($storageResources[0]); // keep!
177
-
178
-        return $storageResources;
179
-    }
180
-
181
-    private function getSvgFilesArrayFromDB(array $storageIds): array
182
-    {
183
-        return ($queryBuilder = $this->connPool->getQueryBuilderForTable('sys_file'))
184
-            ->select('sys_file.storage', 'sys_file.identifier', 'sys_file.sha1')
185
-            ->from('sys_file')
186
-            ->innerJoin(
187
-                'sys_file',
188
-                'sys_file_reference',
189
-                'sys_file_reference',
190
-                $queryBuilder->expr()->eq(
191
-                    'sys_file_reference.uid_local',
192
-                    $queryBuilder->quoteIdentifier('sys_file.uid')
193
-                )
194
-            )
195
-            ->where(
196
-                $queryBuilder->expr()->in('sys_file.storage', $queryBuilder->createNamedParameter($storageIds, \TYPO3\CMS\Core\Database\Connection::PARAM_INT_ARRAY)),
197
-                $queryBuilder->expr()->eq('sys_file.mime_type', $queryBuilder->createNamedParameter('image/svg+xml')),
198
-                $queryBuilder->expr()->lt('sys_file.size', $queryBuilder->createNamedParameter($GLOBALS['TSFE']->config['config']['svgstore.']['fileSize'])),
199
-            )
200
-            ->groupBy('sys_file.uid')
201
-            ->orderBy('sys_file.uid')
202
-            ->execute()
203
-            ->fetchAll() // TODO; use stdClass
204
-        ;
205
-    }
17
+	/**
18
+	 * SVG-Sprite storage DIR.
19
+	 *
20
+	 * @var string
21
+	 */
22
+	protected $outputDir = '/typo3temp/assets/svg/'; // fallback
23
+
24
+	public function __construct()
25
+	{
26
+		//$this->styl = []; # https://stackoverflow.com/questions/39583880/external-svg-fails-to-apply-internal-css
27
+		//$this->defs = []; # https://bugs.chromium.org/p/chromium/issues/detail?id=751733#c14
28
+		$this->svgs = [];
29
+
30
+		$this->sitePath = \TYPO3\CMS\Core\Core\Environment::getPublicPath(); // [^/]$
31
+
32
+		if (isset($GLOBALS['TSFE']->config['config']['svgstore.']['outputDir']) && !empty($GLOBALS['TSFE']->config['config']['svgstore.']['outputDir'])) {
33
+			$this->outputDir = '/typo3temp/'.$GLOBALS['TSFE']->config['config']['svgstore.']['outputDir']; // TODO; please beautify
34
+		}
35
+
36
+		$this->connPool = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class);
37
+		$this->svgCache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('svgstore');
38
+	}
39
+
40
+	public function process(string $html): string
41
+	{
42
+		$this->spritePath = $this->svgCache->get('spritePath');
43
+		$this->svgFileArr = $this->svgCache->get('svgFileArr');
44
+
45
+		if (empty($this->spritePath) && !$this->populateCache()) {
46
+			throw new \Exception('could not write file: '.$this->sitePath.$this->spritePath);
47
+		}
48
+
49
+		if (!file_exists($this->sitePath.$this->spritePath)) {
50
+			throw new \Exception('file does not exists: '.$this->sitePath.$this->spritePath);
51
+		}
52
+
53
+		if (!preg_match('/(?<head>.+?<\/head>)(?<body>.+)/s', $html, $html) && 5 == \count($html)) {
54
+			throw new \Exception('fix HTML!');
55
+		}
56
+
57
+		// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes
58
+		$html['body'] = preg_replace_callback('/<img(?<pre>[^>]*)src="(?<src>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>/s', function (array $matches): string { // ^[/]
59
+			if (!isset($this->svgFileArr[$matches['src']])) { // check usage
60
+				return $matches[0];
61
+			}
62
+			$attr = preg_replace('/\s(?:alt|ismap|loading|title|sizes|srcset|usemap)="[^"]*"/', '', $matches['pre'].$matches['post']); // cleanup
63
+
64
+			return sprintf('<svg%s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$matches['src']]['attr'], $attr, $this->spritePath, $this->convertFilePath($matches['src']));
65
+		}, $html['body']);
66
+
67
+		// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#attributes
68
+		$html['body'] = preg_replace_callback('/<object(?<pre>[^>]*)data="(?<data>\/[^"]+\.svg)"(?<post>[^>]*?)[\s\/]*>(?:<\/object>)/s', function (array $matches): string { // ^[/]
69
+			if (!isset($this->svgFileArr[$matches['data']])) { // check usage
70
+				return $matches[0];
71
+			}
72
+			$attr = preg_replace('/\s(?:form|name|type|usemap)="[^"]*"/', '', $matches['pre'].$matches['post']); // cleanup
73
+
74
+			return sprintf('<svg%s %s><use href="%s#%s"/></svg>', $this->svgFileArr[$matches['src']]['attr'], $attr, $this->spritePath, $this->convertFilePath($matches['data']));
75
+		}, $html['body']);
76
+
77
+		return $html['head'].$html['body'];
78
+	}
79
+
80
+	private function convertFilePath(string $path): string
81
+	{
82
+		return preg_replace('/.svg$|[^\w\-]/', '', str_replace('/', '-', ltrim($path, '/'))); // ^[^/]
83
+	}
84
+
85
+	private function addFileToSpriteArr(string $hash, string $path): ?array
86
+	{
87
+		if (1 === preg_match('/;base64/', $svg = file_get_contents($this->sitePath.$path))) { // noop!
88
+			return null;
89
+		}
90
+
91
+		if (1 === preg_match('/<(?:style|defs|url\()/', $svg)) {
92
+			return null; // check links @ __construct
93
+		}
94
+
95
+		$svg = preg_replace('/<\/svg>.*|xlink:|\s(?:(?:version|xmlns)|(?:[a-z\-]+\:[a-z\-]+))="[^"]*"/s', '', $svg); // clean !?: \s+(?<atr>[\w\-]+)=["\'](?<val>[^"\']+)["\']
96
+
97
+		//$svg = preg_replace('/((?:id|class)=")/', '$1'.$hash.'__', $svg); // extend  IDs
98
+		//$svg = preg_replace('/(href="|url\()#/', '$1#'.$hash.'__', $svg); // recover IDs
99
+
100
+		//$svg = preg_replace_callback('/<style[^>]*>(?<styl>.+?)<\/style>|<defs[^>]*>(?<defs>.+?)<\/defs>/s', function(array $matches) use($hash): string {
101
+		//    if(isset($matches['styl']))
102
+		//    {
103
+		//        $this->styl[] = preg_replace('/\s*(\.|#){1}(.+?)\s*\{/', '$1'.$hash.'__$2{', $matches['styl']); // patch CSS # https://mathiasbynens.be/notes/css-escapes
104
+		//    }
105
+		//    if(isset($matches['defs']))
106
+		//    {
107
+		//        $this->defs[] = trim($matches['defs']);
108
+		//    }
109
+		//    return '';
110
+		//}, $svg);
111
+
112
+		$this->svgs[] = preg_replace('/.*<svg((?:(?!id=)[^>])+)(?:id="[^"]*")?([^>]*>)/s', 'id="'.$this->convertFilePath($path).'"$1$2', $svg, 1); // change ID;
113
+
114
+		return preg_match('/\s+viewBox="\s*([+-]?[\d\.]+(?:\s+[+-]?[\d\.]+){3})\s*"/', $svg, $match) ? ['attr' => ' viewBox="'.preg_replace('/\s+/', ' ', $match[1]).'"', 'hash' => $hash] : null;
115
+	}
116
+
117
+	private function populateCache(): bool
118
+	{
119
+		$storageArr = $this->getStorageArrayFromDB();
120
+		$svgFileArr = $this->getSvgFilesArrayFromDB(array_keys($storageArr));
121
+
122
+		$this->svgFileArr = [];
123
+		foreach ($svgFileArr as $index => $row) {
124
+			if (!$this->svgFileArr[($row['path'] = '/'.$storageArr[$row['storage']].$row['identifier'])] = $this->addFileToSpriteArr($row['sha1'], $row['path'])) { // ^[/]
125
+				unset($this->svgFileArr[$row['path']]);
126
+			}
127
+		}
128
+
129
+		$svg = preg_replace_callback(
130
+			'/<use(?<pre>.*?)(?:xlink:)?href="(?<href>\/.+?\.svg)#[^"]+"(?<post>.*?)[\s\/]*>(?:<\/use>)?/s',
131
+			function (array $matches): string {
132
+				return sprintf('<use%s href="#%s"/>', $matches['pre'].$matches['post'], $this->convertFilePath($matches['href']));
133
+			},
134
+			'<svg xmlns="http://www.w3.org/2000/svg">'
135
+			//."\n<style>\n".implode("\n", $this->styl)."\n</style>"
136
+			//."\n<defs>\n".implode("\n", $this->defs)."\n</defs>"
137
+			."\n<symbol ".implode("</symbol>\n<symbol ", $this->svgs)."</symbol>\n"
138
+			.'</svg>'
139
+		);
140
+
141
+		//unset($this->styl); // save MEM
142
+		//unset($this->defs); // save MEM
143
+		unset($this->svgs); // save MEM
144
+
145
+		if (\is_int($var = $GLOBALS['TSFE']->config['config']['sourceopt.']['formatHtml']) && 1 == $var) {
146
+			$svg = preg_replace('/[\n\r\t\v\0]|\s{2,}/', '', $svg);
147
+		}
148
+
149
+		$svg = preg_replace('/<([a-z]+)\s*(\/|>\s*<\/\1)>\s*/i', '', $svg); // remove emtpy
150
+		$svg = preg_replace('/<((circle|ellipse|line|path|polygon|polyline|rect|stop|use)\s[^>]+?)\s*>\s*<\/\2>/', '<$1/>', $svg); // shorten/minify
151
+
152
+		if (!is_dir($this->sitePath.$this->outputDir)) {
153
+			GeneralUtility::mkdir_deep($this->sitePath.$this->outputDir);
154
+		}
155
+
156
+		$this->spritePath = $this->outputDir.hash('sha1', serialize($this->svgFileArr)).'.svg';
157
+		if (false === file_put_contents($this->sitePath.$this->spritePath, $svg)) {
158
+			return false;
159
+		}
160
+		unset($svg); // save MEM
161
+
162
+		$this->svgCache->set('svgFileArr', $this->svgFileArr);
163
+		$this->svgCache->set('spritePath', $this->spritePath);
164
+
165
+		return true;
166
+	}
167
+
168
+	private function getStorageArrayFromDB(): array
169
+	{
170
+		$storageResources = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class)->findAll();
171
+		foreach ($storageResources as $storage) {
172
+			if ('relative' == $storage->getConfiguration()['pathType']) {
173
+				$storageResources[$storage->getUid()] = rtrim($storage->getConfiguration()['basePath'], '/'); // [^/]$
174
+			}
175
+		}
176
+		unset($storageResources[0]); // keep!
177
+
178
+		return $storageResources;
179
+	}
180
+
181
+	private function getSvgFilesArrayFromDB(array $storageIds): array
182
+	{
183
+		return ($queryBuilder = $this->connPool->getQueryBuilderForTable('sys_file'))
184
+			->select('sys_file.storage', 'sys_file.identifier', 'sys_file.sha1')
185
+			->from('sys_file')
186
+			->innerJoin(
187
+				'sys_file',
188
+				'sys_file_reference',
189
+				'sys_file_reference',
190
+				$queryBuilder->expr()->eq(
191
+					'sys_file_reference.uid_local',
192
+					$queryBuilder->quoteIdentifier('sys_file.uid')
193
+				)
194
+			)
195
+			->where(
196
+				$queryBuilder->expr()->in('sys_file.storage', $queryBuilder->createNamedParameter($storageIds, \TYPO3\CMS\Core\Database\Connection::PARAM_INT_ARRAY)),
197
+				$queryBuilder->expr()->eq('sys_file.mime_type', $queryBuilder->createNamedParameter('image/svg+xml')),
198
+				$queryBuilder->expr()->lt('sys_file.size', $queryBuilder->createNamedParameter($GLOBALS['TSFE']->config['config']['svgstore.']['fileSize'])),
199
+			)
200
+			->groupBy('sys_file.uid')
201
+			->orderBy('sys_file.uid')
202
+			->execute()
203
+			->fetchAll() // TODO; use stdClass
204
+		;
205
+	}
206 206
 }
Please login to merge, or discard this patch.