Completed
Push — master ( 77bd5e...c97259 )
by Nazar
04:13
created

functions.php ➔ format_time()   D

Complexity

Conditions 9
Paths 65

Size

Total Lines 36
Code Lines 28

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 9
eloc 28
nc 65
nop 1
dl 0
loc 36
rs 4.909
1
<?php
2
/**
3
 * @package   CleverStyle CMS
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
	cs\Text;
18
19
/**
20
 * Auto Loading of classes
21
 */
22
spl_autoload_register(
23
	function ($class) {
24
		static $cache, $aliases;
25
		if (!isset($cache)) {
26
			$cache   = file_exists(CACHE.'/classes/autoload') ? file_get_json(CACHE.'/classes/autoload') : [];
27
			$aliases = file_exists(CACHE.'/classes/aliases') ? file_get_json(CACHE.'/classes/aliases') : [];
28
		}
29
		if (isset($aliases[$class])) {
30
			spl_autoload_call($aliases[$class]);
31
			return class_exists($aliases[$class]) || class_alias($aliases[$class], $class);
32
		}
33
		if (isset($cache[$class])) {
34
			return $cache[$class] ? require_once $cache[$class] : false;
35
		}
36
		$prepared_class_name = ltrim($class, '\\');
37
		if (strpos($prepared_class_name, 'cs\\') === 0) {
38
			$prepared_class_name = substr($prepared_class_name, 3);
39
		}
40
		$prepared_class_name = explode('\\', $prepared_class_name);
41
		$namespace           = count($prepared_class_name) > 1 ? implode('/', array_slice($prepared_class_name, 0, -1)) : '';
42
		$class_name          = array_pop($prepared_class_name);
43
		$cache[$class]       = false;
44
		/** @noinspection MkdirRaceConditionInspection */
45
		@mkdir(CACHE.'/classes', 0770, true);
46
		/**
47
		 * Try to load classes from different places. If not found in one place - try in another.
48
		 */
49
		if (
50
			_require_once($file = DIR."/core/classes/$namespace/$class_name.php", false) ||    //Core classes
51
			_require_once($file = DIR."/core/thirdparty/$namespace/$class_name.php", false) || //Third party classes
52
			_require_once($file = DIR."/core/traits/$namespace/$class_name.php", false) ||     //Core traits
53
			_require_once($file = ENGINES."/$namespace/$class_name.php", false) ||             //Core engines
54
			_require_once($file = MODULES."/../$namespace/$class_name.php", false) ||          //Classes in modules
55
			_require_once($file = PLUGINS."/../$namespace/$class_name.php", false)             //Classes in plugins
56
		) {
57
			$cache[$class] = realpath($file);
58
			file_put_json(CACHE.'/classes/autoload', $cache);
59
			return (bool)$cache[$class];
60
		}
61
		// Processing components aliases
62
		if (strpos($namespace, 'modules') === 0 || strpos($namespace, 'plugins') === 0) {
63
			$Config      = Config::instance();
64
			$directories = [];
65
			foreach ($Config->components['modules'] ?: [] as $module_name => $module_data) {
66
				if ($module_data['active'] == Config\Module_Properties::UNINSTALLED) {
67
					continue;
68
				}
69
				$directories[] = MODULES."/$module_name";
70
			}
71
			foreach ($Config->components['plugins'] ?: [] as $plugin_name) {
72
				$directories[] = PLUGINS."/$plugin_name";
73
			}
74
			$class_exploded = explode('\\', $class);
75
			foreach ($directories as $directory) {
76
				if (file_exists("$directory/meta.json")) {
77
					$meta = file_get_json("$directory/meta.json") + ['provide' => []];
78
					if ($class_exploded[2] != $meta['package'] && in_array($class_exploded[2], (array)$meta['provide'])) {
79
						$class_exploded[2] = $meta['package'];
80
						$alias             = implode('\\', $class_exploded);
81
						$aliases[$class]   = $alias;
82
						file_put_json(CACHE.'/classes/aliases', $aliases);
83
						spl_autoload_call($alias);
84
						return class_exists($alias) || class_alias($alias, $class);
85
					}
86
				}
87
			}
88
		}
89
		return false;
90
	},
91
	true,
92
	true
93
);
94
95
/**
96
 * Clean cache of classes autoload and customization
97
 */
