Completed
Push — master ( d70dad...31122f )
by Nazar
04:40
created

functions.php ➔ xap()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 51
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 7.0178

Importance

Changes 0
Metric Value
cc 7
eloc 37
nc 9
nop 3
dl 0
loc 51
ccs 26
cts 28
cp 0.9286
crap 7.0178
rs 6.9743
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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