Completed
Push — master ( 4ecb33...7beef5 )
by Nazar
04:11
created

Builder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
/**
3
 * @package    CleverStyle CMS
4
 * @subpackage Builder
5
 * @author     Nazar Mokrynskyi <[email protected]>
6
 * @copyright  Copyright (c) 2011-2016, Nazar Mokrynskyi
7
 * @license    MIT License, see license.txt
8
 */
9
namespace cs;
10
use
11
	h,
12
	Phar;
13
14
class Builder {
15
	/**
16
	 * @var string
17
	 */
18
	protected $root;
19
	/**
20
	 * @var string
21
	 */
22
	protected $target;
23
	/**
24
	 * @param string $root
25
	 * @param string $target
26
	 */
27
	function __construct ($root, $target) {
28
		$this->root   = $root;
29
		$this->target = $target;
30
		@mkdir($target);
31
	}
32
	/**
33
	 * @param string[]    $modules
34
	 * @param string[]    $plugins
35
	 * @param string[]    $themes
36
	 * @param null|string $suffix
37
	 *
38
	 * @return string
39
	 */
40
	function core ($modules = [], $plugins = [], $themes = [], $suffix = null) {
41
		$suffix      = $suffix ? "_$suffix" : '';
42
		$version     = file_get_json("$this->root/components/modules/System/meta.json")['version'];
43
		$target_file = "$this->target/CleverStyle_CMS_$version$suffix.phar.php";
44
		if (file_exists($target_file)) {
45
			unlink($target_file);
46
		}
47
		$phar = new Phar($target_file);
48
		unset($target_file);
49
		$phar->startBuffering();
50
		$length = strlen("$this->root/");
51
		foreach (get_files_list("$this->root/install", false, 'f', true, true) as $file) {
52
			$phar->addFile($file, substr($file, $length));
53
		}
54
		unset($file);
55
		/**
56
		 * Core files to be included into installation package
57
		 */
58
		$core_files = $this->get_core_files();
59
		/**
60
		 * Add modules that should be built-in into package
61
		 */
62
		$components_files = [];
63
		$modules          = $this->filter_and_add_components("$this->root/components/modules", $modules, $components_files);
64
		$phar->addFromString('modules.json', _json_encode($modules));
65
		/**
66
		 * Add plugins that should be built-in into package
67
		 */
68
		$plugins = $this->filter_and_add_components("$this->root/components/plugins", $plugins, $components_files);
69
		$phar->addFromString('plugins.json', _json_encode($plugins));
70
		/**
71
		 * Add themes that should be built-in into package
72
		 */
73
		$themes = $this->filter_and_add_components("$this->root/themes", $themes, $components_files);
74
		$phar->addFromString('themes.json', _json_encode($themes));
75
		/**
76
		 * Joining system and components files
77
		 */
78
		$core_files = array_merge($core_files, $components_files);
79
		/**
80
		 * Addition of files into package
81
		 */
82
		foreach ($core_files as $index => &$file) {
83
			$phar->addFile($file, "fs/$index");
84
			$file = substr($file, $length);
85
		}
86
		unset($index, $file);
87
		/**
88
		 * Addition of separate files into package
89
		 */
90
		$phar->addFromString('fs/'.count($core_files), $this->get_readme());
91
		$core_files[] = 'readme.html';
92
		$phar->addFromString(
93
			'languages.json',
94
			_json_encode(
95
				array_merge(
96
					_substr(get_files_list("$this->root/core/languages", '/^.*?\.php$/i', 'f'), 0, -4) ?: [],
97
					_substr(get_files_list("$this->root/core/languages", '/^.*?\.json$/i', 'f'), 0, -5) ?: []
98
				)
99
			)
100
		);
101
		$phar->addFromString(
102
			'db_engines.json',
103
			_json_encode(
104
				_substr(get_files_list("$this->root/core/engines/DB", '/^[^_].*?\.php$/i', 'f'), 0, -4)
105
			)
106
		);
107
		/**
108
		 * Fixation of system files list (without components files), it is needed for future system updating
109
		 */
110
		$phar->addFromString(
111
			'fs/'.count($core_files),
112
			_json_encode(
113
				array_values(
114
					array_diff(
115
						$core_files,
116
						_substr($components_files, $length)
117
					)
118
				)
119
			)
120
		);
121
		$core_files[] = 'core/fs.json';
122
		unset($components_files, $length);
123
		/**
124
		 * Addition of files, that are needed only for installation
125
		 */
126
		$phar->addFromString('fs/'.count($core_files), $this->get_htaccess());
127
		$core_files[] = '.htaccess';
128
		$phar->addFile("$this->root/config/main.php", 'fs/'.count($core_files));
129
		$core_files[] = 'config/main.php';
130
		$phar->addFile("$this->root/favicon.ico", 'fs/'.count($core_files));
131
		$core_files[] = 'favicon.ico';
132
		$phar->addFile("$this->root/.gitignore", 'fs/'.count($core_files));
133
		$core_files[] = '.gitignore';
134
		/**
135
		 * Flip array to have direct access to files by name during extracting and installation, and fixing of files list for installation
136
		 */
137
		$phar->addFromString(
138
			'fs.json',
139
			_json_encode(
140
				array_flip($core_files)
141
			)
142
		);
143
		unset($core_files);
144
		/**
145
		 * Addition of supplementary files, that are needed directly for installation process: installer with GUI interface, readme, license, some additional
146
		 * information about available languages, themes, current version of system
147
		 */
148
		$phar->addFile("$this->root/install.php", 'install.php');
149
		$phar->addFromString('readme.html', $this->get_readme());
150
		$phar->addFile("$this->root/license.txt", 'license.txt');
151
		$phar->addFile("$this->root/components/modules/System/meta.json", 'meta.json');
152
		$phar->setStub(
153
			<<<STUB
154
<?php
155
if (PHP_SAPI == 'cli') {
156
	Phar::mapPhar('cleverstyle_cms.phar');
157
	include 'phar://cleverstyle_cms.phar/install.php';
158
} else {
159
	Phar::webPhar(null, 'install.php');
160
}
161
__HALT_COMPILER();
162
STUB
163
		);
164
		$phar->stopBuffering();
165
		return "Done! CleverStyle CMS $version";
166
	}
167
	/**
168
	 * Get array of files
169
	 *
170
	 * @return string[]
171
	 */
172
	protected function get_core_files () {
173
		$files_to_include = [
174
			"$this->root/components/modules/System",
175
			"$this->root/components/blocks/.gitkept",
176
			"$this->root/components/plugins/.gitkept",
177
			"$this->root/core",
178
			"$this->root/custom",
179
			"$this->root/includes",
180
			"$this->root/templates",
181
			"$this->root/themes/CleverStyle",
182
			"$this->root/bower.json",
183
			"$this->root/composer.json",
184
			"$this->root/composer.lock",
185
			"$this->root/index.php",
186
			"$this->root/license.txt",
187
			"$this->root/package.json",
188
			"$this->root/Storage.php"
189
		];
190
		$files            = [];
191
		foreach ($files_to_include as $s) {
192
			if (is_file($s)) {
193
				$files[] = $s;
194
			} elseif (is_dir($s)) {
195
				/** @noinspection SlowArrayOperationsInLoopInspection */
196
				$files = array_merge(
197
					$files,
198
					get_files_list($s, false, 'f', true, true, false, false, true)
199
				);
200
			}
201
		}
202
		return $files;
203
	}
204
	/**
205
	 * @param string   $dir
206
	 * @param string[] $components
207
	 * @param string[] $components_files
208
	 *
209
	 * @return string[]
210
	 */
211
	protected function filter_and_add_components ($dir, $components, &$components_files) {
212
		$components = array_filter(
213
			$components,
214
			function ($component) use ($dir, &$components_files) {
215
				return $this->get_component_files("$dir/$component", $components_files);
216
			}
217
		);
218
		sort($components);
219
		return $components;
220
	}
221
	/**
222
	 * @param string   $component_root
223
	 * @param string[] $files Array, where new files will be appended
224
	 *
225
	 * @return bool
226
	 */
227
	protected function get_component_files ($component_root, &$files) {
228
		/**
229
		 * Do not allow building System module and CleverStyle theme
230
		 */
231
		if (in_array(basename($component_root), ['System', 'CleverStyle'])) {
232
			return false;
233
		}
234
		/**
235
		 * Components without meta.json also not allowed
236
		 */
237
		if (!file_exists("$component_root/meta.json")) {
238
			return false;
239
		}
240
		@unlink("$component_root/fs.json");
241
		$local_files = get_files_list($component_root, false, 'f', true, true, false, false, true);
242
		$files       = array_merge($files, $local_files);
243
		file_put_json(
244
			"$component_root/fs.json",
245
			array_values(
246
				_substr(
247
					$local_files,
248
					strlen("$component_root/")
249
				)
250
			)
251
		);
252
		$files[] = "$component_root/fs.json";
253
		return true;
254
	}
255
	/**
256
	 * @return string
257
	 */
258
	protected function get_readme () {
259
		return str_replace(
260
			[
261
				'$version$',
262
				'$image$'
263
			],
264
			[
265
				file_get_json("$this->root/components/modules/System/meta.json")['version'],
266
				h::img(
267
					[
268
						'src' => 'data:image/png;charset=utf-8;base64,'.base64_encode(file_get_contents("$this->root/install/logo.png"))
269
					]
270
				)
271
			],
272
			file_get_contents("$this->root/readme.html")
273
		);
274
	}
275
	/**
276
	 * @return string
277
	 */
278
	protected function get_htaccess () {
279
		/** @lang ApacheConfig */
280
		return <<<HTACCESS
281
AddDefaultCharset utf-8
282
Options -Indexes -Multiviews +FollowSymLinks
283
IndexIgnore *.php *.pl *.cgi *.htaccess *.htpasswd
284
285
RewriteEngine On
286
RewriteBase /
287
288
<FilesMatch ".*/.*">
289
	Options -FollowSymLinks
290
</FilesMatch>
291
<FilesMatch "\.(css|js|gif|jpg|jpeg|png|ico|svg|svgz|ttc|ttf|otf|woff|woff2|eot)$">
292
	RewriteEngine Off
293
</FilesMatch>
294
<Files license.txt>
295
	RewriteEngine Off
296
</Files>
297
#<Files Storage.php>
298
#	RewriteEngine Off
299
#</Files>
300
301
RewriteRule .* index.php
302
HTACCESS;
303
	}
304
	/**
305
	 * @param string      $module
306
	 * @param null|string $suffix
307
	 *
308
	 * @return string
309
	 */
310
	function module ($module, $suffix = null) {
311
		if ($module == 'System') {
312
			return "Can't build module, System module is a part of core, it is not necessary to build it as separate module";
313
		}
314
		return $this->generic_package_creation("$this->root/components/modules/$module", $suffix);
315
	}
316
	/**
317
	 * @param string      $plugin
318
	 * @param null|string $suffix
319
	 *
320
	 * @return string
321
	 */
322
	function plugin ($plugin, $suffix = null) {
323
		return $this->generic_package_creation("$this->root/components/plugins/$plugin", $suffix);
324
	}
325
	/**
326
	 * @param string      $theme
327
	 * @param null|string $suffix
328
	 *
329
	 * @return string
330
	 */
331
	function theme ($theme, $suffix = null) {
332
		if ($theme == 'CleverStyle') {
333
			return "Can't build theme, CleverStyle theme is a part of core, it is not necessary to build it as separate theme";
334
		}
335
		return $this->generic_package_creation("$this->root/themes/$theme", $suffix);
336
	}
337
	protected function generic_package_creation ($source_dir, $suffix = null) {
338
		if (!file_exists("$source_dir/meta.json")) {
339
			$component = basename($source_dir);
340
			return "Can't build $component, meta information (meta.json) not found";
341
		}
342
		$meta = file_get_json("$source_dir/meta.json");
343
		$type = '';
344
		$Type = '';
345
		switch ($meta['category']) {
346
			case 'modules':
347
				$type = 'module_';
348
				$Type = 'Module';
349
				break;
350
			case 'plugins':
351
				$type = 'plugins_';
352
				$Type = 'Plugin';
353
				break;
354
			case 'themes':
355
				$type = 'theme_';
356
				$Type = 'Theme';
357
				break;
358
		}
359
		$suffix      = $suffix ? "_$suffix" : '';
360
		$target_file = "$this->target/$type$meta[package]_$meta[version]$suffix.phar.php";
361
		if (file_exists($target_file)) {
362
			unlink($target_file);
363
		}
364
		$phar = new Phar($target_file);
365
		unset($target_file);
366
		$phar->startBuffering();
367
		@unlink("$source_dir/fs.json");
368
		$files  = get_files_list($source_dir, false, 'f', true, true, false, false, true);
369
		$length = strlen("$source_dir/");
370
		foreach ($files as $index => &$file) {
371
			$phar->addFile($file, "fs/$index");
372
			$file = substr($file, $length);
373
		}
374
		unset($index, $file, $length);
375
		/**
376
		 * Flip array to have direct access to files by name during extraction
377
		 */
378
		$phar->addFromString(
379
			'fs.json',
380
			_json_encode(
381
				array_flip($files)
382
			)
383
		);
384
		unset($files);
385
		$phar->addFile("$source_dir/meta.json", 'meta.json');
386
		$readme = false;
387
		if (file_exists("$source_dir/readme.html")) {
388
			$phar->addFile("$source_dir/readme.html", 'readme.html');
389
			$readme = 'readme.html';
390
		} elseif (file_exists("$source_dir/readme.txt")) {
391
			$phar->addFile("$source_dir/readme.txt", 'readme.txt');
392
			$readme = 'readme.txt';
393
		}
394
		if ($readme) {
395
			$phar->setStub("<?php Phar::webPhar(null, '$readme'); __HALT_COMPILER();");
396
		} else {
397
			$phar->addFromString('index.html', isset($meta['description']) ? $meta['description'] : $meta['package']);
398
			$phar->setStub("<?php Phar::webPhar(null, 'index.html'); __HALT_COMPILER();");
399
		}
400
		$phar->stopBuffering();
401
		return "Done! $Type $meta[package] $meta[version]";
402
	}
403
}
404