Completed
Push — master ( be40dc...524dea )
by Nazar
06:35
created

Cache   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Test Coverage

Coverage 98.04%

Importance

Changes 0
Metric Value
dl 0
loc 200
rs 10
c 0
b 0
f 0
ccs 100
cts 102
cp 0.9804
wmc 22

8 Methods

Rating   Name   Duplication   Size   Complexity  
A rebuild_webcomponents_polyfill() 0 5 1
A rebuild() 0 13 3
A rebuild_optimized() 0 13 3
B cache_compressed_assets_files() 0 23 4
A rebuild_normal() 0 15 2
A rebuild_languages() 0 13 2
A __construct() 0 2 1
B cache_compressed_assets_files_single() 0 62 6
1
<?php
2
/**
3
 * @package CleverStyle Framework
4
 * @author  Nazar Mokrynskyi <[email protected]>
5
 * @license 0BSD
6
 */
7
namespace cs\Page\Assets;
8
use
9
	cs\Event,
10
	cs\Page\Assets_processing;
11
12
class Cache {
13
	/**
14
	 * @var string
15
	 */
16
	protected $public_cache;
17
	/**
18
	 * @param string $public_cache
19
	 */
20 6
	public function __construct ($public_cache = PUBLIC_CACHE) {
21 6
		$this->public_cache = $public_cache;
22 6
	}
23
	/**
24
	 * @param \cs\Config   $Config
25
	 * @param \cs\Language $L
26
	 * @param string       $theme
27
	 */
28 6
	public function rebuild ($Config, $L, $theme) {
29 6
		if (!file_exists("$this->public_cache/$theme.json")) {
30 6
			$this->rebuild_normal($Config, $theme);
31 6
			Event::instance()->fire('System/Page/rebuild_cache');
32 6
			$this->rebuild_optimized($theme);
33 6
			$this->rebuild_webcomponents_polyfill();
34
		}
35
		/**
36
		 * 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)
37
		 */
38 6
		$languages_hash = md5(implode('', $Config->core['active_languages']));
39 6
		if (!file_exists("$this->public_cache/languages-$languages_hash.json")) {
40 6
			$this->rebuild_languages($Config, $L, $languages_hash);
41
		}
42 6
	}
43
	/**
44
	 * @param \cs\Config $Config
45
	 * @param string     $theme
46
	 */
47 6
	protected function rebuild_normal ($Config, $theme) {
48 6
		list($dependencies, $assets_map) = Collecting::get_assets_dependencies_and_map($Config, $theme);
49 6
		$compressed_assets_map      = [];
50 6
		$not_embedded_resources_map = [];
51
		/** @noinspection ForeachSourceInspection */
52 6
		foreach ($assets_map as $filename_prefix => $local_assets) {
53 6
			$compressed_assets_map[$filename_prefix] = $this->cache_compressed_assets_files(
54 6
				$filename_prefix,
55 6
				$local_assets,
56 6
				$Config->core['vulcanization'],
57 6
				$not_embedded_resources_map
58
			);
59
		}
60 6
		unset($assets_map, $filename_prefix, $local_assets);
61 6
		file_put_json("$this->public_cache/$theme.json", [$dependencies, $compressed_assets_map, array_filter($not_embedded_resources_map)]);
62 6
	}
63
	/**
64
	 * @param string $theme
65
	 */
66 6
	protected function rebuild_optimized ($theme) {
67 6
		list(, $compressed_assets_map, $preload_source) = file_get_json("$this->public_cache/$theme.json");
68 6
		$preload = [array_values($compressed_assets_map['System'])];
69
		/** @noinspection ForeachSourceInspection */
70 6
		foreach ($compressed_assets_map['System'] as $path) {
71 6
			if (isset($preload_source[$path])) {
72 6
				$preload[] = $preload_source[$path];
73
			}
74
		}
75 6
		unset($compressed_assets_map['System']);
76 6
		$optimized_assets = array_merge(...array_values(array_map('array_values', $compressed_assets_map)));
0 ignored issues
show
Bug introduced by
array_values(array_map('...compressed_assets_map)) is expanded, but the parameter $array1 of array_merge() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
		$optimized_assets = array_merge(/** @scrutinizer ignore-type */ ...array_values(array_map('array_values', $compressed_assets_map)));
Loading history...
77 6
		$preload          = array_merge(...$preload);
78 6
		file_put_json("$this->public_cache/$theme.optimized.json", [$optimized_assets, $preload]);
79 6
	}
80 6
	protected function rebuild_webcomponents_polyfill () {
81 6
		$webcomponents_js = file_get_contents(DIR.'/assets/js/WebComponents-polyfill/webcomponents-hi-sd-ce.min.js');
82 6
		$hash             = md5($webcomponents_js);
83 6
		file_put_contents("$this->public_cache/$hash.js", $webcomponents_js, LOCK_EX | FILE_BINARY);
84 6
		file_put_contents("$this->public_cache/webcomponents.js.hash", $hash, LOCK_EX | FILE_BINARY);
85 6
	}
86
	/**
87
	 * @param \cs\Config   $Config
88
	 * @param \cs\Language $L
89
	 * @param string       $languages_hash
90
	 */
91 6
	protected function rebuild_languages ($Config, $L, $languages_hash) {
92 6
		$current_language = $L->clanguage;
93 6
		$languages_map    = [];
94
		/** @noinspection ForeachSourceInspection */
95 6
		foreach ($Config->core['active_languages'] as $language) {
96 6
			$L->change($language);
97 6
			$translations             = _json_encode($L);
98 6
			$language_hash            = md5($translations);
99 6
			$languages_map[$language] = $language_hash;
100 6
			file_put_contents("$this->public_cache/$language_hash.js", "define($translations);");
101
		}
102 6
		$L->change($current_language);
103 6
		file_put_json("$this->public_cache/languages-$languages_hash.json", $languages_map);
104 6
	}
105
	/**
106
	 * Creates cached version of given HTML, JS and CSS files.
107
	 * Resulting files names consist of `$filename_prefix` and extension.
108
	 *
109
	 * @param string     $filename_prefix
110
	 * @param string[][] $assets                     Array of paths to files, may have keys: `css` and/or `js` and/or `html`
111
	 * @param bool       $vulcanization              Whether to put combined files separately or to make included assets built-in (vulcanization)
112
	 * @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
113
	 *                                               of CSP
114
	 *
115
	 * @return array
116
	 */
117 6
	protected function cache_compressed_assets_files ($filename_prefix, $assets, $vulcanization, &$not_embedded_resources_map) {
118 6
		$local_assets = [];
119 6
		foreach ($assets as $extension => $files) {
120 6
			$not_embedded_resources = [];
121 6
			$content                = $this->cache_compressed_assets_files_single(
122 6
				$extension,
123 6
				$filename_prefix,
124 6
				$files,
125 6
				$vulcanization,
126 6
				$not_embedded_resources
127
			);
128 6
			foreach ($not_embedded_resources as &$resource) {
129
				if (strpos($resource, '/') !== 0) {
130
					$resource = "/storage/public_cache/$resource";
131
				}
132
			}
133 6
			$file_path = $this->public_cache.'/'.md5($content).'.'.$extension;
134 6
			file_put_contents($file_path, $content, LOCK_EX | FILE_BINARY);
135 6
			$relative_path                              = '/storage/public_cache/'.basename($file_path);
136 6
			$local_assets[$extension]                   = $relative_path;
137 6
			$not_embedded_resources_map[$relative_path] = $not_embedded_resources;
138
		}
139 6
		return $local_assets;
140
	}
141
	/**
142
	 * @param string   $extension
143
	 * @param string   $filename_prefix
144
	 * @param string[] $files
145
	 * @param bool     $vulcanization          Whether to put combined files separately or to make included assets built-in (vulcanization)
146
	 * @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
147
	 *
148
	 * @return string
149
	 */
150 6
	protected function cache_compressed_assets_files_single ($extension, $filename_prefix, $files, $vulcanization, &$not_embedded_resources) {
151 6
		$content = '';
152 6
		switch ($extension) {
153
			/**
154
			 * Insert external elements into resulting css file.
155
			 * It is needed, because those files will not be copied into new destination of resulting css file.
156
			 */
157 6
			case 'css':
158
				/**
159
				 * @param string $content
160
				 * @param string $file
161
				 *
162
				 * @return string
163
				 */
164 6
				$callback = function ($content, $file) use (&$not_embedded_resources) {
165 6
					return $content.Assets_processing::css(file_get_contents($file), $file, $this->public_cache, $not_embedded_resources);
166 6
				};
167 6
				break;
168
			/**
169
			 * Combine css and js files for Web Component into resulting files in order to optimize loading process
170
			 */
171 6
			case 'html':
172
				/**
173
				 * For CSP-compatible HTML files we need to know destination to put there additional JS/CSS files
174
				 *
175
				 * @param string $content
176
				 * @param string $file
177
				 *
178
				 * @return string
179
				 */
180 6
				$callback = function ($content, $file) use (&$not_embedded_resources) {
181
					return
182
						$content.
183 6
						Assets_processing::html(
184 6
							file_get_contents($file),
185 6
							$file,
186 6
							$this->public_cache,
187 6
							true,
188 6
							$not_embedded_resources
189
						);
190 6
				};
191 6
				break;
192 6
			case 'js':
193
				/**
194
				 * @param string $content
195
				 * @param string $file
196
				 *
197
				 * @return string
198
				 */
199 6
				$callback = function ($content, $file) {
200 6
					return $content.Assets_processing::js(file_get_contents($file));
201 6
				};
202 6
				if ($filename_prefix == 'System') {
203 6
					$content = 'window.cs={};window.requirejs='._json_encode(RequireJS::get_config()).';';
204
				}
205
		}
206
		/** @noinspection PhpUndefinedVariableInspection */
207 6
		$content .= array_reduce($files, $callback);
208 6
		if ($extension == 'html') {
209 6
			$content = Assets_processing::html($content, '', $this->public_cache, $vulcanization);
210
		}
211 6
		return $content;
212
	}
213
}
214