Completed
Push — master ( bf102e...fafcea )
by Nazar
06:46
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 17
CRAP Score 4.0186

Importance

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