123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- <?php
- /**
- * Bibliotheca
- *
- * Copyright 2018-2023 Johannes Keßler
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.
- */
- /**
- * Class Summoner
- * a static helper class
- */
- 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;
- }
- /**
- * Return the current config for a theme based on UI_THEME
- *
- * @param string $configProperty The property to fetch
- * @param String $theme Theme name
- * @param string $defaultTheme Default theme name can be overwritten
- * @return string
- */
- static function themeConfig(string $configProperty, string $theme, string $defaultTheme = 'default'): string {
- $ret = '';
- if(defined('UI_THEME_CONFIG')) {
- $ret = UI_THEME_CONFIG[$theme][$configProperty] ?? UI_THEME_CONFIG[$defaultTheme][$configProperty];
- }
- 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;
- }
- }
- /**
- * 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
- *
- * @param mixed $input The string|array to be made more safe
- * @return string
- */
- static function cleanForLog(mixed $input): string {
- $input = var_export($input, true);
- $input = preg_replace( "/[\t\n\r]/", " ", $input);
- return addcslashes($input, "\000..\037\177..\377\\");
- }
- /**
- * error_log with a dedicated destination
- * Uses LOGFILE const
- *
- * @param string $msg The string to be written to the log
- */
- static function sysLog(string $msg): void {
- error_log(date("c")." ".$msg."\n", 3, LOGFILE);
- }
- /**
- * Create unique words from the given data
- *
- * @param $data string
- * @return array
- *
- */
- static function words(string $data): array {
- preg_match_all('/\w{3,}+/u',$data,$matches);
- return array_unique($matches[0]);
- }
- }
|