98
function clean_classes_cache () {
99
	@unlink(CACHE.'/classes/autoload');
100
	@unlink(CACHE.'/classes/aliases');
101
	@unlink(CACHE.'/classes/modified');
102
}
103
104
/**
105
 * Get or set modified classes (used in Singleton trait)
106
 *
107
 * @param array|null $updated_modified_classes
108
 *
109
 * @return array
110
 */
111
function modified_classes ($updated_modified_classes = null) {
112
	static $modified_classes;
113
	if (!defined('CACHE')) {
114
		return [];
115
	}
116
	/** @noinspection MkdirRaceConditionInspection */
117
	@mkdir(CACHE.'/classes', 0770, true);
118
	if (!isset($modified_classes)) {
119
		$modified_classes = file_exists(CACHE.'/classes/modified') ? file_get_json(CACHE.'/classes/modified') : [];
120
	}
121
	if ($updated_modified_classes) {
122
		$modified_classes = $updated_modified_classes;
123
		file_put_json(CACHE.'/classes/modified', $modified_classes);
124
	}
125
	return $modified_classes;
126
}
127
128
/**
129
 * Easy getting of translations
130
 *
131
 * @param string  $item
132
 * @param mixed[] $arguments There can be any necessary number of arguments here
133
 *
134
 * @return string
135
 */
136
function __ ($item, ...$arguments) {
137
	$L = Language::instance();
138
	if (func_num_args() > 1) {
139
		return $L->format($item, ...$arguments);
140
	} else {
141
		return $L->$item;
142
	}
143
}
144
145
/**
146
 * Get file url by it's destination in file system
147
 *
148
 * @param string $source
149
 *
150
 * @return false|string
151
 */
152
function url_by_source ($source) {
153
	$Config = Config::instance(true);
154
	if (!$Config) {
155
		return false;
156
	}
157
	$source = realpath($source);
158
	if (mb_strpos($source, DIR) === 0) {
159
		return $Config->core_url().mb_substr($source, mb_strlen(DIR));
160
	}
161
	return false;
162
}
163
164
/**
165
 * Get file destination in file system by it's url
166
 *
167
 * @param string $url
168
 *
169
 * @return false|string
170
 */
171
function source_by_url ($url) {
172
	$Config = Config::instance(true);
173
	if (!$Config) {
174
		return false;
175
	}
176
	if (mb_strpos($url, $Config->core_url()) === 0) {
177
		return DIR.mb_substr($url, mb_strlen($Config->core_url()));
178
	}
179
	return false;
180
}
181
182
/**
183
 * Public cache cleaning
184
 *
185
 * @return bool
186
 */
187
function clean_pcache () {
188
	$ok   = true;
189
	$list = get_files_list(PUBLIC_CACHE, false, 'fd', true, true, 'name|desc');
190
	foreach ($list as $item) {
191
		if (is_writable($item)) {
192
			is_dir($item) ? @rmdir($item) : @unlink($item);
193
		} else {
194
			$ok = false;
195
		}
196
	}
197
	return $ok;
198
}
199
200
/**
201
 * Formatting of time in seconds to human-readable form
202
 *
203
 * @param int $time Time in seconds
204
 *
205
 * @return string
206
 */
