Completed
Push — master ( b7128d...2e6a15 )
by Nazar
08:00
created

functions.php ➔ modified_classes()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 3
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) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
/**
9
 * Base system functions, do not edit this file, or make it very carefully
10
 * otherwise system workability may be broken
11
 */
12
use
13
	cs\Cache,
14
	cs\Config,
15
	cs\Language,
16
	cs\Page;
17
18
/**
19
 * @param string $file
20
 *
21
 * @return array
22
 */
23
function __classes_get_from_cache ($file) {
24 184
	return defined('CACHE') && file_exists(CACHE."/classes/$file") ? file_get_json(CACHE."/classes/$file") : [];
25
}
26
27
/**
28
 * @param string $file
29
 * @param array  $content
30
 */
31
function __classes_put_into_cache ($file, $content) {
32 122
	if (defined('CACHE') && is_dir(CACHE)) {
33
		/** @noinspection MkdirRaceConditionInspection */
34 38
		@mkdir(CACHE.'/classes', 0770);
35 38
		file_put_json(CACHE."/classes/$file", $content);
36
	}
37 122
}
38
39
/**
40
 * Clean cache of classes autoload and customization
41
 */
42
function __classes_clean_cache () {
43
	@unlink(CACHE.'/classes/autoload');
44
	@unlink(CACHE.'/classes/aliases');
45
	@unlink(CACHE.'/classes/modified');
46
}
47
48
/**
49
 * Auto Loading of classes
50
 */
51 194
spl_autoload_register(
52
	function ($class) {
53 184
		static $cache, $aliases;
54 184
		if (!isset($cache)) {
55 184
			$cache   = __classes_get_from_cache('autoload');
56 184
			$aliases = __classes_get_from_cache('aliases');
57
		}
58 184
		if (isset($aliases[$class])) {
59 2
			spl_autoload_call($aliases[$class]);
60 2
			return class_exists($class, false) || (class_exists($aliases[$class], false) && class_alias($aliases[$class], $class));
61
		}
62 184
		if (isset($cache[$class])) {
63 98
			return $cache[$class] ? require_once $cache[$class] : false;
64
		}
65 108
		$prepared_class_name = ltrim($class, '\\');
66 108
		if (strpos($prepared_class_name, 'cs\\') === 0) {
67 106
			$prepared_class_name = substr($prepared_class_name, 3);
68
		}
69 108
		$prepared_class_name = explode('\\', $prepared_class_name);
70 108
		$namespace           = count($prepared_class_name) > 1 ? implode('/', array_slice($prepared_class_name, 0, -1)) : '';
71 108
		$class_name          = array_pop($prepared_class_name);
72 108
		$cache[$class]       = false;
73
		/**
74
		 * Try to load classes from different places. If not found in one place - try in another.
75
		 */
76
		if (
77 108
			file_exists($file = CORE."/classes/$namespace/$class_name.php") ||    //Core classes
78 86
			file_exists($file = CORE."/thirdparty/$namespace/$class_name.php") || //Third party classes
79 82
			file_exists($file = CORE."/traits/$namespace/$class_name.php") ||     //Core traits
80 30
			file_exists($file = CORE."/engines/$namespace/$class_name.php") ||    //Core engines
81 108
			file_exists($file = MODULES."/../$namespace/$class_name.php")         //Classes in modules
82
		) {
83 108
			$cache[$class] = realpath($file);
84 108
			__classes_put_into_cache('autoload', $cache);
85 108
			require_once $file;
86 108
			return true;
87
		}
88 8
		__classes_put_into_cache('autoload', $cache);
89
		// Processing components aliases
90 8
		if (strpos($namespace, 'modules') === 0) {
91 2
			$Config      = Config::instance();
92 2
			$directories = [];
93 2
			foreach ($Config->components['modules'] ?: [] as $module_name => $module_data) {
94 2
				if ($module_data['active'] == Config\Module_Properties::UNINSTALLED) {
95 2
					continue;
96
				}
97 2
				$directories[] = MODULES."/$module_name";
98
			}
99 2
			$class_exploded = explode('\\', $class);
100 2
			foreach ($directories as $directory) {
101 2
				if (file_exists("$directory/meta.json")) {
102 2
					$meta = file_get_json("$directory/meta.json") + ['provide' => []];
103 2
					if ($class_exploded[2] != $meta['package'] && in_array($class_exploded[2], (array)$meta['provide'])) {
104 2
						$class_exploded[2] = $meta['package'];
105 2
						$alias             = implode('\\', $class_exploded);
106 2
						$aliases[$class]   = $alias;
107 2
						__classes_put_into_cache('aliases', $aliases);
108 2
						spl_autoload_call($alias);
109 2
						return class_exists($class, false) || (class_exists($alias, false) && class_alias($alias, $class));
110
					}
111
				}
112
			}
113
		}
114 8
		return false;
115 194
	}
116
);
117
118
/**
119
 * Get or set modified classes (used in Singleton trait)
120
 *
121
 * @param array|null $updated_modified_classes
122
 *
123
 * @return array
124
 */
