Completed
Push — master ( a1e1e6...8edf6f )
by Nazar
04:25
created

Cache::get_hash_of()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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