207
function format_time ($time) {
208
	if (!is_numeric($time)) {
209
		return $time;
210
	}
211
	$L   = Language::instance();
212
	$res = [];
213
	if ($time >= 31536000) {
214
		$time_x = round($time / 31536000);
215
		$time -= $time_x * 31536000;
216
		$res[] = $L->time($time_x, 'y');
217
	}
218
	if ($time >= 2592000) {
219
		$time_x = round($time / 2592000);
220
		$time -= $time_x * 2592000;
221
		$res[] = $L->time($time_x, 'M');
222
	}
223
	if ($time >= 86400) {
224
		$time_x = round($time / 86400);
225
		$time -= $time_x * 86400;
226
		$res[] = $L->time($time_x, 'd');
227
	}
228
	if ($time >= 3600) {
229
		$time_x = round($time / 3600);
230
		$time -= $time_x * 3600;
231
		$res[] = $L->time($time_x, 'h');
232
	}
233
	if ($time >= 60) {
234
		$time_x = round($time / 60);
235
		$time -= $time_x * 60;
236
		$res[] = $L->time($time_x, 'm');
237
	}
238
	if ($time > 0 || empty($res)) {
239
		$res[] = $L->time($time, 's');
240
	}
241
	return implode(' ', $res);
242
}
243
244
/**
245
 * Formatting of data size in bytes to human-readable form
246
 *
247
 * @param int      $size
248
 * @param bool|int $round
249
 *
250
 * @return float|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
251
 */
252
function format_filesize ($size, $round = false) {
253
	if (!is_numeric($size)) {
254
		return $size;
255
	}
256
	$L    = Language::prefix('system_filesize_');
257
	$unit = '';
258
	if ($size >= 1099511627776) {
259
		$size /= 1099511627776;
260
		$unit = " $L->TiB";
261
	} elseif ($size >= 1073741824) {
262
		$size /= 1073741824;
263
		$unit = " $L->GiB";
264
	} elseif ($size >= 1048576) {
265
		$size /= 1048576;
266
		$unit = " $L->MiB";
267
	} elseif ($size >= 1024) {
268
		$size /= 1024;
269
		$unit = " $L->KiB";
270
	} else {
271
		$size = "$size $L->Bytes";
272
	}
273
	return $round ? round($size, $round).$unit : $size.$unit;
274
}
275
276
/**
277
 * Get list of timezones
278
 *
279
 * @return array
280
 */
281
function get_timezones_list () {
282
	if (
283
		!class_exists('\\cs\\Cache', false) ||
284
		!($Cache = Cache::instance(true)) ||
285
		($timezones = $Cache->timezones) === false
286
	) {
287
		$tzs        = timezone_identifiers_list();
288
		$timezones_ = $timezones = [];
289
		foreach ($tzs as $tz) {
290
			$offset           = (new DateTimeZone($tz))->getOffset(new DateTime);
291
			$offset_          = ($offset < 0 ? '-' : '+').
292
								str_pad(floor(abs($offset / 3600)), 2, 0, STR_PAD_LEFT).':'.
293
								str_pad(abs(($offset % 3600) / 60), 2, 0, STR_PAD_LEFT);
294
			$key              = (39600 + $offset).$tz;
295
			$timezones_[$key] = [
296
				'key'   => strtr($tz, '_', ' ')." ($offset_)",
297
				'value' => $tz
298
			];
299
		}
300
		unset($tzs, $tz, $offset);
301
		ksort($timezones_, SORT_NATURAL);
302
		/**
303
		 * @var array $offset
304
		 */
305
		foreach ($timezones_ as $tz) {
306
			$timezones[$tz['key']] = $tz['value'];
307
		}
308
		unset($timezones_, $tz);
309
		/** @noinspection NotOptimalIfConditionsInspection */
310
		if (isset($Cache) && $Cache) {
311
			$Cache->timezones = $timezones;
312
		}
313
	}
314
	return $timezones;
315
}
316
317
/**
318
 * Get multilingual value from $Config->core array
319
 *
320
 * @param string $item
321
 *
322
 * @return false|string
323
 */
324
function get_core_ml_text ($item) {
325
	$Config = Config::instance(true);
326
	if (!$Config) {
327
		return false;
328
	}
329
	return Text::instance()->process($Config->module('System')->db('texts'), $Config->core[$item], true);
330
}
331
332
/**
333
 * Set multilingual value from $Config->core array
334
 *
335
 * @param string $item
336
 * @param string $value
337
 *
338
 * @return false|string
339
 */
340
function set_core_ml_text ($item, $value) {
341
	$Config = Config::instance(true);
342
	if (!$Config || !isset($Config->core[$item])) {
343
		return false;
344
	}
345
	return Text::instance()->set($Config->module('System')->db('texts'), 'System/Config/core', $item, $value);
346
}
347
348
/**
349
 * String representation of HTTP status code
350
 *
351
 * @param int $code
352
 *
353
 * @return null|string
354
 */