125
function modified_classes ($updated_modified_classes = null) {
126 144
	static $modified_classes;
127 144
	if (!isset($modified_classes)) {
128 144
		$modified_classes = __classes_get_from_cache('modified');
129
	}
130 144
	if ($updated_modified_classes) {
131 72
		$modified_classes = $updated_modified_classes;
132 72
		__classes_put_into_cache('modified', $modified_classes);
133
	}
134 144
	return $modified_classes;
135
}
136
137
/**
138
 * Easy getting of translations
139
 *
140
 * @param string  $item
141
 * @param mixed[] $arguments There can be any necessary number of arguments here
142
 *
143
 * @return string
144
 */
145
function __ ($item, ...$arguments) {
146
	$L = Language::instance();
147
	if (func_num_args() > 1) {
148
		return $L->format($item, ...$arguments);
149
	} else {
150
		return $L->$item;
151
	}
152
}
153
154
/**
155
 * Get file url by it's destination in file system
156
 *
157
 * @param string $source
158
 *
159
 * @return false|string
160
 */
161
function url_by_source ($source) {
162 2
	$Config = Config::instance(true);
163 2
	if (!$Config) {
164
		return false;
165
	}
166 2
	$source = realpath($source);
167 2
	if (mb_strpos($source, DIR) === 0) {
168 2
		return $Config->core_url().mb_substr($source, mb_strlen(DIR));
169
	}
170
	return false;
171
}
172
173
/**
174
 * Get file destination in file system by it's url
175
 *
176
 * @param string $url
177
 *
178
 * @return false|string
179
 */
180
function source_by_url ($url) {
181 2
	$Config = Config::instance(true);
182 2
	if (!$Config) {
183
		return false;
184
	}
185 2
	if (mb_strpos($url, $Config->core_url()) === 0) {
186 2
		return DIR.mb_substr($url, mb_strlen($Config->core_url()));
187
	}
188
	return false;
189
}
190
191
/**
192
 * Public cache cleaning
193
 *
194
 * @return bool
195
 */
196
function clean_pcache () {
197
	$ok   = true;
198
	$list = get_files_list(PUBLIC_CACHE, false, 'fd', true, true, 'name|desc');
199
	foreach ($list as $item) {
200
		if (is_writable($item)) {
201
			is_dir($item) ? @rmdir($item) : @unlink($item);
202
		} else {
203
			$ok = false;
204
		}
205
	}
206
	return $ok;
207
}
208
209
/**
210
 * Formatting of time in seconds to human-readable form
211
 *
212
 * @param int $time Time in seconds
213
 *
214
 * @return string
215
 */
216
function format_time ($time) {
217
	if (!is_numeric($time)) {
218
		return $time;
219
	}
220
	$L   = Language::instance();
221
	$res = [];
222
	if ($time >= 31536000) {
223
		$time_x = round($time / 31536000);
224
		$time -= $time_x * 31536000;
225
		$res[] = $L->time($time_x, 'y');
226
	}
227
	if ($time >= 2592000) {
228
		$time_x = round($time / 2592000);
229
		$time -= $time_x * 2592000;
230
		$res[] = $L->time($time_x, 'M');
231
	}
232
	if ($time >= 86400) {
233
		$time_x = round($time / 86400);
234
		$time -= $time_x * 86400;
235
		$res[] = $L->time($time_x, 'd');
236
	}
237
	if ($time >= 3600) {
238
		$time_x = round($time / 3600);
239
		$time -= $time_x * 3600;
240
		$res[] = $L->time($time_x, 'h');
241
	}
242
	if ($time >= 60) {
243
		$time_x = round($time / 60);
244
		$time -= $time_x * 60;
245
		$res[] = $L->time($time_x, 'm');
246
	}
247
	if ($time > 0 || empty($res)) {
248
		$res[] = $L->time($time, 's');
249
	}
250
	return implode(' ', $res);
251
}
252
253
/**
254
 * Formatting of data size in bytes to human-readable form
255
 *
256
 * @param int      $size
257
 * @param bool|int $round
258
 *
259
 * @return float|string
260
 */
