Completed
Push — master ( 4e8078...fd8f4b )
by Nazar
04:30
created

Cache::cache_compressed_assets_files()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.0466

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 4
nop 4
dl 0
loc 24
ccs 12
cts 14
cp 0.8571
crap 4.0466
rs 8.6845
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2014-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\Page\Assets;
9
use
10
	cs\Event,
11
	cs\Page\Assets_processing;
12
13
class Cache {
14
	/**
15
	 * @param \cs\Config   $Config
16
	 * @param \cs\Language $L
17
	 * @param string       $theme
18
	 */
19 4
	public static function rebuild ($Config, $L, $theme) {
20 4
		if (!file_exists(PUBLIC_CACHE."/$theme.json")) {
21 4
			static::rebuild_normal($Config, $theme);
22 4
			Event::instance()->fire('System/Page/rebuild_cache');
23 4
			static::rebuild_optimized($theme);
24 4
			static::rebuild_webcomponents_polyfill();
25
		}
26
		/**
27
		 * We take hash of languages in order to take into account when list of active languages has changed (and generate cache for all acive languages)
28
		 */
29 4
		$languages_hash = md5(implode('', $Config->core['active_languages']));
30 4
		if (!file_exists(PUBLIC_CACHE."/languages-$languages_hash.json")) {
31 4
			static::rebuild_languages($Config, $L, $languages_hash);
32
		}
33 4
	}
34
	/**
35
	 * @param \cs\Config $Config
36
	 * @param string     $theme
37
	 */
38 4
	protected static function rebuild_normal ($Config, $theme) {
39 4
		list($dependencies, $assets_map) = Collecting::get_assets_dependencies_and_map($Config, $theme);
40 4
		$compressed_assets_map      = [];
41 4
		$not_embedded_resources_map = [];
42
		/** @noinspection ForeachSourceInspection */
43 4
		foreach ($assets_map as $filename_prefix => $local_assets) {
44 4
			$compressed_assets_map[$filename_prefix] = static::cache_compressed_assets_files(
45
				$filename_prefix,
46
				$local_assets,
47 4
				$Config->core['vulcanization'],
48
				$not_embedded_resources_map
49
			);
50
		}
51 4
		unset($assets_map, $filename_prefix, $local_assets);
52 4
		file_put_json(PUBLIC_CACHE."/$theme.json", [$dependencies, $compressed_assets_map, array_filter($not_embedded_resources_map)]);
53 4
	}
54
	/**
55
	 * @param string $theme
56
	 */
57 4
	protected static function rebuild_optimized ($theme) {
58 4
		list(, $compressed_assets_map, $preload_source) = file_get_json(PUBLIC_CACHE."/$theme.json");
59 4
		$preload = [array_values($compressed_assets_map['System'])];
60
		/** @noinspection ForeachSourceInspection */
61 4
		foreach ($compressed_assets_map['System'] as $path) {
62 4
			if (isset($preload_source[$path])) {
63 4
				$preload[] = $preload_source[$path];
64
			}
65
		}
66 4
		unset($compressed_assets_map['System']);
67 4
		$optimized_assets = array_merge(...array_values(array_map('array_values', $compressed_assets_map)));
68 4
		$preload          = array_merge(...$preload);
69 4
		file_put_json(PUBLIC_CACHE."/$theme.optimized.json", [$optimized_assets, $preload]);
70 4
	}
71 4
	protected static function rebuild_webcomponents_polyfill () {
72 4
		$webcomponents_js = file_get_contents(DIR.'/assets/js/WebComponents-polyfill/webcomponents-custom.min.js');
73 4
		$hash             = md5($webcomponents_js);
74 4
		file_put_contents(PUBLIC_CACHE."/$hash.js", $webcomponents_js, LOCK_EX | FILE_BINARY);
75 4
		file_put_contents(PUBLIC_CACHE.'/webcomponents.js.hash', $hash, LOCK_EX | FILE_BINARY);
76 4
	}
77
	/**
78
	 * @param \cs\Config   $Config
79
	 * @param \cs\Language $L
80
	 * @param string       $languages_hash
81
	 */
82 4
	protected static function rebuild_languages ($Config, $L, $languages_hash) {
83 4
		$current_language = $L->clanguage;
84 4
		$languages_map    = [];
85
		/** @noinspection ForeachSourceInspection */
86 4
		foreach ($Config->core['active_languages'] as $language) {
87 4
			$L->change($language);
88 4
			$translations             = _json_encode($L);
89 4
			$language_hash            = md5($translations);
90 4
			$languages_map[$language] = $language_hash;
91 4
			file_put_contents(PUBLIC_CACHE."/$language_hash.js", "define($translations);");
92
		}
93 4
		$L->change($current_language);
94 4
		file_put_json(PUBLIC_CACHE."/languages-$languages_hash.json", $languages_map);
95 4
	}
96
	/**
97
	 * Creates cached version of given HTML, JS and CSS files.
98
	 * Resulting files names consist of `$filename_prefix` and extension.
99
	 *
100
	 * @param string     $filename_prefix
101
	 * @param string[][] $assets                     Array of paths to files, may have keys: `css` and/or `js` and/or `html`
102
	 * @param bool       $vulcanization              Whether to put combined files separately or to make included assets built-in (vulcanization)
103
	 * @param string[][] $not_embedded_resources_map Resources like images/fonts might not be embedded into resulting CSS because of big size or CSS/JS because
104
	 *                                               of CSP
105
	 *
106
	 * @return array
107
	 */
108 4
	protected static function cache_compressed_assets_files ($filename_prefix, $assets, $vulcanization, &$not_embedded_resources_map) {
109 4
		$local_assets = [];
110 4
		foreach ($assets as $extension => $files) {
111 4
			$not_embedded_resources = [];
112 4
			$content                = static::cache_compressed_assets_files_single(
113
				$extension,
114
				$filename_prefix,
115
				$files,
116
				$vulcanization,
117
				$not_embedded_resources
118
			);
119 4
			foreach ($not_embedded_resources as &$resource) {
120
				if (strpos($resource, '/') !== 0) {
121
					$resource = "/storage/public_cache/$resource";
122
				}
123
			}
124 4
			$file_path = PUBLIC_CACHE.'/'.md5($content).'.'.$extension;
125 4
			file_put_contents($file_path, $content, LOCK_EX | FILE_BINARY);
126 4
			$relative_path                              = '/storage/public_cache/'.basename($file_path);
127 4
			$local_assets[$extension]                   = $relative_path;
128 4
			$not_embedded_resources_map[$relative_path] = $not_embedded_resources;
129
		}
130 4
		return $local_assets;
131
	}
132
	/**
133
	 * @param string   $extension
134
	 * @param string   $filename_prefix
135
	 * @param string[] $files
136
	 * @param bool     $vulcanization          Whether to put combined files separately or to make included assets built-in (vulcanization)
137
	 * @param string[] $not_embedded_resources Resources like images/fonts might not be embedded into resulting CSS because of big size or CSS/JS because of CSP
138
	 *
139
	 * @return string
140
	 */
141 4
	protected static function cache_compressed_assets_files_single ($extension, $filename_prefix, $files, $vulcanization, &$not_embedded_resources) {
142 4
		$content = '';
143
		switch ($extension) {
144
			/**
145
			 * Insert external elements into resulting css file.
146
			 * It is needed, because those files will not be copied into new destination of resulting css file.
147
			 */
148 4
			case 'css':
149
				/**
150
				 * @param string $content
151
				 * @param string $file
152
				 *
153
				 * @return string
154
				 */
155
				$callback = function ($content, $file) use (&$not_embedded_resources) {
156 4
					return $content.Assets_processing::css(file_get_contents($file), $file, $not_embedded_resources);
157 4
				};
158 4
				break;
159
			/**
160
			 * Combine css and js files for Web Component into resulting files in order to optimize loading process
161
			 */
162 4
			case 'html':
163
				/**
164
				 * For CSP-compatible HTML files we need to know destination to put there additional JS/CSS files
165
				 *
166
				 * @param string $content
167
				 * @param string $file
168
				 *
169
				 * @return string
170
				 */
171
				$callback = function ($content, $file) use (&$not_embedded_resources) {
172
					return
173
						$content.
174 4
						Assets_processing::html(
175
							file_get_contents($file),
176
							$file,
177 4
							'',
178 4
							true,
179
							$not_embedded_resources
180
						);
181 4
				};
182 4
				break;
183 4
			case 'js':
184
				/**
185
				 * @param string $content
186
				 * @param string $file
187
				 *
188
				 * @return string
189
				 */
190 4
				$callback = function ($content, $file) {
191 4
					return $content.Assets_processing::js(file_get_contents($file));
192 4
				};
193 4
				if ($filename_prefix == 'System') {
194 4
					$content = 'window.cs={};window.requirejs='._json_encode(RequireJS::get_config()).';';
195
				}
196
		}
197
		/** @noinspection PhpUndefinedVariableInspection */
198 4
		$content .= array_reduce($files, $callback);
199 4
		if ($extension == 'html') {
200 4
			$content   = Assets_processing::html($content, '', PUBLIC_CACHE, $vulcanization);
201
		}
202 4
		return $content;
203
	}
204
}
205