4in1_ws_web/functions.php

395 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
function verifyHostname(?string $host = null): void {
global $config;
if ($host === null) {
$host = $_SERVER['HTTP_HOST'];
// IE moment
if (($pos = strpos($host, ':')) !== false)
$host = substr($host, 0, $pos);
}
if (!str_ends_with($host, $config['domain']))
throw new RuntimeException('invalid http_host '.$host);
if (strlen($host) > ($orig_domain_len = strlen($config['domain']))) {
$sub = substr($host, 0, -$orig_domain_len-1);
if (in_array($sub, $config['dev_domains'])) {
$config['is_dev'] = true;
} else if (!in_array($sub, $config['subdomains'])) {
throw new RuntimeException('invalid subdomain '.$sub);
}
}
if (isCli() && str_ends_with(__DIR__, 'www-dev'))
$config['is_dev'] = true;
}
function htmlescape(string|array $s): string|array {
if (is_array($s)) {
foreach ($s as $k => $v) {
$s[$k] = htmlescape($v);
}
return $s;
}
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
function sizeString(int $size): string {
$ks = array('B', 'KiB', 'MiB', 'GiB');
foreach ($ks as $i => $k) {
if ($size < pow(1024, $i + 1)) {
if ($i == 0)
return $size . ' ' . $k;
return round($size / pow(1024, $i), 2).' '.$k;
}
}
return $size;
}
function extension(string $name): string {
$expl = explode('.', $name);
return end($expl);
}
/**
* @param string $filename
* @return resource|bool
*/
function imageopen(string $filename) {
$size = getimagesize($filename);
$types = [
1 => 'gif',
2 => 'jpeg',
3 => 'png'
];
if (!$size || !isset($types[$size[2]]))
return null;
return call_user_func('imagecreatefrom'.$types[$size[2]], $filename);
}
function detectImageType(string $filename) {
$size = getimagesize($filename);
$types = [
1 => 'gif',
2 => 'jpg',
3 => 'png'
];
if (!$size || !isset($types[$size[2]]))
return false;
return $types[$size[2]];
}
function transliterate(string $string): string {
$roman = array(
'Sch', 'sch', 'Yo', 'Zh', 'Kh', 'Ts', 'Ch', 'Sh', 'Yu', 'ya', 'yo',
'zh', 'kh', 'ts', 'ch', 'sh', 'yu', 'ya', 'A', 'B', 'V', 'G', 'D', 'E',
'Z', 'I', 'Y', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F',
'', 'Y', '', 'E', 'a', 'b', 'v', 'g', 'd', 'e', 'z', 'i', 'y', 'k',
'l', 'm', 'n', 'o', 'p', 'r', 's', 't', 'u', 'f', '', 'y', '', 'e'
);
$cyrillic = array(
'Щ', 'щ', 'Ё', 'Ж', 'Х', 'Ц', 'Ч', 'Ш', 'Ю', 'я', 'ё', 'ж', 'х', 'ц',
'ч', 'ш', 'ю', 'я', 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'З', 'И', 'Й', 'К',
'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Ь', 'Ы', 'Ъ', 'Э',
'а', 'б', 'в', 'г', 'д', 'е', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о',
'п', 'р', 'с', 'т', 'у', 'ф', 'ь', 'ы', 'ъ', 'э'
);
return str_replace($cyrillic, $roman, $string);
}
/**
* @param resource $img
* @param ?int $w
* @param ?int $h
* @param ?int[] $transparent_color
*/
function imageresize(&$img, ?int $w = null, ?int $h = null, ?array $transparent_color = null) {
assert(is_int($w) || is_int($h));
$curw = imagesx($img);
$curh = imagesy($img);
if (!is_int($w) && is_int($h)) {
$w = round($curw / ($curw / $w));
} else if (is_int($w) && !is_int($h)) {
$h = round($curh / ($curh / $h));
}
$img2 = imagecreatetruecolor($w, $h);
if (is_array($transparent_color)) {
list($r, $g, $b) = $transparent_color;
$col = imagecolorallocate($img2, $r, $g, $b);
imagefilledrectangle($img2, 0, 0, $w, $h, $col);
} else {
imagealphablending($img2, false);
imagesavealpha($img2, true);
imagefilledrectangle($img2, 0, 0, $w, $h, imagecolorallocatealpha($img2, 255, 255, 255, 127));
}
imagecopyresampled($img2, $img, 0, 0, 0, 0, $w, $h, $curw, $curh);
imagedestroy($img);
$img = $img2;
}
function rrmdir(string $dir, bool $dont_delete_dir = false): bool {
if (!is_dir($dir)) {
logError('rrmdir: '.$dir.' is not a directory');
return false;
}
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != '.' && $object != '..') {
if (is_dir($dir.'/'.$object)) {
rrmdir($dir.'/'.$object);
} else {
unlink($dir.'/'.$object);
}
}
}
if (!$dont_delete_dir)
rmdir($dir);
return true;
}
function ip2ulong(string $ip): int {
return sprintf("%u", ip2long($ip));
}
function ulong2ip(int $ip): string {
$long = 4294967295 - ($ip - 1);
return long2ip(-$long);
}
function fromCamelCase(string $s): string {
$buf = '';
$len = strlen($s);
for ($i = 0; $i < $len; $i++) {
if (!ctype_upper($s[$i])) {
$buf .= $s[$i];
} else {
$buf .= '_'.strtolower($s[$i]);
}
}
return $buf;
}
function toCamelCase(string $input, string $separator = '_'): string {
return lcfirst(str_replace($separator, '', ucwords($input, $separator)));
}
function strReplaceOnce(string $needle, string $replace, string $haystack) {
$pos = strpos($haystack, $needle);
if ($pos !== false)
$haystack = substr_replace($haystack, $replace, $pos, strlen($needle));
return $haystack;
}
function strgen(int $len): string {
$buf = '';
for ($i = 0; $i < $len; $i++) {
$j = mt_rand(0, 61);
if ($j >= 36) {
$j += 13;
} else if ($j >= 10) {
$j += 7;
}
$buf .= chr(48 + $j);
}
return $buf;
}
function sanitizeFilename(string $name): string {
$name = mb_strtolower($name);
$name = transliterate($name);
$name = preg_replace('/[^\w\d\-_\s.]/', '', $name);
$name = preg_replace('/\s+/', '_', $name);
return $name;
}
function setperm(string $file): void {
global $config;
// chgrp
$gid = filegroup($file);
if ($gid != $config['group']) {
if (!chgrp($file, $config['group'])) {
logError(__FUNCTION__.": chgrp() failed on $file");
}
}
// chmod
$perms = fileperms($file);
$need_perms = is_dir($file) ? $config['dirs_mode'] : $config['files_mode'];
if (($perms & $need_perms) !== $need_perms) {
if (!chmod($file, $need_perms)) {
logError(__FUNCTION__.": chmod() failed on $file");
}
}
}
function saltPassword(string $pwd): string {
global $config;
return hash('sha256', "{$pwd}|{$config['password_salt']}");
}
function exectime(?string $format = null): string|float {
$time = round(microtime(true) - START_TIME, 4);
if (!is_null($format))
$time = sprintf($format, $time);
return $time;
}
function formatNumber(int|float $num, string $delim = ' ', bool $short = false): string {
if ($short) {
if ($num >= 1000000)
return floor($num / 1000000).'m';
if ($num >= 1000)
return floor($num / 1000).'k';
}
return number_format($num, 0, '.', $delim);
}
function lang() {
global $__lang;
return call_user_func_array([$__lang, 'get'], func_get_args());
}
function langNum() {
global $__lang;
return call_user_func_array([$__lang, 'num'], func_get_args());
}
function isDev(): bool { global $config; return $config['is_dev']; }
function isCli(): bool { return PHP_SAPI == 'cli'; };
function isRetina(): bool { return isset($_COOKIE['is_retina']) && $_COOKIE['is_retina']; }
function isAdmin(): bool {
if (admin::getId() === null)
admin::check();
return admin::getId() != 0;
}
function jsonEncode($obj): ?string { return json_encode($obj, JSON_UNESCAPED_UNICODE) ?: null; }
function jsonDecode($json) { return json_decode($json, true); }
function pcreNoError(mixed &$result, bool $no_error = false): bool {
if ($result === null) {
if (preg_last_error() !== PREG_NO_ERROR) {
if (!$no_error)
logError('an error occurred while PCRE regex execution: '.preg_last_error_msg());
return false;
}
}
return true;
}
function highlightSubstring(string $s, string|array|null $keywords = []): string {
if (is_null($keywords))
return htmlescape($s);
if ($keywords instanceof Stringable)
$keywords = $keywords->__toString();
else if (is_array($keywords)) {
$keywords = array_map(function($s) {
if ($s instanceof Stringable)
return $s->__toString();
return $s;
}, $keywords);
}
if (is_string($keywords))
$keywords = preg_split('/\s+/', $keywords);
$charset = 'utf-8';
$s_len = mb_strlen($s, $charset);
usort($keywords, function($a, $b) use (&$charset) {
return mb_strlen($b, $charset) - mb_strlen($a, $charset);
});
$all = [];
foreach ($keywords as $kw) {
$kw_len = mb_strlen($kw, $charset);
$offset = 0;
while (($pos = mb_stripos($s, $kw, $offset, $charset)) !== false) {
$offset = $pos + 1;
$all[] = [$pos, $kw_len];
}
}
usort($all, function($a, $b) {
return $a[0] - $b[0];
});
$last_index = 0;
$buf = '';
foreach ($all as $range) {
list($start_pos, $len) = $range;
if ($start_pos < $last_index) {
continue;
}
if ($start_pos > $last_index) {
$buf .= htmlescape(mb_substr($s, $last_index, $start_pos - $last_index, $charset));
}
$buf .= '<span class="matched">'.htmlescape(mb_substr($s, $start_pos, $len, $charset)).'</span>';
$last_index = $start_pos + $len;
}
if ($last_index < $s_len) {
$buf .= htmlescape(mb_substr($s, $last_index, $s_len - $last_index, $charset));
}
return $buf;
}
function formatTime($ts, array $opts = array()) {
$default_opts = [
'date_only' => false,
'day_of_week' => false,
'seconds' => false,
'short_months' => false,
'skip_today_date' => false,
'skip_old_time' => false,
'at' => null,
];
$opts = array_merge($default_opts, $opts);
$is_today = date('Ymd') == date('Ymd', $ts);
$date = '';
$skip_old_time = false;
if (!$is_today || !$opts['skip_today_date']) {
$y = (int)date('Y', $ts);
$date .= date('j '.(!$opts['short_months'] ? 'F' : 'M'), $ts);
if ($y != (int)date('Y')) {
$date .= ' ' . $y;
if ($opts['skip_old_time']) {
$skip_old_time = true;
}
}
if ($opts['day_of_week']) {
$date .= ', ' . date('l', $ts);
}
}
if (!$opts['date_only'] && !$skip_old_time) {
if ($date != '') {
$date .= $opts['at'] ?? ' at ';
}
$date .= date('H:i', $ts);
if ($opts['seconds'])
$date .= date(':s', $ts);
}
return $date;
}