1 | <?php |
||
2 | |||
3 | /* |
||
4 | * @copyright 2014 Mautic Contributors. All rights reserved |
||
5 | * @author Mautic |
||
6 | * |
||
7 | * @link http://mautic.org |
||
8 | * |
||
9 | * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html |
||
10 | */ |
||
11 | |||
12 | namespace Mautic\CoreBundle\Helper; |
||
13 | |||
14 | use Symfony\Component\Finder\Finder; |
||
15 | |||
16 | /** |
||
17 | * Class AssetGenerationHelper. |
||
18 | */ |
||
19 | class AssetGenerationHelper |
||
20 | { |
||
21 | /** |
||
22 | * @var BundleHelper |
||
23 | */ |
||
24 | private $bundleHelper; |
||
25 | |||
26 | /** |
||
27 | * @var PathsHelper |
||
28 | */ |
||
29 | private $pathsHelper; |
||
30 | |||
31 | /** |
||
32 | * @var string |
||
33 | */ |
||
34 | private $version; |
||
35 | |||
36 | /** |
||
37 | * AssetGenerationHelper constructor. |
||
38 | */ |
||
39 | public function __construct(CoreParametersHelper $coreParametersHelper, BundleHelper $bundleHelper, PathsHelper $pathsHelper, AppVersion $version) |
||
40 | { |
||
41 | $this->bundleHelper = $bundleHelper; |
||
42 | $this->pathsHelper = $pathsHelper; |
||
43 | $this->version = substr(hash('sha1', $coreParametersHelper->get('secret_key').$version->getVersion()), 0, 8); |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * Generates and returns assets. |
||
48 | * |
||
49 | * @param bool $forceRegeneration |
||
50 | * |
||
51 | * @return array |
||
52 | */ |
||
53 | public function getAssets($forceRegeneration = false) |
||
54 | { |
||
55 | static $assets = []; |
||
56 | |||
57 | if (empty($assets)) { |
||
58 | $loadAll = true; |
||
59 | $env = ($forceRegeneration) ? 'prod' : MAUTIC_ENV; |
||
60 | $rootPath = $this->pathsHelper->getSystemPath('assets_root'); |
||
61 | $assetsPath = $this->pathsHelper->getSystemPath('assets'); |
||
62 | |||
63 | $assetsFullPath = "$rootPath/$assetsPath"; |
||
64 | if ('prod' == $env) { |
||
65 | $loadAll = false; //by default, loading should not be required |
||
66 | |||
67 | //check for libraries and app files and generate them if they don't exist if in prod environment |
||
68 | $prodFiles = [ |
||
69 | 'css/libraries.css', |
||
70 | 'css/app.css', |
||
71 | 'js/libraries.js', |
||
72 | 'js/app.js', |
||
73 | ]; |
||
74 | |||
75 | foreach ($prodFiles as $file) { |
||
76 | if (!file_exists("$assetsFullPath/$file")) { |
||
77 | $loadAll = true; //it's missing so compile it |
||
78 | break; |
||
79 | } |
||
80 | } |
||
81 | } |
||
82 | |||
83 | if ($loadAll || $forceRegeneration) { |
||
84 | if ('prod' == $env) { |
||
85 | ini_set('max_execution_time', 300); |
||
86 | |||
87 | $inProgressFile = "$assetsFullPath/generation_in_progress.txt"; |
||
88 | |||
89 | if (!$forceRegeneration) { |
||
90 | while (file_exists($inProgressFile)) { |
||
91 | //dummy loop to prevent conflicts if one process is actively regenerating assets |
||
92 | } |
||
93 | } |
||
94 | file_put_contents($inProgressFile, date('r')); |
||
95 | } |
||
96 | |||
97 | $modifiedLast = []; |
||
98 | |||
99 | //get a list of all core asset files |
||
100 | $bundles = $this->bundleHelper->getMauticBundles(); |
||
101 | |||
102 | $fileTypes = ['css', 'js']; |
||
103 | foreach ($bundles as $bundle) { |
||
104 | foreach ($fileTypes as $ft) { |
||
105 | if (!isset($modifiedLast[$ft])) { |
||
106 | $modifiedLast[$ft] = []; |
||
107 | } |
||
108 | $dir = "{$bundle['directory']}/Assets/$ft"; |
||
109 | if (file_exists($dir)) { |
||
110 | $modifiedLast[$ft] = array_merge($modifiedLast[$ft], $this->findAssets($dir, $ft, $env, $assets)); |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | $modifiedLast = array_merge($modifiedLast, $this->findOverrides($env, $assets)); |
||
115 | |||
116 | //combine the files into their corresponding name and put in the root media folder |
||
117 | if ('prod' == $env) { |
||
118 | $checkPaths = [ |
||
119 | $assetsFullPath, |
||
120 | "$assetsFullPath/css", |
||
121 | "$assetsFullPath/js", |
||
122 | ]; |
||
123 | array_walk($checkPaths, function ($path) { |
||
124 | if (!file_exists($path)) { |
||
125 | mkdir($path); |
||
126 | } |
||
127 | }); |
||
128 | |||
129 | $useMinify = class_exists('\Minify'); |
||
130 | |||
131 | foreach ($assets as $type => $groups) { |
||
132 | foreach ($groups as $group => $files) { |
||
133 | $assetFile = "$assetsFullPath/$type/$group.$type"; |
||
134 | |||
135 | //only refresh if a change has occurred |
||
136 | $modified = ($forceRegeneration || !file_exists($assetFile)) ? true : filemtime($assetFile) < $modifiedLast[$type][$group]; |
||
137 | |||
138 | if ($modified) { |
||
139 | if (file_exists($assetFile)) { |
||
140 | //delete it |
||
141 | unlink($assetFile); |
||
142 | } |
||
143 | |||
144 | if ('css' == $type) { |
||
145 | $out = fopen($assetFile, 'w'); |
||
146 | |||
147 | foreach ($files as $relPath => $details) { |
||
148 | $cssRel = '../../'.dirname($relPath).'/'; |
||
149 | if ($useMinify) { |
||
150 | $content = \Minify::combine([$details['fullPath']], [ |
||
151 | 'rewriteCssUris' => false, |
||
152 | 'minifierOptions' => [ |
||
153 | 'text/css' => [ |
||
154 | 'currentDir' => '', |
||
155 | 'prependRelativePath' => $cssRel, |
||
156 | ], |
||
157 | ], |
||
158 | ]); |
||
159 | } else { |
||
160 | $content = file_get_contents($details['fullPath']); |
||
161 | $search = '#url\((?!\s*([\'"]?(((?:https?:)?//)|(?:data\:?:))))\s*([\'"])?#'; |
||
162 | $replace = "url($4{$cssRel}"; |
||
163 | $content = preg_replace($search, $replace, $content); |
||
164 | } |
||
165 | |||
166 | fwrite($out, $content); |
||
167 | } |
||
168 | |||
169 | fclose($out); |
||
170 | } else { |
||
171 | array_walk($files, function (&$file) { |
||
172 | $file = $file['fullPath']; |
||
173 | }); |
||
174 | file_put_contents($assetFile, \Minify::combine($files)); |
||
175 | } |
||
176 | } |
||
177 | } |
||
178 | } |
||
179 | |||
180 | unlink($inProgressFile); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
181 | } |
||
182 | } |
||
183 | |||
184 | if ('prod' == $env) { |
||
185 | //return prod generated assets |
||
186 | $assets = [ |
||
187 | 'css' => [ |
||
188 | "{$assetsPath}/css/libraries.css?v{$this->version}", |
||
189 | "{$assetsPath}/css/app.css?v{$this->version}", |
||
190 | ], |
||
191 | 'js' => [ |
||
192 | "{$assetsPath}/js/libraries.js?v{$this->version}", |
||
193 | "{$assetsPath}/js/app.js?v{$this->version}", |
||
194 | ], |
||
195 | ]; |
||
196 | } else { |
||
197 | foreach ($assets as &$typeAssets) { |
||
198 | $typeAssets = array_keys($typeAssets); |
||
199 | } |
||
200 | } |
||
201 | } |
||
202 | |||
203 | return $assets; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Finds directory assets. |
||
208 | * |
||
209 | * @param string $dir |
||
210 | * @param string $ext |
||
211 | * @param string $env |
||
212 | * @param array $assets |
||
213 | * |
||
214 | * @return array |
||
215 | */ |
||
216 | protected function findAssets($dir, $ext, $env, &$assets) |
||
217 | { |
||
218 | $rootPath = str_replace('\\', '/', $this->pathsHelper->getSystemPath('assets_root').'/'); |
||
219 | $directories = new Finder(); |
||
220 | $directories->directories()->exclude('*less')->depth('0')->ignoreDotFiles(true)->in($dir); |
||
221 | |||
222 | $modifiedLast = []; |
||
223 | |||
224 | if (count($directories)) { |
||
225 | foreach ($directories as $directory) { |
||
226 | $group = $directory->getBasename(); |
||
227 | |||
228 | // Only auto load directories app or libraries |
||
229 | if (!in_array($group, ['app', 'libraries'])) { |
||
230 | continue; |
||
231 | } |
||
232 | |||
233 | $files = new Finder(); |
||
234 | $thisDirectory = str_replace('\\', '/', $directory->getRealPath()); |
||
235 | $files->files()->depth('0')->name('*.'.$ext)->in($thisDirectory); |
||
236 | |||
237 | $sort = function (\SplFileInfo $a, \SplFileInfo $b) { |
||
238 | return strnatcmp($a->getRealpath(), $b->getRealpath()); |
||
239 | }; |
||
240 | $files->sort($sort); |
||
241 | |||
242 | foreach ($files as $file) { |
||
243 | $fullPath = $file->getPathname(); |
||
244 | $relPath = str_replace($rootPath, '', $file->getPathname()); |
||
245 | if (0 === strpos($relPath, '/')) { |
||
246 | $relPath = substr($relPath, 1); |
||
247 | } |
||
248 | |||
249 | $details = [ |
||
250 | 'fullPath' => $fullPath, |
||
251 | 'relPath' => $relPath, |
||
252 | ]; |
||
253 | |||
254 | if ('prod' == $env) { |
||
255 | $lastModified = filemtime($fullPath); |
||
256 | if (!isset($modifiedLast[$group]) || $lastModified > $modifiedLast[$group]) { |
||
257 | $modifiedLast[$group] = $lastModified; |
||
258 | } |
||
259 | $assets[$ext][$group][$relPath] = $details; |
||
260 | } else { |
||
261 | $assets[$ext][$relPath] = $details; |
||
262 | } |
||
263 | } |
||
264 | unset($files); |
||
265 | } |
||
266 | } |
||
267 | |||
268 | unset($directories); |
||
269 | $files = new Finder(); |
||
270 | $files->files()->depth('0')->ignoreDotFiles(true)->name('*.'.$ext)->in($dir); |
||
271 | |||
272 | $sort = function (\SplFileInfo $a, \SplFileInfo $b) { |
||
273 | return strnatcmp($a->getRealpath(), $b->getRealpath()); |
||
274 | }; |
||
275 | $files->sort($sort); |
||
276 | |||
277 | foreach ($files as $file) { |
||
278 | $fullPath = str_replace('\\', '/', $file->getPathname()); |
||
279 | $relPath = str_replace($rootPath, '', $fullPath); |
||
280 | |||
281 | $details = [ |
||
282 | 'fullPath' => $fullPath, |
||
283 | 'relPath' => $relPath, |
||
284 | ]; |
||
285 | |||
286 | if ('prod' == $env) { |
||
287 | $lastModified = filemtime($fullPath); |
||
288 | if (!isset($modifiedLast['app']) || $lastModified > $modifiedLast['app']) { |
||
289 | $modifiedLast['app'] = $lastModified; |
||
290 | } |
||
291 | $assets[$ext]['app'][$relPath] = $details; |
||
292 | } else { |
||
293 | $assets[$ext][$relPath] = $details; |
||
294 | } |
||
295 | } |
||
296 | unset($files); |
||
297 | |||
298 | return $modifiedLast; |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Find asset overrides in the template. |
||
303 | * |
||
304 | * @param $env |
||
305 | * @param $assets |
||
306 | * |
||
307 | * @return array |
||
308 | */ |
||
309 | protected function findOverrides($env, &$assets) |
||
310 | { |
||
311 | $rootPath = $this->pathsHelper->getSystemPath('assets_root'); |
||
312 | $currentTheme = $this->pathsHelper->getSystemPath('current_theme'); |
||
313 | $modifiedLast = []; |
||
314 | $types = ['css', 'js']; |
||
315 | $overrideFiles = [ |
||
316 | 'libraries' => 'libraries_custom', |
||
317 | 'app' => 'app_custom', |
||
318 | ]; |
||
319 | |||
320 | foreach ($types as $ext) { |
||
321 | foreach ($overrideFiles as $group => $of) { |
||
322 | if (file_exists("$rootPath/$currentTheme/$ext/$of.$ext")) { |
||
323 | $fullPath = "$rootPath/$currentTheme/$ext/$of.$ext"; |
||
324 | $relPath = "$currentTheme/$ext/$of.$ext"; |
||
325 | |||
326 | $details = [ |
||
327 | 'fullPath' => $fullPath, |
||
328 | 'relPath' => $relPath, |
||
329 | ]; |
||
330 | |||
331 | if ('prod' == $env) { |
||
332 | $lastModified = filemtime($fullPath); |
||
333 | if (!isset($modifiedLast[$ext][$group]) || $lastModified > $modifiedLast[$ext][$group]) { |
||
334 | $modifiedLast[$ext][$group] = $lastModified; |
||
335 | } |
||
336 | $assets[$ext][$group][$relPath] = $details; |
||
337 | } else { |
||
338 | $assets[$ext][$relPath] = $details; |
||
339 | } |
||
340 | } |
||
341 | } |
||
342 | } |
||
343 | |||
344 | return $modifiedLast; |
||
345 | } |
||
346 | } |
||
347 |