Completed
Push — master ( 62ba97...e9b6cb )
by Nazar
04:41 queued 29s
created

functions.php ➔ clean_pcache()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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