($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 (is_cli() && str_ends_with(dirname(__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 strtrim(string $str, int $len, bool &$trimmed): string { if (mb_strlen($str) > $len) { $str = mb_substr($str, 0, $len); $trimmed = true; } else { $trimmed = false; } return $str; } 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 detect_image_type(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 from_camel_case(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 to_camel_case(string $input, string $separator = '_'): string { return lcfirst(str_replace($separator, '', ucwords($input, $separator))); } function str_replace_once(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 sanitize_filename(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 glob_escape(string $pattern): string { if (strpos($pattern, '[') !== false || strpos($pattern, ']') !== false) { $placeholder = uniqid(); $replaces = array( $placeholder.'[', $placeholder.']', ); $pattern = str_replace( array('[', ']', ), $replaces, $pattern); $pattern = str_replace( $replaces, array('[[]', '[]]', ), $pattern); } return $pattern; } /** * Does not support flag GLOB_BRACE * * @param string $pattern * @param int $flags * @return array */ function glob_recursive(string $pattern, int $flags = 0): array { $files = glob(glob_escape($pattern), $flags); foreach (glob(glob_escape(dirname($pattern)).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { $files = array_merge($files, glob_recursive($dir.'/'.basename($pattern), $flags)); } return $files; } 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 salt_password(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 is_dev(): bool { global $config; return $config['is_dev']; } function is_cli(): bool { return PHP_SAPI == 'cli'; }; function is_retina(): bool { return isset($_COOKIE['is_retina']) && $_COOKIE['is_retina']; } function jsonEncode($obj): ?string { return json_encode($obj, JSON_UNESCAPED_UNICODE) ?: null; } function jsonDecode($json) { return json_decode($json, true); }