355
function status_code_string ($code) {
356
	switch ($code) {
357
		case 201:
358
			$string_code = '201 Created';
359
			break;
360
		case 202:
361
			$string_code = '202 Accepted';
362
			break;
363
		case 301:
364
			$string_code = '301 Moved Permanently';
365
			break;
366
		case 302:
367
			$string_code = '302 Found';
368
			break;
369
		case 303:
370
			$string_code = '303 See Other';
371
			break;
372
		case 307:
373
			$string_code = '307 Temporary Redirect';
374
			break;
375
		case 400:
376
			$string_code = '400 Bad Request';
377
			break;
378
		case 403:
379
			$string_code = '403 Forbidden';
380
			break;
381
		case 404:
382
			$string_code = '404 Not Found';
383
			break;
384
		case 405:
385
			$string_code = '405 Method Not Allowed';
386
			break;
387
		case 409:
388
			$string_code = '409 Conflict';
389
			break;
390
		case 429:
391
			$string_code = '429 Too Many Requests';
392
			break;
393
		case 500:
394
			$string_code = '500 Internal Server Error';
395
			break;
396
		case 501:
397
			$string_code = '501 Not Implemented';
398
			break;
399
		case 503:
400
			$string_code = '503 Service Unavailable';
401
			break;
402
		default:
403
			return null;
404
	}
405
	return $string_code;
406
}
407
408
/**
409
 * Pages navigation based on links
410
 *
411
 * @param int             $page       Current page
412
 * @param int             $total      Total pages number
413
 * @param callable|string $url        if string - it will be formatted with sprintf with one parameter - page number<br>
414
 *                                    if callable - one parameter will be given, callable should return url string
415
 * @param bool            $head_links If <b>true</b> - links with rel="prev" and rel="next" will be added
416
 *
417
 * @return bool|string <b>false</b> if single page, otherwise string, set of navigation links
418
 */
