Completed
Push — master ( 4a3457...2febd0 )
by Nazar
04:30
created

Cache::rebuild_languages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 11
nc 2
nop 3
dl 0
loc 15
ccs 12
cts 12
cp 1
crap 2
rs 9.4285
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\Includes;
9
use
10
	cs\Event,
11
	cs\Page\Includes_processing;
12
13
class Cache {
14
	/**
15
	 * @param \cs\Config   $Config
16
	 * @param \cs\Language $L
17
	 * @param string       $pcache_basename_path
18
	 * @param string       $theme
19
	 */
20 4
	public static function rebuild ($Config, $L, $pcache_basename_path, $theme) {
21 4
		if (!file_exists("$pcache_basename_path.json")) {
22 4
			static::rebuild_normal($Config, $pcache_basename_path, $theme);
23 4
			Event::instance()->fire('System/Page/rebuild_cache');
24 4
			static::rebuild_optimized($pcache_basename_path);
25 4
			static::rebuild_webcomponents_polyfill();
26
		}
27
		/**
28
		 * 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)
29
		 */
30 4
		$languages_hash = static::get_hash_of(implode('', $Config->core['active_languages']));
31 4
		if (!file_exists(PUBLIC_CACHE."/languages-$languages_hash.json")) {
32 4
			static::rebuild_languages($Config, $L, $languages_hash);
33
		}
34 4
	}
35
	/**
36
	 * @param string $content
37
	 *
38
	 * @return string
39
	 */
40 4
	protected static function get_hash_of ($content) {
41 4
		return substr(md5($content), 0, 5);
42
	}
43
	/**
44
	 * @param \cs\Config $Config
45
	 * @param string     $pcache_basename_path
46
	 */
47 4
	protected static function rebuild_normal ($Config, $pcache_basename_path, $theme) {
48 4
		list($dependencies, $includes_map) = Collecting::get_includes_dependencies_and_map($Config, $theme);
49 4
		$compressed_includes_map    = [];
50 4
		$not_embedded_resources_map = [];
51
		/** @noinspection ForeachSourceInspection */
52 4
		foreach ($includes_map as $filename_prefix => $local_includes) {
53 4
			$compressed_includes_map[$filename_prefix] = static::cache_compressed_includes_files(
54 4
				"$pcache_basename_path:".str_replace('/', '+', $filename_prefix),
55
				$local_includes,
56 4
				$Config->core['vulcanization'],
57
				$not_embedded_resources_map
58
			);
59
		}
60 4
		unset($includes_map, $filename_prefix, $local_includes);
61 4
		file_put_json("$pcache_basename_path.json", [$dependencies, $compressed_includes_map, array_filter($not_embedded_resources_map)]);
62 4
	}
63
	/**
64
	 * @param string $pcache_basename_path
65
	 */
66 4
	protected static function rebuild_optimized ($pcache_basename_path) {
67 4
		list(, $compressed_includes_map, $preload_source) = file_get_json("$pcache_basename_path.json");
68 4
		$preload = [array_values($compressed_includes_map['System'])];
69
		/** @noinspection ForeachSourceInspection */
70 4
		foreach ($compressed_includes_map['System'] as $path) {
71 4
			if (isset($preload_source[$path])) {
72 4
				$preload[] = $preload_source[$path];
73
			}
74
		}
75 4
		unset($compressed_includes_map['System']);
76 4
		$optimized_includes = array_flip(array_merge(...array_values(array_map('array_values', $compressed_includes_map))));
77 4
		$preload            = array_merge(...$preload);
78 4
		file_put_json("$pcache_basename_path.optimized.json", [$optimized_includes, $preload]);
79 4
	}
80 4
	protected static function rebuild_webcomponents_polyfill () {
81 4
		$webcomponents_js = file_get_contents(DIR.'/includes/js/WebComponents-polyfill/webcomponents-custom.min.js');
82 4
		file_put_contents(PUBLIC_CACHE.'/webcomponents.js', $webcomponents_js, LOCK_EX | FILE_BINARY);
83 4
		file_put_contents(PUBLIC_CACHE.'/webcomponents.js.hash', static::get_hash_of($webcomponents_js), LOCK_EX | FILE_BINARY);
84 4
	}
85
	/**
86
	 * @param \cs\Config   $Config
87
	 * @param \cs\Language $L
88
	 * @param string       $languages_hash
89
	 */
90 4
	protected static function rebuild_languages ($Config, $L, $languages_hash) {
91 4
		$current_language = $L->clanguage;
92 4
		$languages_map    = [];
93
		/** @noinspection ForeachSourceInspection */
94 4
		foreach ($Config->core['active_languages'] as $language) {
95 4
			$L->change($language);
96
			/** @noinspection DisconnectedForeachInstructionInspection */
97 4
			$translations             = _json_encode($L);
98 4
			$language_hash            = static::get_hash_of($translations);
99 4
			$languages_map[$language] = $language_hash;
100 4
			file_put_contents(PUBLIC_CACHE."/languages-$language-$language_hash.js", "define($translations);");
101
		}
102 4
		$L->change($current_language);
103 4
		file_put_json(PUBLIC_CACHE."/languages-$languages_hash.json", $languages_map);
104 4
	}
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     $target_file_path
110
	 * @param string[][] $includes                   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 includes 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 4
	protected static function cache_compressed_includes_files ($target_file_path, $includes, $vulcanization, &$not_embedded_resources_map) {
118 4
		$local_includes = [];
119 4
		foreach ($includes as $extension => $files) {
120 4
			$not_embedded_resources = [];
121 4
			$content                = static::cache_compressed_includes_files_single(
122
				$extension,
123
				$target_file_path,
124
				$files,
125
				$vulcanization,
126
				$not_embedded_resources
127
			);
128 4
			foreach ($not_embedded_resources as &$resource) {
129
				if (strpos($resource, '/') !== 0) {
130
					$resource = "/storage/pcache/$resource";
131
				}
132
			}
133 4
			unset($resource);
134 4
			$file_path = "$target_file_path.$extension";
135 4
			file_put_contents($file_path, $content, LOCK_EX | FILE_BINARY);
136 4
			$relative_path                              = '/storage/pcache/'.basename($file_path).'?'.static::get_hash_of($content);
137 4
			$local_includes[$extension]                 = $relative_path;
138 4
			$not_embedded_resources_map[$relative_path] = $not_embedded_resources;
139
		}
140 4
		return $local_includes;
141
	}
142
	/**
143
	 * @param string   $extension
144
	 * @param string   $target_file_path
145
	 * @param string[] $files
146
	 * @param bool     $vulcanization          Whether to put combined files separately or to make includes 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 static function cache_compressed_includes_files_single ($extension, $target_file_path, $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.Includes_processing::css(file_get_contents($file), $file, $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 4
					return $content.Includes_processing::html(
183
						file_get_contents($file),
184
						$file,
185 4
						'',
186 4
						true,
187
						$not_embedded_resources
188
					);
189 4
				};
190 4
				break;
191 4
			case 'js':
192
				/**
193
				 * @param string $content
194
				 * @param string $file
195
				 *
196
				 * @return string
197
				 */
198 4
				$callback = function ($content, $file) {
199 4
					return $content.Includes_processing::js(file_get_contents($file));
200 4
				};
201 4
				if (substr($target_file_path, -7) == ':System') {
202 4
					$content = 'window.cs={};window.requirejs={paths:'._json_encode(RequireJS::get_paths()).'};';
203
				}
204
		}
205
		/** @noinspection PhpUndefinedVariableInspection */
206 4
		$content .= array_reduce($files, $callback);
207 4
		if ($extension == 'html') {
208 4
			$file_path = "$target_file_path-$extension";
209 4
			$content   = Includes_processing::html($content, $file_path, $file_path, $vulcanization);
210
		}
211 4
		return $content;
212
	}
213
}
214