Completed
Push — master ( 364b6f...511e9a )
by Nazar
04:08
created

functions.php ➔ status_code_string()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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