419
function pages ($page, $total, $url, $head_links = false) {
420
	if ($total == 1) {
421
		return false;
422
	}
423
	$Page   = Page::instance();
424
	$output = [];
425
	if (is_callable($url)) {
426
		$url_func = $url;
427
	} else {
428
		$original_url = $url;
429
		$url_func     = function ($page) use ($original_url) {
430
			return sprintf($original_url, $page);
431
		};
432
	}
433
	$base_url          = Config::instance()->base_url();
434
	$url               = function ($page) use ($url_func, $base_url) {
435
		$url = $url_func($page);
436
		if (is_string($url) && strpos($url, 'http') !== 0) {
437
			$url = ltrim($url, '/');
438
			$url = "$base_url/$url";
439
		}
440
		return $url;
441
	};
442
	$render_head_links = function ($i) use ($Page, $page, $url) {
443
		$href = is_callable($url) ? $url($i) : sprintf($url, $i);
444
		switch ($i) {
445
			case $page - 1:
446
				$rel = 'prev';
447
				break;
448
			case $page + 1:
449
				$rel = 'next';
450
				break;
451
			case $page:
452
				$Page->canonical_url($href);
453
			/**
454
			 * This is not a mistake, break is not needed here
455
			 */
456
			default:
457
				return;
458
		}
459
		$Page->link(
460
			[
461
				'href' => $href,
462
				'rel'  => $rel
463
			]
464
		);
465
	};
466
	$render_page_item  = function ($i) use ($page, $url, $head_links, $render_head_links) {
467
		if ($head_links) {
468
			$render_head_links($i);
469
		}
470
		return [
471
			$i,
472
			[
473
				'href'    => $i == $page ? false : $url($i),
474
				'is'      => 'cs-link-button',
475
				'primary' => $i == $page
476
			]
477
		];
478
	};
479
	if ($total <= 11) {
480
		for ($i = 1; $i <= $total; ++$i) {
481
			$output[] = $render_page_item($i);
482
		}
483
	} else {
484
		if ($page <= 6) {
485
			for ($i = 1; $i <= 7; ++$i) {
486
				$output[] = $render_page_item($i);
487
			}
488
			$output[] = [
489
				'...',
490
				[
491
					'is' => 'cs-link-button',
492
					'disabled'
493
				]
494
			];
495
			for ($i = $total - 2; $i <= $total; ++$i) {
496
				$output[] = $render_page_item($i);
497
			}
498
		} elseif ($page >= $total - 5) {
499
			for ($i = 1; $i <= 3; ++$i) {
500
				$output[] = $render_page_item($i);
501
			}
502
			$output[] = [
503
				'...',
504
				[
505
					'is' => 'cs-link-button',
506
					'disabled'
507
				]
508
			];
509
			for ($i = $total - 6; $i <= $total; ++$i) {
510
				$output[] = $render_page_item($i);
511
			}
512
		} else {
513
			for ($i = 1; $i <= 2; ++$i) {
514
				$output[] = $render_page_item($i);
515
			}
516
			$output[] = [
517
				'...',
518
				[
519
					'is' => 'cs-link-button',
520
					'disabled'
521
				]
522
			];
523
			for ($i = $page - 2; $i <= $page + 2; ++$i) {
524
				$output[] = $render_page_item($i);
525
			}
526
			$output[] = [
527
				'...',
528
				[
529
					'is' => 'cs-link-button',
530
					'disabled'
531
				]
532
			];
533
			for ($i = $total - 1; $i <= $total; ++$i) {
534
				$output[] = $render_page_item($i);
535
			}
536
		}
537
	}
538
	return h::a($output);
539
}
540
541
/**
542
 * Pages navigation based on buttons (for search forms, etc.)
543
 *
544
 * @param int                  $page  Current page
545
 * @param int                  $total Total pages number
546
 * @param bool|callable|string $url   Adds <i>formaction</i> parameter to every button<br>
547
 *                                    if <b>false</b> - only form parameter <i>page</i> will we added<br>
548
 *                                    if string - it will be formatted with sprintf with one parameter - page number<br>
549
 *                                    if callable - one parameter will be given, callable should return url string
550
 *
551
 * @return false|string                        <b>false</b> if single page, otherwise string, set of navigation buttons
552
 */
