$v) {
$s[$k] = htmlescape($v);
}
return $s;
}
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
function sizeString(int $size): string {
$ks = ['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 = [
'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 = [
'Щ', 'щ', 'Ё', 'Ж', 'Х', 'Ц', 'Ч', 'Ш', 'Ю', 'я', 'ё', 'ж', 'х', 'ц',
'ч', 'ш', 'ю', 'я', 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'З', 'И', 'Й', 'К',
'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Ь', 'Ы', 'Ъ', 'Э',
'а', 'б', 'в', 'г', 'д', 'е', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о',
'п', 'р', 'с', 'т', 'у', 'ф', 'ь', 'ы', 'ъ', 'э'
];
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 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 .= ''.htmlescape(mb_substr($s, $start_pos, $len, $charset)).'';
$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 = []) {
$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;
}