Completed
Push — master ( c26535...08dd3b )
by Nazar
03:45
created

Packages_manipulation   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 6

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 214
rs 8.8
wmc 36
lcom 0
cbo 6

6 Methods

Rating   Name   Duplication   Size   Complexity  
C move_uploaded_file_to_tmp() 0 25 8
B install_extract() 0 33 5
C update_extract() 0 65 10
A update_php_sql() 0 16 4
B get_update_versions() 0 13 5
A execute_sql_from_directory() 0 16 4
1
<?php
2
/**
3
 * @package    CleverStyle CMS
4
 * @subpackage System module
5
 * @category   modules
6
 * @author     Nazar Mokrynskyi <[email protected]>
7
 * @copyright  Copyright (c) 2015-2016, Nazar Mokrynskyi
8
 * @license    MIT License, see license.txt
9
 */
10
namespace cs\modules\System;
11
use
12
	cs\Config,
13
	cs\Core,
14
	cs\DB,
15
	cs\Language,
16
	cs\Page,
17
	cs\Request;
18
19
/**
20
 * Utility functions, necessary during packages manipulation (installation/uninstallation, enabling/disabling)
21
 */
22
class Packages_manipulation {
23
	/**
24
	 * @param string $file_name File key in `cs\Request::$files`
25
	 *
26
	 * @return false|string Path to file location if succeed or `false` on failure
27
	 */
28
	static function move_uploaded_file_to_tmp ($file_name) {
29
		$file = Request::instance()->files($file_name);
30
		if (!$file) {
31
			return false;
32
		}
33
		$L    = Language::prefix('system_admin_');
34
		$Page = Page::instance();
35
		switch ($file['error']) {
36
			case UPLOAD_ERR_INI_SIZE:
37
			case UPLOAD_ERR_FORM_SIZE:
38
				$Page->warning($L->file_too_large);
39
				return false;
40
			case UPLOAD_ERR_NO_TMP_DIR:
41
				$Page->warning($L->temporary_folder_is_missing);
42
				return false;
43
			case UPLOAD_ERR_CANT_WRITE:
44
				$Page->warning($L->cant_write_file_to_disk);
45
				return false;
46
		}
47
		if ($file['error'] != UPLOAD_ERR_OK) {
48
			return false;
49
		}
50
		$tmp_name = TEMP.'/'.md5(random_bytes(1000)).'.phar';
51
		return copy($file['tmp_name'], $tmp_name) ? $tmp_name : false;
52
	}
53
	/**
54
	 * Generic extraction of files from phar distributive for CleverStyle CMS (components installation)
55
	 *
56
	 * @param string $target_directory
57
	 * @param string $source_phar Will be removed after extraction
58
	 *
59
	 * @return bool
60
	 */
61
	static function install_extract ($target_directory, $source_phar) {
62
		/** @noinspection MkdirRaceConditionInspection */
63
		if (!mkdir($target_directory, 0770)) {
64
			return false;
65
		}
66
		$tmp_dir   = "phar://$source_phar";
67
		$fs        = file_get_json("$tmp_dir/fs.json");
68
		$extracted = array_filter(
69
			array_map(
70
				function ($index, $file) use ($tmp_dir, $target_directory) {
71
					if (
72
						!@mkdir(dirname("$target_directory/$file"), 0770, true) &&
73
						!is_dir(dirname("$target_directory/$file"))
74
					) {
75
						return false;
76
					}
77
					/**
78
					 * TODO: copy() + file_exists() is a hack for HHVM, when bug fixed upstream (copying of empty files) this should be simplified
79
					 */
80
					copy("$tmp_dir/fs/$index", "$target_directory/$file");
81
					return file_exists("$target_directory/$file");
82
				},
83
				$fs,
84
				array_keys($fs)
85
			)
86
		);
87
		unlink($source_phar);
88
		if (count($extracted) === count($fs)) {
89
			file_put_json("$target_directory/fs.json", array_keys($fs));
90
			return true;
91
		}
92
		return false;
93
	}
94
	/**
95
	 * Generic extraction of files from phar distributive for CleverStyle CMS (system and components update)
96
	 *
97
	 * @param string      $target_directory
98
	 * @param string      $source_phar             Will be removed after extraction
99
	 * @param null|string $fs_location_directory   Defaults to `$target_directory`
100
	 * @param null|string $meta_location_directory Defaults to `$target_directory`
101
	 *
102
	 * @return bool
103
	 */
104
	static function update_extract ($target_directory, $source_phar, $fs_location_directory = null, $meta_location_directory = null) {
105
		$fs_location_directory   = $fs_location_directory ?: $target_directory;
106
		$meta_location_directory = $meta_location_directory ?: $target_directory;
107
		/**
108
		 * Backup some necessary information about current version
109
		 */
110
		copy("$fs_location_directory/fs.json", "$fs_location_directory/fs_backup.json");
111
		copy("$meta_location_directory/meta.json", "$meta_location_directory/meta_backup.json");
112
		/**
113
		 * Extracting new versions of files
114
		 */
115
		$tmp_dir   = "phar://$source_phar";
116
		$fs        = file_get_json("$tmp_dir/fs.json");
117
		$extracted = array_filter(
118
			array_map(
119
				function ($index, $file) use ($tmp_dir, $target_directory) {
120
					if (
121
						!@mkdir(dirname("$target_directory/$file"), 0770, true) &&
122
						!is_dir(dirname("$target_directory/$file"))
123
					) {
124
						return false;
125
					}
126
					/**
127
					 * TODO: copy() + file_exists() is a hack for HHVM, when bug fixed upstream (copying of empty files) this should be simplified
128
					 */
129
					copy("$tmp_dir/fs/$index", "$target_directory/$file");
130
					return file_exists("$target_directory/$file");
131
				},
132
				$fs,
133
				array_keys($fs)
134
			)
135
		);
136
		unlink($source_phar);
137
		unset($tmp_dir);
138
		if (count($extracted) !== count($fs)) {
139
			return false;
140
		}
141
		unset($extract);
142
		$fs = array_keys($fs);
143
		/**
144
		 * Removing of old unnecessary files and directories
145
		 */
146
		foreach (
147
			array_diff(
148
				file_get_json("$fs_location_directory/fs_backup.json"),
149
				$fs
150
			) as $file
151
		) {
152
			$file = "$target_directory/$file";
153
			if (is_writable($file)) {
154
				unlink($file);
155
				// Recursively remove all empty parent directories
156
				while (!get_files_list($file = dirname($file), false, 'fd')) {
157
					rmdir($file);
158
				}
159
			}
160
		}
161
		unset($file, $dir);
162
		file_put_json("$fs_location_directory/fs.json", $fs);
163
		clearstatcache(true);
164
		if (function_exists('opcache_reset')) {
165
			opcache_reset();
166
		}
167
		return true;
168
	}
169
	/**
170
	 * Generic update for CleverStyle CMS (system and components), runs PHP scripts and does DB migrations after extracting of new distributive
171
	 *
172
	 * @param string     $target_directory
173
	 * @param string     $old_version
174
	 * @param array|null $db_array `$Config->components['modules'][$module]['db']` if module or system
175
	 *
176
	 * @throws \cs\ExitException
177
	 */
178
	static function update_php_sql ($target_directory, $old_version, $db_array = null) {
179
		foreach (self::get_update_versions($target_directory) as $version) {
180
			if (version_compare($old_version, $version, '<')) {
181
				/**
182
				 * PHP update script
183
				 */
184
				_include_once("$target_directory/meta/update/$version.php", false);
185
				/**
186
				 * Database update
187
				 */
188
				if ($db_array) {
189
					self::execute_sql_from_directory("$target_directory/meta/update_db", $db_array, $version);
190
				}
191
			}
192
		}
193
	}
194
	/**
195
	 * @param string $target_directory
196
	 *
197
	 * @return string[]
198
	 */
199
	protected static function get_update_versions ($target_directory) {
200
		$update_versions = _mb_substr(get_files_list("$target_directory/meta/update"), 0, -4) ?: [];
201
		foreach (get_files_list("$target_directory/meta/update_db", false, 'd') ?: [] as $db) {
202
			/** @noinspection SlowArrayOperationsInLoopInspection */
203
			$update_versions = array_merge(
204
				$update_versions,
205
				get_files_list("$target_directory/meta/update_db/$db", false, 'd') ?: []
206
			);
207
		}
208
		$update_versions = array_unique($update_versions);
209
		usort($update_versions, 'version_compare');
210
		return $update_versions;
211
	}
212
	/**
213
	 * @param string $directory        Base path to SQL files
214
	 * @param array  $db_configuration Array in form [$db_name => $index]
215
	 * @param string $version          In case when we are working with update script we might have version subdirectory
216
	 *
217
	 * @throws \cs\ExitException
218
	 */
219
	static function execute_sql_from_directory ($directory, $db_configuration, $version = '') {
220
		$Config = Config::instance();
221
		$Core   = Core::instance();
222
		$db     = DB::instance();
223
		time_limit_pause();
224
		foreach ($db_configuration as $db_name => $index) {
225
			$db_type  = $index == 0 ? $Core->db_type : $Config->db[$index]['type'];
226
			$sql_file = "$directory/$db_name/$version/$db_type.sql";
227
			if (file_exists($sql_file)) {
228
				$db->db_prime($index)->q(
229
					explode(';', file_get_contents($sql_file))
230
				);
231
			}
232
		}
233
		time_limit_pause(false);
234
	}
235
}
236