553
function pages_buttons ($page, $total, $url = false) {
554
	if ($total == 1) {
555
		return false;
556
	}
557
	$output = [];
558
	if (!is_callable($url)) {
559
		$original_url = $url;
560
		$url          = function ($page) use ($original_url) {
561
			return sprintf($original_url, $page);
562
		};
563
	}
564
	if ($total <= 11) {
565
		for ($i = 1; $i <= $total; ++$i) {
566
			$output[] = [
567
				$i,
568
				[
569
					'is'         => 'cs-button',
570
					'formaction' => $i == $page || $url === false ? false : $url($i),
571
					'value'      => $i,
572
					'type'       => $i == $page ? 'button' : 'submit',
573
					'primary'    => $i == $page
574
				]
575
			];
576
		}
577
	} else {
578
		if ($page <= 6) {
579
			for ($i = 1; $i <= 7; ++$i) {
580
				$output[] = [
581
					$i,
582
					[
583
						'is'         => 'cs-button',
584
						'formaction' => $i == $page || $url === false ? false : $url($i),
585
						'value'      => $i == $page ? false : $i,
586
						'type'       => $i == $page ? 'button' : 'submit',
587
						'primary'    => $i == $page
588
					]
589
				];
590
			}
591
			$output[] = [
592
				'...',
593
				[
594
					'is'   => 'cs-button',
595
					'type' => 'button',
596
					'disabled'
597
				]
598
			];
599
			for ($i = $total - 2; $i <= $total; ++$i) {
600
				$output[] = [
601
					$i,
602
					[
603
						'is'         => 'cs-button',
604
						'formaction' => is_callable($url) ? $url($i) : sprintf($url, $i),
605
						'value'      => $i,
606
						'type'       => 'submit'
607
					]
608
				];
609
			}
610
		} elseif ($page >= $total - 5) {
611
			for ($i = 1; $i <= 3; ++$i) {
612
				$output[] = [
613
					$i,
614
					[
615
						'is'         => 'cs-button',
616
						'formaction' => is_callable($url) ? $url($i) : sprintf($url, $i),
617
						'value'      => $i,
618
						'type'       => 'submit'
619
					]
620
				];
621
			}
622
			$output[] = [
623
				'...',
624
				[
625
					'is'   => 'cs-button',
626
					'type' => 'button',
627
					'disabled'
628
				]
629
			];
630
			for ($i = $total - 6; $i <= $total; ++$i) {
631
				$output[] = [
632
					$i,
633
					[
634
						'is'         => 'cs-button',
635
						'formaction' => $i == $page || $url === false ? false : $url($i),
636
						'value'      => $i == $page ? false : $i,
637
						'type'       => $i == $page ? 'button' : 'submit',
638
						'primary'    => $i == $page
639
					]
640
				];
641
			}
642
		} else {
643
			for ($i = 1; $i <= 2; ++$i) {
644
				$output[] = [
645
					$i,
646
					[
647
						'is'         => 'cs-button',
648
						'formaction' => is_callable($url) ? $url($i) : sprintf($url, $i),
649
						'value'      => $i,
650
						'type'       => 'submit'
651
					]
652
				];
653
			}
654
			$output[] = [
655
				'...',
656
				[
657
					'is'   => 'cs-button',
658
					'type' => 'button',
659
					'disabled'
660
				]
661
			];
662
			for ($i = $page - 2; $i <= $page + 2; ++$i) {
663
				$output[] = [
664
					$i,
665
					[
666
						'is'         => 'cs-button',
667
						'formaction' => $i == $page || $url === false ? false : $url($i),
668
						'value'      => $i == $page ? false : $i,
669
						'type'       => $i == $page ? 'button' : 'submit',
670
						'primary'    => $i == $page
671
					]
672
				];
673
			}
674
			$output[] = [
675
				'...',
676
				[
677
					'is'   => 'cs-button',
678
					'type' => 'button',
679
					'disabled'
680
				]
681
			];
682
			for ($i = $total - 1; $i <= $total; ++$i) {
683
				$output[] = [
684
					$i,
685
					[
686
						'is'         => 'cs-button',
687
						'formaction' => is_callable($url) ? $url($i) : sprintf($url, $i),
688
						'value'      => $i,
689
						'type'       => 'submit'
690
					]
691
				];
692
			}
693
		}
694
	}
695
	return h::{'button[is=cs-button][name=page]'}($output);
696
}
697
698
/**
699
 * Checks whether specified functionality available or not
700
 *
701
 * @param string|string[] $functionality One functionality or array of them
702
 *
703
 * @return bool `true` if all functionality available, `false` otherwise
704
 */
705
function functionality ($functionality) {
706
	if (is_array($functionality)) {
707
		$result = true;
708
		foreach ($functionality as $f) {
709
			$result = $result && functionality($f);
710
		}
711
		return $result;
712
	}
713
	$all = Cache::instance()->get(
714
		'functionality',
715
		function () {
716
			$functionality = [];
717
			$Config        = Config::instance();
718
			$components    = $Config->components;
719
			foreach (array_keys($components['modules']) as $module) {
720
				if (!$Config->module($module)->enabled() || !file_exists(MODULES."/$module/meta.json")) {
721
					continue;
722
				}
723
				$functionality[] = [$module];
724
				$meta            = file_get_json(MODULES."/$module/meta.json");
725
				if (isset($meta['provide'])) {
726
					$functionality[] = (array)$meta['provide'];
727
				}
728
			}
729
			foreach ($components['plugins'] as $plugin) {
730
				if (!file_exists(PLUGINS."/$plugin/meta.json")) {
731
					continue;
732
				}
733
				$functionality[] = [$plugin];
734
				$meta            = file_get_json(PLUGINS."/$plugin/meta.json");
735
				if (isset($meta['provide'])) {
736
					$functionality[] = (array)$meta['provide'];
737
				}
738
			}
739
			return array_merge(...$functionality);
740
		}
741
	);
742
	return in_array($functionality, $all);
743
}
744