From: Banana Date: Mon, 9 Oct 2023 12:53:57 +0000 (+0200) Subject: cleanup to ssummoner X-Git-Tag: 1.6~23 X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=f364f099c218b15533157d8a50278b005abe392c;p=bibliotheca-php.git cleanup to ssummoner --- diff --git a/webclient/lib/summoner.class.php b/webclient/lib/summoner.class.php index 054747b..72d92ba 100644 --- a/webclient/lib/summoner.class.php +++ b/webclient/lib/summoner.class.php @@ -23,560 +23,510 @@ */ class Summoner { - /** - * Return path to given theme file with fallback to default theme - * - * @param string $file relative path from THEME/ - * @param string $theme Theme name - * @param string $defaultTheme Default theme name can be overwritten - * @return string False of nothing is found - */ - static function themefile(string $file, string $theme, string $defaultTheme = 'default'): string { - $ret = ''; - - if(file_exists('view/'.$theme.'/'.$file)) { - $ret = 'view/'.$theme.'/'.$file; - } - elseif (file_exists('view/'.$defaultTheme.'/'.$file)) { - $ret = 'view/'.$defaultTheme.'/'.$file; - } - - return $ret; - } - - /** - * validate the given string with the given type. Optional check the string - * length - * - * @param string $input The string to check - * @param string $mode How the string should be checked - * @param integer $limit If int given the string is checked for length - * - * @return bool - * @see http://de.php.net/manual/en/regexp.reference.unicode.php - * http://www.sql-und-xml.de/unicode-database/#pc - * - * the pattern replaces all that is allowed. the correct result after - * the replace should be empty, otherwise are there chars which are not - * allowed - * - */ - static function validate(string $input, string $mode = 'text', int $limit = 0): bool { - // check if we have input - $input = trim($input); - - if($input == "") return false; - - $ret = false; - - switch ($mode) { - case 'mail': - if(filter_var($input,FILTER_VALIDATE_EMAIL) === $input) { - return true; - } - else { - return false; - } - break; - - case 'rights': - return self::isRightsString($input); - break; - - case 'url': - if(filter_var($input,FILTER_VALIDATE_URL) === $input) { - return true; - } - else { - return false; - } - break; - - case 'nospace': - // text without any whitespace and special chars - $pattern = '/[\p{L}\p{N}]/u'; - break; - - case 'nospaceP': - // text without any whitespace and special chars - // but with Punctuation other - # http://www.sql-und-xml.de/unicode-database/po.html - $pattern = '/[\p{L}\p{N}\p{Po}\-]/u'; - break; - - case 'digit': - // only numbers and digit - // warning with negative numbers... - $pattern = '/[\p{N}\-]/u'; - break; - - case 'pageTitle': - // text with whitespace and without special chars - // but with Punctuation - $pattern = '/[\p{L}\p{N}\p{Po}\p{Z}\s-]/u'; - break; - - # strange. the \p{M} is needed.. don't know why.. - case 'filename': - $pattern = '/[\p{L}\p{N}\p{M}\-_\.\p{Zs}]/u'; - break; - - case 'text': - default: - $pattern = '/[\p{L}\p{N}\p{P}\p{S}\p{Z}\p{M}\s]/u'; - } - - $value = preg_replace($pattern, '', $input); - - if($value === "") { - $ret = true; - } - - if(!empty($limit)) { - # isset starts with 0 - if(isset($input[$limit])) { - # too long - $ret = false; - } - } - - return $ret; - } - - /** - * check if the given string is a rights string. - * - * @param string $string - * @return boolean - */ - static function isRightsString(string $string): bool { - $ret = false; - - $string = trim($string); - if(empty($string)) return false; - if(isset($string[9])) return false; - - $check = str_replace("r", "", $string); - $check = str_replace("w", "", $check); - $check = str_replace("x", "", $check); - $check = str_replace("-", "", $check); - - if(empty($check)) { - $ret = true; - } - - return $ret; - } - - /** - * creates the rights string from the given rights array - * check what options are set and set the missing ones to - - * - * then create the rights string - * IMPORTANT: keep the order otherwise the rights will be messed up - * - * @param array $rightsArr - * @return string - */ - static function prepareRightsString(array $rightsArr): string { - $rsArr = array(); - $ret = ''; - - if(!empty($rightsArr)) { - // we need a complete type list - // since we can get an "incomplete" array - // if the user hasnt the rights for a specific type - if(!isset($rightsArr['user'])) { - $rightsArr['user'] = ""; - } - if(!isset($rightsArr['group'])) { - $rightsArr['group'] = ""; - } - if(!isset($rightsArr['other'])) { - $rightsArr['other'] = ""; - } - - // create the rights information - foreach ($rightsArr as $type=>$data) { - if(!empty($data['read']) && $data['read'] == "1") { - $rsArr[$type]['read'] = "r"; - } - else { - $rsArr[$type]['read'] = "-"; - } - - if(!empty($data['write']) && $data['write'] == "1") { - $rsArr[$type]['write'] = "w"; - } - else { - $rsArr[$type]['write'] = "-"; - } - - if(!empty($data['delete']) && $data['delete'] == "1") { - $rsArr[$type]['delete'] = "x"; - } - else { - $rsArr[$type]['delete'] = "-"; - } - } - - $rString = $rsArr['user']['read'].$rsArr['user']['write'].$rsArr['user']['delete']; - $rString .= $rsArr['group']['read'].$rsArr['group']['write'].$rsArr['group']['delete']; - $rString .= $rsArr['other']['read'].$rsArr['other']['write'].$rsArr['other']['delete']; - - if(strlen($rString) != 9) { - $ret = ''; - // invalid rights string !! - } - else { - $ret = $rString; - } - } - - return $ret; - } - - /** - * Creates from given rights string the rights array - * - * @param string $rightsString - * @return array - */ - static function prepareRightsArray(string $rightsString): array { - $ret = array(); - - if(self::isRightsString($rightsString) === true) { - $ret['user']['read'] = '-'; - $ret['user']['write'] = '-'; - $ret['user']['delete'] = '-'; - if($rightsString[0] === 'r') $ret['user']['read'] = 'r'; - if($rightsString[1] === 'w') $ret['user']['write'] = 'w'; - if($rightsString[2] === 'x') $ret['user']['delete'] = 'x'; - - $ret['group']['read'] = '-'; - $ret['group']['write'] = '-'; - $ret['group']['delete'] = '-'; - if($rightsString[3] === 'r') $ret['group']['read'] = 'r'; - if($rightsString[4] === 'w') $ret['group']['write'] = 'w'; - if($rightsString[5] === 'x') $ret['group']['delete'] = 'x'; - - $ret['other']['read'] = '-'; - $ret['other']['write'] = '-'; - $ret['other']['delete'] = '-'; - if($rightsString[6] === 'r') $ret['other']['read'] = 'r'; - if($rightsString[7] === 'w') $ret['other']['write'] = 'w'; - if($rightsString[8] === 'x') $ret['other']['delete'] = 'x'; - } - - return $ret; - } - - /** - * read a dir and return the entries as an array - * with full path to the files - * - * @param string $directory The absolute path to the directory - * @param array $ignore An Array with strings to ignored - * @param bool $recursive If we run a recursive scan or not - * @return array - */ - static function readDir(string $directory, array $ignore = array(), bool $recursive = false): array { - $files = array(); - - $dh = opendir($directory); - while(false !== ($file = readdir($dh))) { - if($file[0] ==".") continue; - if(!empty($ignore)) { - foreach ($ignore as $ig) { - if(strstr($file,$ig)) continue 2; - } - } - - if(is_file($directory."/".$file)) { - array_push($files, $directory."/".$file); - } - elseif($recursive === true) { - array_push($files, $directory."/".$file); - $files = array_merge($files, self::readDir($directory."/".$file,$ignore, $recursive)); - } - elseif(is_dir($directory."/".$file)) { - array_push($files, $directory."/".$file); - } - } - closedir($dh); - - return $files; - } - - /** - * delete and/or empty a directory - * - * $empty = true => empty the directory but do not delete it - * - * @param string $directory - * @param bool $empty - * @param int $fTime If not false remove files older then this value in sec. - * @return bool - */ - static function recursive_remove_directory(string $directory, bool $empty = false, int $fTime = 0): bool { - // if the path has a slash at the end we remove it here - if(substr($directory,-1) == '/') { - $directory = substr($directory,0,-1); - } - - // if the path is not valid or is not a directory ... - if(!file_exists($directory) || !is_dir($directory)) { - // ... we return false and exit the function - return false; - - // ... if the path is not readable - }elseif(!is_readable($directory)) { - // ... we return false and exit the function - return false; - - // ... else if the path is readable - } - else { - // we open the directory - $handle = opendir($directory); - - // and scan through the items inside - while (false !== ($item = readdir($handle))) { - // if the filepointer is not the current directory - // or the parent directory - //if($item != '.' && $item != '..' && $item != '.svn') { - if($item[0] != '.') { - // we build the new path to delete - $path = $directory.'/'.$item; - - // if the new path is a directory - if(is_dir($path)) { - // we call this function with the new path - self::recursive_remove_directory($path); - - // if the new path is a file - } - else { - // we remove the file - if($fTime !== false && is_int($fTime)) { - // check filemtime - $ft = filemtime($path); - $offset = time()-$fTime; - if($ft <= $offset) { - unlink($path); - } - } - else { - unlink($path); - } - } - } - } - // close the directory - closedir($handle); - - // if the option to empty is not set to true - if($empty == false) { - // try to delete the now empty directory - if(!rmdir($directory)) { - // return false if not possible - return false; - } - } - // return success - return true; - } - } - - /** - * check if a string starts with a given string - * - * @param string $haystack - * @param string $needle - * @return bool - */ - static function startsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); - } - - /** - * check if a string ends with a given string - * - * @param string $haystack - * @param string $needle - * @return bool - */ - static function endsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - if ($length == 0) { - return true; - } - - return (substr($haystack, -$length) === $needle); - } - - /** - * fix the filesystem filenames. Remove whitespace and ... - * - * @param array $filenames File or folder list - * @return array - */ - static function fixAssetFilenames(array $filenames): array { - $ret = $filenames; - - foreach($filenames as $k=>$file) { - if(file_exists($file)) { - if(strstr($file, " ")) { - # we do not allow any whitespace in a filename - $newFilename = str_replace(" ", "-", $file); - rename($file, $newFilename); - $filenames[$k] = $newFilename; - } - } - } - - return $filenames; - } - - /** - * simulate the Null coalescing operator in php5 - * - * this only works with arrays and checking if the key is there and echo/return it. - * - * http://php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op - * - * @param array $array - * @param array|string $key - * @return bool|mixed - */ - static function ifset(array $array, array|string $key): mixed { - if(is_array($key)) { - $_t = $array; - $_c = 0; - foreach ($key as $k) { - if(isset($_t[$k])) { - $_t = $_t[$k]; - $_c++; - } - } - - return sizeof($key)==$_c ? $_t : false; - - } else { - return isset($array[$key]) ? $array[$key] : false; - } - } - - /** - * based on self::ifset check also the value - * - * @param array $array The array to use - * @param string $key The key to check - * @param string $value The value to compare - * @return bool - */ - static function ifsetValue(array $array, string $key, string $value): bool { - if(self::ifset($array,$key) !== false) { - return $array[$key] == $value; - } - return false; - } - - /** - * Replace in $haystack the $needle with $replace only once - * - * @param string $haystack - * @param string $needle - * @param string $replace - * @return string - */ - static function replaceOnce(string $haystack, string $needle, string $replace): string { - $newstring = $haystack; - $pos = strpos($haystack, $needle); - if ($pos !== false) { - $newstring = substr_replace($haystack, $replace, $pos, strlen($needle)); - } - return $newstring; - } - - /** - * http_build_query with modify array - * modify will add: key AND value not empty - * modify will remove: only key with no value - * - * @param array $array - * @param array $modify - * @return string - */ - static function createFromParameterLinkQuery(array $array, array $modify = array()): string { - $ret = ''; - - if(!empty($modify)) { - foreach($modify as $k=>$v) { - if(empty($v)) { - unset($array[$k]); - } - else { - $array[$k] = $v; - } - } - } - - if(!empty($array)) { - $ret = http_build_query($array); - } - - return $ret; - } - - /** - * Return given string with given $endChar with the max $length - * - * @param string $string - * @param int $length - * @param string $endChar - * @return string - */ - static function limitWithDots(string $string, int $length, string $endChar): string { - $ret = $string; - - if(strlen($string.$endChar) > $length) { - $ret = substr($string,0, $length).$endChar; - } - - return $ret; - } - - /** - * Size of the folder and the data within in bytes - * - * @param string $dir - * @return int - */ - static function folderSize(string $dir): int { - $size = 0; - - foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) { - $size += is_file($each) ? filesize($each) : self::folderSize($each); - } - - return $size; - } - - /** - * Given bytes to human format with unit - * - * @param int $bytes - * @return string - */ - static function bytesToHuman(int $bytes): string { - $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; - for ($i = 0; $bytes > 1024; $i++) { - $bytes /= 1024; - } - return round($bytes, 2) . ' ' . $units[$i]; - } + /** + * Return path to given theme file with fallback to default theme + * + * @param string $file relative path from THEME/ + * @param string $theme Theme name + * @param string $defaultTheme Default theme name can be overwritten + * @return string False of nothing is found + */ + static function themefile(string $file, string $theme, string $defaultTheme = 'default'): string { + $ret = ''; + + if(file_exists('view/'.$theme.'/'.$file)) { + $ret = 'view/'.$theme.'/'.$file; + } + elseif (file_exists('view/'.$defaultTheme.'/'.$file)) { + $ret = 'view/'.$defaultTheme.'/'.$file; + } + + return $ret; + } + + /** + * validate the given string with the given type. Optional check the string + * length + * + * @param string $input The string to check + * @param string $mode How the string should be checked + * @param integer $limit If int given the string is checked for length + * + * @return bool + * @see http://de.php.net/manual/en/regexp.reference.unicode.php + * http://www.sql-und-xml.de/unicode-database/#pc + * + * the pattern replaces all that is allowed. the correct result after + * the replace should be empty, otherwise are there chars which are not + * allowed + * + */ + static function validate(string $input, string $mode = 'text', int $limit = 0): bool { + // check if we have input + $input = trim($input); + + if($input == "") return false; + + $ret = false; + + switch ($mode) { + case 'mail': + if(filter_var($input,FILTER_VALIDATE_EMAIL) === $input) { + return true; + } + else { + return false; + } + break; + + case 'rights': + return self::isRightsString($input); + break; + + case 'url': + if(filter_var($input,FILTER_VALIDATE_URL) === $input) { + return true; + } + else { + return false; + } + break; + + case 'nospace': + // text without any whitespace and special chars + $pattern = '/[\p{L}\p{N}]/u'; + break; + + case 'nospaceP': + // text without any whitespace and special chars + // but with Punctuation other + # http://www.sql-und-xml.de/unicode-database/po.html + $pattern = '/[\p{L}\p{N}\p{Po}\-_]/u'; + break; + + case 'digit': + // only numbers and digit + // warning with negative numbers... + $pattern = '/[\p{N}\-]/u'; + break; + + case 'pageTitle': + // text with whitespace and without special chars + // but with Punctuation + $pattern = '/[\p{L}\p{N}\p{Po}\p{Z}\s\-_]/u'; + break; + + # strange. the \p{M} is needed.. don't know why.. + case 'filename': + $pattern = '/[\p{L}\p{N}\p{M}\-_\.\p{Zs}]/u'; + break; + + case 'text': + default: + $pattern = '/[\p{L}\p{N}\p{P}\p{S}\p{Z}\p{M}\s]/u'; + } + + $value = preg_replace($pattern, '', $input); + + if($value === "") { + $ret = true; + } + + if(!empty($limit)) { + # isset starts with 0 + if(isset($input[$limit])) { + # too long + $ret = false; + } + } + + return $ret; + } + + /** + * check if the given string is a rights string. + * + * @param string $string + * @return boolean + */ + static function isRightsString(string $string): bool { + $ret = false; + + $string = trim($string); + if(empty($string)) return false; + if(isset($string[9])) return false; + + $check = str_replace("r", "", $string); + $check = str_replace("w", "", $check); + $check = str_replace("x", "", $check); + $check = str_replace("-", "", $check); + + if(empty($check)) { + $ret = true; + } + + return $ret; + } + + /** + * creates the rights string from the given rights array + * check what options are set and set the missing ones to - + * + * then create the rights string + * IMPORTANT: keep the order otherwise the rights will be messed up + * + * @param array $rightsArr + * @return string + */ + static function prepareRightsString(array $rightsArr): string { + $rsArr = array(); + $ret = ''; + + if(!empty($rightsArr)) { + // we need a complete type list + // since we can get an "incomplete" array + // if the user hasnt the rights for a specific type + if(!isset($rightsArr['user'])) { + $rightsArr['user'] = ""; + } + if(!isset($rightsArr['group'])) { + $rightsArr['group'] = ""; + } + if(!isset($rightsArr['other'])) { + $rightsArr['other'] = ""; + } + + // create the rights information + foreach ($rightsArr as $type=>$data) { + if(!empty($data['read']) && $data['read'] == "1") { + $rsArr[$type]['read'] = "r"; + } + else { + $rsArr[$type]['read'] = "-"; + } + + if(!empty($data['write']) && $data['write'] == "1") { + $rsArr[$type]['write'] = "w"; + } + else { + $rsArr[$type]['write'] = "-"; + } + + if(!empty($data['delete']) && $data['delete'] == "1") { + $rsArr[$type]['delete'] = "x"; + } + else { + $rsArr[$type]['delete'] = "-"; + } + } + + $rString = $rsArr['user']['read'].$rsArr['user']['write'].$rsArr['user']['delete']; + $rString .= $rsArr['group']['read'].$rsArr['group']['write'].$rsArr['group']['delete']; + $rString .= $rsArr['other']['read'].$rsArr['other']['write'].$rsArr['other']['delete']; + + if(strlen($rString) != 9) { + $ret = ''; + // invalid rights string !! + } + else { + $ret = $rString; + } + } + + return $ret; + } + + /** + * Creates from given rights string the rights array + * + * @param string $rightsString + * @return array + */ + static function prepareRightsArray(string $rightsString): array { + $ret = array(); + + if(self::isRightsString($rightsString) === true) { + $ret['user']['read'] = '-'; + $ret['user']['write'] = '-'; + $ret['user']['delete'] = '-'; + if($rightsString[0] === 'r') $ret['user']['read'] = 'r'; + if($rightsString[1] === 'w') $ret['user']['write'] = 'w'; + if($rightsString[2] === 'x') $ret['user']['delete'] = 'x'; + + $ret['group']['read'] = '-'; + $ret['group']['write'] = '-'; + $ret['group']['delete'] = '-'; + if($rightsString[3] === 'r') $ret['group']['read'] = 'r'; + if($rightsString[4] === 'w') $ret['group']['write'] = 'w'; + if($rightsString[5] === 'x') $ret['group']['delete'] = 'x'; + + $ret['other']['read'] = '-'; + $ret['other']['write'] = '-'; + $ret['other']['delete'] = '-'; + if($rightsString[6] === 'r') $ret['other']['read'] = 'r'; + if($rightsString[7] === 'w') $ret['other']['write'] = 'w'; + if($rightsString[8] === 'x') $ret['other']['delete'] = 'x'; + } + + return $ret; + } + + /** + * read a dir and return the entries as an array + * with full path to the files + * + * @param string $directory The absolute path to the directory + * @param array $ignore An Array with strings to ignored + * @param bool $recursive If we run a recursive scan or not + * @return array + */ + static function readDir(string $directory, array $ignore = array(), bool $recursive = false): array { + $files = array(); + + $dh = opendir($directory); + while(false !== ($file = readdir($dh))) { + if($file[0] ==".") continue; + if(!empty($ignore)) { + foreach ($ignore as $ig) { + if(strstr($file,$ig)) continue 2; + } + } + + if(is_file($directory."/".$file)) { + array_push($files, $directory."/".$file); + } + elseif($recursive === true) { + array_push($files, $directory."/".$file); + $files = array_merge($files, self::readDir($directory."/".$file,$ignore, $recursive)); + } + elseif(is_dir($directory."/".$file)) { + array_push($files, $directory."/".$file); + } + } + closedir($dh); + + return $files; + } + + /** + * delete and/or empty a directory + * + * $empty = true => empty the directory but do not delete it + * + * @param string $directory + * @param bool $empty + * @param int $fTime If not false remove files older then this value in sec. + * @return bool + */ + static function recursive_remove_directory(string $directory, bool $empty = false, int $fTime = 0): bool { + // if the path has a slash at the end we remove it here + if(substr($directory,-1) == '/') { + $directory = substr($directory,0,-1); + } + + // if the path is not valid or is not a directory ... + if(!file_exists($directory) || !is_dir($directory)) { + // ... we return false and exit the function + return false; + + // ... if the path is not readable + }elseif(!is_readable($directory)) { + // ... we return false and exit the function + return false; + + // ... else if the path is readable + } + else { + // we open the directory + $handle = opendir($directory); + + // and scan through the items inside + while (false !== ($item = readdir($handle))) { + // if the filepointer is not the current directory + // or the parent directory + //if($item != '.' && $item != '..' && $item != '.svn') { + if($item[0] != '.') { + // we build the new path to delete + $path = $directory.'/'.$item; + + // if the new path is a directory + if(is_dir($path)) { + // we call this function with the new path + self::recursive_remove_directory($path); + + // if the new path is a file + } + else { + // we remove the file + if($fTime !== false && is_int($fTime)) { + // check filemtime + $ft = filemtime($path); + $offset = time()-$fTime; + if($ft <= $offset) { + unlink($path); + } + } + else { + unlink($path); + } + } + } + } + // close the directory + closedir($handle); + + // if the option to empty is not set to true + if($empty == false) { + // try to delete the now empty directory + if(!rmdir($directory)) { + // return false if not possible + return false; + } + } + // return success + return true; + } + } + + + /** + * simulate the Null coalescing operator in php5 + * + * this only works with arrays and checking if the key is there and echo/return it. + * + * http://php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op + * + * @param array $array + * @param array|string $key + * @return bool|mixed + */ + static function ifset(array $array, array|string $key): mixed { + if(is_array($key)) { + $_t = $array; + $_c = 0; + foreach ($key as $k) { + if(isset($_t[$k])) { + $_t = $_t[$k]; + $_c++; + } + } + + return sizeof($key)==$_c ? $_t : false; + + } else { + return isset($array[$key]) ? $array[$key] : false; + } + } + + /** + * based on self::ifset check also the value + * + * @param array $array The array to use + * @param string $key The key to check + * @param string $value The value to compare + * @return bool + */ + static function ifsetValue(array $array, string $key, string $value): bool { + if(self::ifset($array,$key) !== false) { + return $array[$key] == $value; + } + return false; + } + + /** + * Replace in $haystack the $needle with $replace only once + * + * @param string $haystack + * @param string $needle + * @param string $replace + * @return string + */ + static function replaceOnce(string $haystack, string $needle, string $replace): string { + $newstring = $haystack; + $pos = strpos($haystack, $needle); + if ($pos !== false) { + $newstring = substr_replace($haystack, $replace, $pos, strlen($needle)); + } + return $newstring; + } + + /** + * http_build_query with modify array + * modify will add: key AND value not empty + * modify will remove: only key with no value + * + * @param array $array + * @param array $modify + * @return string + */ + static function createFromParameterLinkQuery(array $array, array $modify = array()): string { + $ret = ''; + + if(!empty($modify)) { + foreach($modify as $k=>$v) { + if(empty($v)) { + unset($array[$k]); + } + else { + $array[$k] = $v; + } + } + } + + if(!empty($array)) { + $ret = http_build_query($array); + } + + return $ret; + } + + /** + * Return given string with given $endChar with the max $length + * + * @param string $string + * @param int $length + * @param string $endChar + * @return string + */ + static function limitWithDots(string $string, int $length, string $endChar): string { + $ret = $string; + + if(strlen($string.$endChar) > $length) { + $ret = substr($string,0, $length).$endChar; + } + + return $ret; + } + + /** + * Size of the folder and the data within in bytes + * + * @param string $dir + * @return int + */ + static function folderSize(string $dir): int { + $size = 0; + + foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) { + $size += is_file($each) ? filesize($each) : self::folderSize($each); + } + + return $size; + } + + /** + * Given bytes to human format with unit + * + * @param int $bytes + * @return string + */ + static function bytesToHuman(int $bytes): string { + $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + for ($i = 0; $bytes > 1024; $i++) { + $bytes /= 1024; + } + return round($bytes, 2) . ' ' . $units[$i]; + } /** * Make the input more safe for logging