261
function format_filesize ($size, $round = false) {
262
	if (!is_numeric($size)) {
263
		return $size;
264
	}
265
	$L    = Language::prefix('system_filesize_');
266
	$unit = '';
267
	if ($size >= 1099511627776) {
268
		$size /= 1099511627776;
269
		$unit = " $L->TiB";
270
	} elseif ($size >= 1073741824) {
271
		$size /= 1073741824;
272
		$unit = " $L->GiB";
273
	} elseif ($size >= 1048576) {
274
		$size /= 1048576;
275
		$unit = " $L->MiB";
276
	} elseif ($size >= 1024) {
277
		$size /= 1024;
278
		$unit = " $L->KiB";
279
	} else {
280
		$size = "$size $L->Bytes";
281
	}
282
	return $round ? round($size, $round).$unit : $size.$unit;
283
}
284
285
/**
286
 * Get list of timezones
287
 *
288
 * @return array
289
 */
290
function get_timezones_list () {
291 8
	$timezones = [];
292 8
	foreach (timezone_identifiers_list() as $timezone) {
293 8
		$offset          = (new DateTimeZone($timezone))->getOffset(new DateTime);
294 8
		$key             = (39600 + $offset).$timezone;
295 8
		$sign            = ($offset < 0 ? '-' : '+');
296 8
		$hours           = str_pad(floor(abs($offset / 3600)), 2, 0, STR_PAD_LEFT);
297 8
		$minutes         = str_pad(abs(($offset % 3600) / 60), 2, 0, STR_PAD_LEFT);
298 8
		$timezones[$key] = [
299 8
			'key'   => str_replace('_', ' ', $timezone)." ($sign$hours:$minutes)",
300 8
			'value' => $timezone
301
		];
302
	}
303 8
	ksort($timezones, SORT_NATURAL);
304 8
	return array_column($timezones, 'value', 'key');
305
}
306
307
/**
308
 * String representation of HTTP status code
309
 *
310
 * @param int $code
311
 *
312
 * @return null|string
313
 */
314
function status_code_string ($code) {
315
	$code_to_string = [
316 14
		201 => '201 Created',
317
		202 => '202 Accepted',
318
		301 => '301 Moved Permanently',
319
		302 => '302 Found',
320
		303 => '303 See Other',
321
		307 => '307 Temporary Redirect',
322
		400 => '400 Bad Request',
323
		403 => '403 Forbidden',
324
		404 => '404 Not Found',
325
		405 => '405 Method Not Allowed',
326
		409 => '409 Conflict',
327
		429 => '429 Too Many Requests',
328
		500 => '500 Internal Server Error',
329
		501 => '501 Not Implemented',
330
		503 => '503 Service Unavailable'
331
	];
332 14
	return @$code_to_string[$code];
333
}
334
335
/**
336
 * Pages navigation based on links
337
 *
338
 * @param int             $page       Current page
339
 * @param int             $total      Total pages number
340
 * @param callable|string $url        if string - it will be formatted with sprintf with one parameter - page number<br>
341
 *                                    if callable - one parameter will be given, callable should return url string
342
 * @param bool            $head_links If <b>true</b> - links with rel="prev" and rel="next" will be added
343
 *
344
 * @return bool|string <b>false</b> if single page, otherwise string, set of navigation links
345
 */
346
function pages ($page, $total, $url, $head_links = false) {
347 2
	if ($total == 1) {
348 2
		return false;
349
	}
350 2
	$Page             = Page::instance();
351 2
	$original_url     = $url;
352 2
	$base_url         = Config::instance()->base_url();
353
	$url              = function ($page) use ($original_url, $base_url) {
354 2
		$href = is_callable($original_url) ? $original_url($page) : sprintf($original_url, $page);
355 2
		if (is_string($href) && strpos($href, 'http') !== 0) {
356 2
			$href = ltrim($href, '/');
357 2
			$href = "$base_url/$href";
358
		}
359 2
		return $href;
360 2
	};
361 2
	$output           = [];
362
	$render_page_item = function ($i) use ($Page, $page, $url, $head_links, &$output) {
363 2
		$href = $url($i);
364 2
		if ($head_links) {
365
			switch ($i) {
366 2
				case $page - 1:
367 2
					$Page->link(['href' => $href, 'rel' => 'prev']);
368 2
					break;
369 2
				case $page + 1:
370 2
					$Page->link(['href' => $href, 'rel' => 'next']);
371 2
					break;
372 2
				case $page:
373 2
					$Page->canonical_url($href);
374 2
					break;
375
			}
376
		}
377 2
		$output[] = [
378 2
			$i,
379
			[
380 2
				'href'    => $i == $page ? false : $href,
381 2
				'is'      => 'cs-link-button',
382 2
				'primary' => $i == $page
383
			]
384
		];
385 2
	};
386 2
	if ($total <= 11) {
387 2
		array_map($render_page_item, range(1, $total));
388
	} else {
389 2
		if ($page <= 6) {
390 2
			array_map($render_page_item, range(1, 7));
391 2
			$output[] = [
392
				'...',
393
				[
394
					'disabled' => true
395
				]
396
			];
397 2
			array_map($render_page_item, range($total - 2, $total));
398 2
		} elseif ($page >= $total - 5) {
399 2
			array_map($render_page_item, range(1, 3));
400 2
			$output[] = [
401
				'...',
402
				[
403
					'disabled' => true
404
				]
405
			];
406 2
			array_map($render_page_item, range($total - 6, $total));
407
		} else {
408 2
			array_map($render_page_item, range(1, 2));
409 2
			$output[] = [
410
				'...',
411
				[
412
					'disabled' => true
413
				]
414
			];
415 2
			array_map($render_page_item, range($page - 2, $page + 2));
416 2
			$output[] = [
417
				'...',
418
				[
419
					'disabled' => true
420
				]
421
			];
422 2
			array_map($render_page_item, range($total - 1, $total));
423
		}
424
	}
425 2
	return h::{'a[is=cs-link-button]'}($output);
426
}
427
428
/**
429
 * Pages navigation based on buttons (for search forms, etc.)
430
 *
431
 * @param int                  $page  Current page
432
 * @param int                  $total Total pages number
433
 * @param bool|callable|string $url   Adds <i>formaction</i> parameter to every button<br>
434
 *                                    if <b>false</b> - only form parameter <i>page</i> will we added<br>
435
 *                                    if string - it will be formatted with sprintf with one parameter - page number<br>
436
 *                                    if callable - one parameter will be given, callable should return url string
437
 *
438
 * @return false|string                        <b>false</b> if single page, otherwise string, set of navigation buttons
439
 */
