2020-07-26 14:52:41 +02:00
< ? PHP
/**
* Provides type - safe overrides of default PHP functions .
*/
declare ( strict_types = 1 );
/**
* Standard class providing overrides of default PHP functions as static
* functions .
*/
2020-08-29 17:22:16 +02:00
final class MD_STD {
2020-07-26 14:52:41 +02:00
/**
* Wrapper around file_get_contents , that provides catches errors on it and returns
* with type safety .
*
* @ param string $filename Filepath of the file to read .
*
* @ return string
*/
public static function file_get_contents ( string $filename ) : string {
2020-08-19 14:55:38 +02:00
if ( \substr ( $filename , 0 , 4 ) !== 'http' && ! \file_exists ( $filename )) {
2020-07-26 14:52:41 +02:00
throw new MDFileDoesNotExist ( " There is no file { $filename } " );
}
2020-08-19 14:55:38 +02:00
$contents = \file_get_contents ( $filename );
2020-07-26 14:52:41 +02:00
2020-08-19 14:55:38 +02:00
if ( \is_bool ( $contents )) {
2020-07-26 14:52:41 +02:00
throw new MDFileIsNotReadable ( " File { $filename } is not readable " );
}
return $contents ;
}
2020-08-05 14:01:26 +02:00
/**
* Returns the real path of a relative file path . Throws an error rather than
* returning the default false .
*
* @ param string $path File path to convert .
*
* @ return string
*/
public static function realpath ( string $path ) : string {
2020-08-19 14:55:38 +02:00
$output = \realpath ( $path );
if ( ! \is_string ( $output )) throw new MDFileDoesNotExist ( " The file { $path } does not exist or is not readable. " );
2020-08-05 14:01:26 +02:00
return $output ;
}
2020-07-26 14:52:41 +02:00
/**
* Gets contents of a folder .
*
* @ param string $filepath Directory path .
*
* @ return array < string >
*/
public static function scandir ( string $filepath ) : array {
2020-08-19 14:55:38 +02:00
if ( ! \is_dir ( $filepath ) || ( $output = \scandir ( $filepath )) === false ) {
2020-07-26 15:13:03 +02:00
throw new MDFileDoesNotExist ( " There is no file { $filepath } " );
2020-07-26 14:52:41 +02:00
}
2020-11-09 14:17:54 +01:00
// Remove unwanted files from list
$output = \array_diff ( $output , [ '.' , '..' , '.git' ]);
// Return array values, to make it a list rather than an associative array.
// This should be done in a separate line, as it observably leads to a
// significant reduction in the used RAM.
return \array_values ( $output );
2020-07-26 14:52:41 +02:00
}
2020-08-05 15:31:29 +02:00
/**
* Type safe wrapper around ob_get_clean () : Gets the current buffer
* contents and delete current output buffer .
*
* @ return string
*/
public static function ob_get_clean () : string {
2020-08-19 14:55:38 +02:00
$output = \ob_get_clean ();
2020-08-05 15:31:29 +02:00
if ( $output === false ) throw new MDOutputBufferNotStarted ( " Output buffer was not started " );
return $output ;
}
2020-08-05 09:06:28 +02:00
/**
* Function checking if a string starts with another .
*
* @ param string $haystack String to check .
* @ param string $needle Potential start of $haystack .
*
* @ return boolean
*/
public static function startsWith ( string $haystack , string $needle ) : bool {
2020-08-22 23:57:37 +02:00
if ( substr ( $haystack , 0 , \strlen ( $needle )) == $needle ) return true ;
2020-08-05 09:06:28 +02:00
else return false ;
}
/**
* Function checking if a string starts with any input from the input array .
*
* @ param string $haystack String to check .
* @ param string [] $needles Array containing potential start values of $haystack .
*
* @ return boolean
*/
public static function startsWithAny ( string $haystack , array $needles ) : bool {
$output = false ;
foreach ( $needles as $needle ) {
$output = self :: startsWith ( $haystack , $needle );
if ( $output == true ) return $output ;
}
return $output ;
}
2020-08-09 00:59:41 +02:00
/**
* Type - safe ( r ) wrapper around preg_replace .
*
* @ param string $pattern The pattern to search for . It can be either a string or an array with strings .
* @ param string $replacement To replace with .
* @ param string $subject The string or an array with strings to search and replace .
*
* @ return string
*/
public static function preg_replace_str ( string $pattern , string $replacement , string $subject ) : string {
2020-08-19 14:55:38 +02:00
$output = \preg_replace ( $pattern , $replacement , $subject );
2020-08-09 00:59:41 +02:00
if ( $output === null ) {
throw new Exception ( " Error replacing in $subject : Replacing $pattern with $replacement " );
}
return $output ;
}
2020-08-05 17:02:03 +02:00
/**
* Type - safe wrapper around json_encode .
*
* @ see https :// www . php . net / manual / en / function . json - encode . php
*
* @ param array < mixed > $value The value being encoded . Can be any type except a resource .
* @ param integer $options Bitmask consisting of JSON_FORCE_OBJECT , JSON_HEX_QUOT ...
* @ param integer $depth Depth of coding .
*
* @ return string
*/
public static function json_encode ( array $value , int $options = 0 , int $depth = 512 ) : string {
2020-08-19 14:55:38 +02:00
$output = \json_encode ( $value , $options , $depth );
2020-08-05 17:02:03 +02:00
if ( $output === false ) throw new Exception ( " JSON output could not be generated " );
return $output ;
}
2020-08-10 16:59:06 +02:00
/**
* Type - safe wrapper around strtotime () .
*
* @ param string $datetime String to convert .
*
* @ return integer
*/
public static function strtotime ( string $datetime ) : int {
2020-08-20 14:13:49 +02:00
$output = \strtotime ( $datetime );
2020-08-10 16:59:06 +02:00
if ( $output === false ) throw new MDInvalidInputDate ( " Invalid input date { $datetime } . " );
return $output ;
}
2020-08-05 16:36:02 +02:00
/**
* Wrapper for curling contents from the web .
*
* @ param string $url URL to query .
* @ param integer $timeout Timeout in milliseconds .
*
* @ return string
*/
public static function runCurl ( string $url , int $timeout = 1200 ) : string {
2020-12-08 20:42:54 +01:00
$curl = \curl_init ();
2020-08-05 16:36:02 +02:00
2020-08-22 23:57:37 +02:00
\curl_setopt ( $curl , CURLOPT_URL , $url );
\curl_setopt ( $curl , CURLOPT_HEADER , false );
\curl_setopt ( $curl , CURLOPT_CONNECTTIMEOUT_MS , $timeout ); //timeout in seconds
\curl_setopt ( $curl , CURLOPT_TIMEOUT_MS , $timeout ); //timeout in seconds
\curl_setopt ( $curl , CURLOPT_RETURNTRANSFER , true );
\curl_setopt ( $curl , CURLOPT_FOLLOWLOCATION , true );
2020-09-04 18:09:56 +02:00
// \curl_setopt($curl, CURLOPT_COOKIESESSION, true);
\curl_setopt ( $curl , CURLOPT_AUTOREFERER , true );
\curl_setopt ( $curl , CURLOPT_USERAGENT , 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2' );
2020-08-22 23:57:37 +02:00
$result = \curl_exec ( $curl );
2020-08-05 16:36:02 +02:00
2020-09-04 18:09:56 +02:00
// if ($err = curl_errno($curl)) echo $err;
// if ($errmsg = curl_error($curl)) echo $errmsg;
2020-08-22 23:57:37 +02:00
\curl_close ( $curl );
if ( \is_bool ( $result )) return " " ;
2020-08-05 16:36:02 +02:00
return $result ;
}
2020-08-10 19:55:40 +02:00
/**
* Function lang_getfrombrowser gets the browser language based on HTTP headers .
*
* @ param array < string > $allowed_languages Array containing all the languages for which
2020-11-19 23:32:29 +01:00
* there are translations .
2020-08-10 19:55:40 +02:00
* @ param string $default_language Default language of the instance of MD .
* @ param string $lang_variable Currently set language variable . Optional .
* @ param boolean $strict_mode Whether to demand " de-de " ( true ) or " de " ( false ) Optional .
*
* @ return string
*/
public static function lang_getfrombrowser ( array $allowed_languages , string $default_language , string $lang_variable = " " , bool $strict_mode = true ) : string {
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwenden, wenn keine Sprachvariable mitgegeben wurde
if ( $lang_variable === " " ) {
if ( isset ( $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ])) $lang_variable = $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ];
}
// wurde irgendwelche Information mitgeschickt?
if ( empty ( $lang_variable )) {
// Nein? => Standardsprache zurückgeben
return $default_language ;
}
// Den Header auftrennen
$accepted_languages = preg_split ( '/,\s*/' , $lang_variable );
if ( ! is_array ( $accepted_languages )) return $default_language ;
// Die Standardwerte einstellen
$current_lang = $default_language ;
$current_q = 0 ;
// Nun alle mitgegebenen Sprachen abarbeiten
foreach ( $accepted_languages as $accepted_language ) {
// Alle Infos über diese Sprache rausholen
// phpcs:disable Generic.Strings.UnnecessaryStringConcat
2020-08-19 14:55:38 +02:00
$res = \preg_match ( '/^([a-z]{1,8}(?:-[a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i' , $accepted_language , $matches );
2020-08-10 19:55:40 +02:00
// phpcs:enable
// war die Syntax gültig?
if ( ! $res ) {
// Nein? Dann ignorieren
continue ;
}
// Sprachcode holen und dann sofort in die Einzelteile trennen
2020-08-19 14:55:38 +02:00
$lang_code = \explode ( '-' , $matches [ 1 ]);
2020-08-10 19:55:40 +02:00
// Wurde eine Qualität mitgegeben?
if ( isset ( $matches [ 2 ])) {
// die Qualität benutzen
$lang_quality = ( float ) $matches [ 2 ];
} else {
// Kompabilitätsmodus: Qualität 1 annehmen
$lang_quality = 1.0 ;
}
// Bis der Sprachcode leer ist...
// phpcs:disable Squiz.PHP.DisallowSizeFunctionsInLoops
while ( ! empty ( $lang_code )) {
// phpcs:enable
// mal sehen, ob der Sprachcode angeboten wird
2020-08-27 17:16:48 +02:00
if ( \in_array ( \strtolower ( \join ( '-' , $lang_code )), $allowed_languages , true )) {
2020-08-10 19:55:40 +02:00
// Qualität anschauen
if ( $lang_quality > $current_q ) {
// diese Sprache verwenden
2020-08-19 14:55:38 +02:00
$current_lang = \strtolower ( join ( '-' , $lang_code ));
2020-08-10 19:55:40 +02:00
$current_q = $lang_quality ;
// Hier die innere while-Schleife verlassen
break ;
}
}
// Wenn wir im strengen Modus sind, die Sprache nicht versuchen zu minimalisieren
if ( $strict_mode ) {
// innere While-Schleife aufbrechen
break ;
}
// den rechtesten Teil des Sprachcodes abschneiden
2020-08-19 14:55:38 +02:00
\array_pop ( $lang_code );
2020-08-10 19:55:40 +02:00
}
}
// die gefundene Sprache zurückgeben
return $current_lang ;
}
2020-08-22 17:00:21 +02:00
/**
* Type - safe wrapper around filesize , if output is false , throws an error .
*
* @ param string $filename File name .
*
* @ return integer
*/
public static function filesize ( string $filename ) : int {
2020-08-22 23:57:37 +02:00
$output = \filesize ( $filename );
2020-08-22 17:00:21 +02:00
if ( $output === false ) {
throw new Exception ( " Cannot get filesize of file { $filename } " );
}
return $output ;
}
2020-08-10 19:55:40 +02:00
/**
* Function human_filesize translates byte - level filesizes to human readable ones .
* Thanks to Jeffrey Sambells http :// jeffreysambells . com / 2012 / 10 / 25 / human - readable - filesize - php
*
2020-08-11 08:10:23 +02:00
* @ param integer $bytes A file size , e . g . returned from filesize () .
2020-08-10 19:55:40 +02:00
* @ param integer $decimals Number of decimal digits to allow .
*
* @ return string
*/
2020-08-11 08:10:23 +02:00
public static function human_filesize ( int $bytes , int $decimals = 2 ) : string {
2020-08-10 19:55:40 +02:00
$size = [ 'B' , 'kB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ];
2020-08-19 14:55:38 +02:00
$factor = \floor (( \strlen (( string ) $bytes ) - 1 ) / 3 );
return \sprintf ( " %. { $decimals } f " , $bytes / \pow ( 1024 , $factor )) . $size [ $factor ];
2020-08-10 19:55:40 +02:00
}
2020-08-20 14:56:36 +02:00
/**
* Type - safe wrapper around openssl_random_pseudo_bytes .
*
* @ param integer $length Length .
*
* @ return string
*/
public static function openssl_random_pseudo_bytes ( int $length ) : string {
$output = \openssl_random_pseudo_bytes ( $length );
if ( $output === false ) throw new Exception ( " Failed generating random pseudo bytes using openssl_random_pseudo_bytes " );
return $output ;
}
2020-10-21 21:16:18 +02:00
/**
* Function for minimizing HTML , trimming each line .
*
* @ param string $input Input .
*
* @ return string
*/
public static function minimizeHTMLString ( string $input ) : string {
$input = \explode ( PHP_EOL , $input );
$output = " " ;
foreach ( $input as $line ) $output .= \trim ( $line ) . PHP_EOL ;
return $output ;
}
2020-10-23 16:13:02 +02:00
/**
* This function cuts down a string and adds a period in case it ' s longer
* than length to create a snippet .
*
* @ param string $string Input text to cut down .
* @ param integer $length Length of the snippet to create .
*
* @ return string
*/
public static function createTextSnippet ( string $string , int $length = 180 ) : string {
if ( \mb_strlen ( $string ) > $length ) {
$string = \mb_substr ( $string , 0 , $length );
if (( $lastWhitespace = \mb_strrpos ( $string , ' ' )) !== false ) {
$string = \mb_substr ( $string , 0 , $lastWhitespace );
}
$string .= '...' ;
}
return $string ;
}
2020-11-08 18:54:40 +01:00
/**
* Wrapper around the finfo functions to get the mime content type of a file .
*
* @ param string $filepath Expected file path .
*
* @ return string
*/
public static function mime_content_type ( string $filepath ) : string {
if ( ! ( $finfo = \finfo_open ( FILEINFO_MIME_TYPE ))) {
throw new Exception ( " Cannot open finfo context " );
}
if ( ! ( $mime_type = finfo_file ( $finfo , $filepath ))) {
throw new MDWrongFileType ( " Cannot get mime type of file: " . basename ( $filepath ));
}
\finfo_close ( $finfo );
return $mime_type ;
}
2020-11-08 00:12:02 +01:00
/**
* Checks if a file exists , with one of the expected mime types .
*
* @ param string $filepath File path of the file that needs to exist .
* @ param string [] $accepted_mimetype Mime type the file should have .
*
* @ return void
*/
2020-12-05 20:48:51 +01:00
public static function ensure_file ( string $filepath , array $accepted_mimetype = []) : void {
2020-11-08 00:12:02 +01:00
if ( ! \file_exists ( $filepath )) {
throw new MDFileDoesNotExist ( " File " . basename ( $filepath ) . " does not exist " );
}
// Check for mime type follows. If no check is to be done, ignore this.
if ( empty ( $accepted_mimetype )) {
return ;
}
2020-11-08 18:54:40 +01:00
$mime_type = self :: mime_content_type ( $filepath );
2020-11-08 00:12:02 +01:00
2020-11-08 13:06:05 +01:00
if ( ! \in_array ( $mime_type , $accepted_mimetype , true )) {
2020-11-08 00:12:02 +01:00
throw new MDWrongFileType ( " Incorrect mime type of file " . \basename ( $filepath ) . " . Mime type is " . \mime_content_type ( $filepath ) . " , accepted any of [' " . \implode ( " ', ' " , $accepted_mimetype ) . " '] " );
}
}
2020-12-03 12:39:47 +01:00
2020-12-04 21:33:11 +01:00
/**
* Wrapper around exec , to be used with editing functions .
* Pipes STDERR to STDOUT and throws an Exception on any error .
*
* @ param string $cmds Commands to run .
*
* @ return void
*/
public static function exec_edit ( string $cmds ) : void {
2020-12-09 13:34:31 +01:00
$error = \shell_exec ( $cmds . ' 2>&1 1>/dev/null' );
2020-12-04 21:33:11 +01:00
if ( ! empty ( $error )) {
2020-12-05 20:48:51 +01:00
throw new \Exception ( 'Shell error: ' . $error . PHP_EOL . PHP_EOL . 'Command was: ' . $cmds );
2020-12-04 21:33:11 +01:00
}
}
2020-12-03 12:39:47 +01:00
/**
* Wrapper around levenshtein (), that only compares the first 250
* characters ( levenshtein can ' t handle more than 255 ) .
*
* @ param string $str1 First string .
* @ param string $str2 Second string .
*
* @ return integer
*/
public static function levenshtein ( string $str1 , string $str2 ) : int {
if ( \strlen ( $str1 ) > 250 ) $str1 = \substr ( $str1 , 0 , 250 );
if ( \strlen ( $str2 ) > 250 ) $str2 = \substr ( $str2 , 0 , 250 );
return \levenshtein ( $str1 , $str2 );
}
2020-07-26 14:52:41 +02:00
}