440
function pages_buttons ($page, $total, $url = false) {
441 2
	if ($total == 1) {
442 2
		return false;
443
	}
444 2
	if (!is_callable($url)) {
445 2
		$original_url = $url;
446
		$url          = function ($page) use ($original_url) {
447 2
			return sprintf($original_url, $page);
448 2
		};
449
	}
450 2
	$output           = [];
451
	$render_page_item = function ($i) use ($page, $url, &$output) {
452 2
		$output[] = [
453 2
			$i,
454
			[
455 2
				'formaction' => $i == $page || $url === false ? false : $url($i),
456 2
				'value'      => $i == $page ? false : $i,
457 2
				'type'       => $i == $page ? 'button' : 'submit',
458 2
				'primary'    => $i == $page
459
			]
460
		];
461 2
	};
462 2
	if ($total <= 11) {
463 2
		array_map($render_page_item, range(1, $total));
464
	} else {
465 2
		if ($page <= 6) {
466 2
			array_map($render_page_item, range(1, 7));
467 2
			$output[] = [
468
				'...',
469
				[
470
					'type' => 'button',
471
					'disabled'
472
				]
473
			];
474 2
			array_map($render_page_item, range($total - 2, $total));
475 2
		} elseif ($page >= $total - 5) {
476 2
			array_map($render_page_item, range(1, 3));
477 2
			$output[] = [
478
				'...',
479
				[
480
					'type' => 'button',
481
					'disabled'
482
				]
483
			];
484 2
			array_map($render_page_item, range($total - 6, $total));
485
		} else {
486 2
			array_map($render_page_item, range(1, 2));
487 2
			$output[] = [
488
				'...',
489
				[
490
					'type' => 'button',
491
					'disabled'
492
				]
493
			];
494 2
			array_map($render_page_item, range($page - 2, $page + 2));
495 2
			$output[] = [
496
				'...',
497
				[
498
					'type' => 'button',
499
					'disabled'
500
				]
501
			];
502 2
			array_map($render_page_item, range($total - 1, $total));
503
		}
504
	}
505 2
	return h::{'button[is=cs-button][name=page]'}($output);
506
}
507
508
/**
509
 * Checks whether specified functionality available or not
510
 *
511
 * @param string|string[] $functionality One functionality or array of them
512
 *
513
 * @return bool `true` if all functionality available, `false` otherwise
514
 */
515
function functionality ($functionality) {
516 2
	if (is_array($functionality)) {
517 2
		$result = true;
518 2
		foreach ($functionality as $f) {
519 2
			$result = $result && functionality($f);
520
		}
521 2
		return $result;
522
	}
523 2
	$all = Cache::instance()->get(
524 2
		'functionality',
525
		function () {
526 2
			$functionality = [];
527 2
			$Config        = Config::instance();
528 2
			foreach (array_keys($Config->components['modules']) as $module) {
529 2
				if (!$Config->module($module)->enabled() || !file_exists(MODULES."/$module/meta.json")) {
530 2
					continue;
531
				}
532 2
				$functionality[] = [$module];
533 2
				$meta            = file_get_json(MODULES."/$module/meta.json");
534 2
				if (isset($meta['provide'])) {
535 2
					$functionality[] = (array)$meta['provide'];
536
				}
537
			}
538 2
			return array_merge(...$functionality);
539 2
		}
540
	);
541 2
	return in_array($functionality, $all);
542
}
543