386 lines
11 KiB
PHP
386 lines
11 KiB
PHP
<?php
|
|
|
|
namespace EesyPHP;
|
|
|
|
use League\MimeTypeDetection\ExtensionMimeTypeDetector;
|
|
|
|
/*
|
|
* Parser/formatter values helpers
|
|
*/
|
|
function format_size($size, $digit=False) {
|
|
if (!$digit && $digit!==0) $digit=2;
|
|
if ($size>=1099511627776)
|
|
return number_format($size/1099511627776,$digit)."To";
|
|
elseif ($size>=1073741824)
|
|
return number_format($size/1073741824,$digit)."Go";
|
|
else if ($size>=1048576)
|
|
return number_format($size/1048576,$digit)."Mo";
|
|
else if ($size>=1024)
|
|
return number_format($size/1024,$digit)."Ko";
|
|
else
|
|
return $size."o";
|
|
}
|
|
|
|
/*
|
|
* Generic Data/value helpers
|
|
*/
|
|
function vardump($data) {
|
|
ob_start();
|
|
var_dump($data);
|
|
$data = ob_get_contents();
|
|
ob_end_clean();
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Format a callable object for logging
|
|
* @param string|array $callable The callable object
|
|
* @return string The callable object string representation
|
|
*/
|
|
function format_callable($callable) {
|
|
if (is_string($callable))
|
|
return $callable."()";
|
|
if (is_array($callable))
|
|
if (is_string($callable[0]))
|
|
return $callable[0]."::".$callable[1]."()";
|
|
elseif (is_object($callable[0]))
|
|
return get_class($callable[0])."->".$callable[1]."()";
|
|
else
|
|
return "Unkown->".$callable[1]."()";
|
|
// @phpstan-ignore-next-line
|
|
return vardump($callable);
|
|
}
|
|
|
|
/**
|
|
* Check if given value is empty
|
|
* @param mixed $value
|
|
* @return bool
|
|
*/
|
|
function check_is_empty($value) {
|
|
switch(gettype($value)) {
|
|
case "boolean":
|
|
case "integer":
|
|
case "double":
|
|
case "object":
|
|
case "resource":
|
|
return False;
|
|
case "array":
|
|
case "string":
|
|
if ($value == "0") return false;
|
|
return empty($value);
|
|
case "NULL":
|
|
return True;
|
|
}
|
|
return empty($value);
|
|
}
|
|
|
|
/**
|
|
* Ensure the given value is an array and return an array with this value if not
|
|
* @param mixed $value
|
|
* @return array
|
|
*/
|
|
function ensure_is_array($value) {
|
|
if (is_array($value))
|
|
return $value;
|
|
if (check_is_empty($value))
|
|
return array();
|
|
return array($value);
|
|
}
|
|
|
|
/**
|
|
* Get a specific configuration variable value
|
|
*
|
|
* @param mixed $value The value to cast
|
|
* @param string $type The type of expected value. The configuration variable
|
|
* value will be cast as this type. Could be : bool, int,
|
|
* float or string.
|
|
* @param bool $split If true, $type=='array' and $value is a string, split
|
|
* the value by comma (optional, default: false)
|
|
* @return mixed The cast value
|
|
**/
|
|
function cast($value, $type, $split=false) {
|
|
if (strpos($type, 'array_of_') === 0) {
|
|
$type = substr($type, 9);
|
|
$values = array();
|
|
foreach(ensure_is_array($value) as $key => $value)
|
|
$values[$key] = cast($value, $type);
|
|
return $values;
|
|
}
|
|
switch($type) {
|
|
case 'bool':
|
|
case 'boolean':
|
|
return boolval($value);
|
|
case 'int':
|
|
case 'integer':
|
|
return intval($value);
|
|
case 'float':
|
|
return floatval($value);
|
|
case 'str':
|
|
case 'string':
|
|
return strval($value);
|
|
case 'array':
|
|
if ($split && is_string($value))
|
|
$value = preg_split('/ *, */', $value);
|
|
return ensure_is_array($value);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/*
|
|
* Generic file/directory helpers
|
|
*/
|
|
|
|
/**
|
|
* Dump file content to propose its download
|
|
* @param string $file_path The file path
|
|
* @param string|null $mime_type The file MIME type (optional, default: auto-detected)
|
|
* @param int|null $max_age Max age in second of this file in browser cache (optional,
|
|
* default: null=1h, set to False to disable Cache-Control header)
|
|
* @return never
|
|
*/
|
|
function dump_file($file_path, $mime_type=null, $max_age=null) {
|
|
if (is_file($file_path)) {
|
|
if (is_null($mime_type)) {
|
|
$detector = new ExtensionMimeTypeDetector();
|
|
$mime_type = $detector->detectMimeTypeFromFile($file_path);
|
|
Log::trace('MIME type detected for "%s" is "%s"', $file_path, $mime_type);
|
|
}
|
|
header("Content-Type: $mime_type");
|
|
$last_modified_time = filemtime($file_path);
|
|
$etag = md5_file($file_path);
|
|
$max_age = is_null($max_age)?3600:$max_age;
|
|
if ($max_age)
|
|
header("Cache-Control: max-age=$max_age, must-revalidate");
|
|
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
|
|
header("Etag: $etag");
|
|
|
|
if (
|
|
(
|
|
isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
|
@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time
|
|
) || (
|
|
isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
|
|
trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag
|
|
)
|
|
) {
|
|
header("HTTP/1.1 304 Not Modified");
|
|
exit();
|
|
}
|
|
|
|
header('Pragma: public');
|
|
header('Content-Length: ' . filesize($file_path));
|
|
readfile($file_path);
|
|
exit();
|
|
}
|
|
Url :: trigger_error_404();
|
|
}
|
|
|
|
function delete_directory($dir, $recursive=true) {
|
|
$files = array_diff(scandir($dir), array('.','..'));
|
|
if ($recursive) {
|
|
foreach ($files as $file) {
|
|
if (is_dir("$dir/$file")) {
|
|
if (!delete_directory("$dir/$file", true)) {
|
|
Log :: error("delete_directory($dir) : Fail to delete sub-directory '$dir/$file'.");
|
|
return false;
|
|
}
|
|
}
|
|
else if (!unlink("$dir/$file")) {
|
|
Log :: error("delete_directory($dir) : Fail to delete '$dir/$file'.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (!empty($files)) {
|
|
Log :: error("delete_directory($dir) : Directory is not empty.");
|
|
return false;
|
|
}
|
|
return rmdir($dir);
|
|
}
|
|
|
|
/*
|
|
* Run external command helper
|
|
*/
|
|
/**
|
|
* Run external command
|
|
*
|
|
* @param $command string|array The command. It's could be an array of the command with its
|
|
* arguments.
|
|
* @param $data_stdin string|null The command arguments (optional, default: null)
|
|
* @param $escape_command_args boolean If true, the command will be escaped
|
|
* (optional, default: true)
|
|
* @param string|null $cwd The initial working dir for the command
|
|
* (optional, default: null = use current PHP process working
|
|
* directory)
|
|
*
|
|
* @return false|array An array of return code, stdout and stderr result or False in case of fatal
|
|
* error
|
|
**/
|
|
function run_external_command($command, $data_stdin=null, $escape_command_args=true, $cwd=null) {
|
|
if (is_array($command))
|
|
$command = implode(' ', $command);
|
|
if ($escape_command_args)
|
|
$command = escapeshellcmd($command);
|
|
Log :: debug("Run external command: '$command'");
|
|
$descriptorspec = array(
|
|
0 => array("pipe", "r"), // stdin
|
|
1 => array("pipe", "w"), // stdout
|
|
2 => array("pipe", "w"), // stderr
|
|
);
|
|
$process = proc_open($command, $descriptorspec, $pipes, $cwd);
|
|
|
|
if (!is_resource($process)) {
|
|
Log :: error("Fail to run external command: '$command'");
|
|
return false;
|
|
}
|
|
|
|
if (!is_null($data_stdin)) {
|
|
fwrite($pipes[0], $data_stdin);
|
|
}
|
|
fclose($pipes[0]);
|
|
|
|
$stdout = stream_get_contents($pipes[1]);
|
|
fclose($pipes[1]);
|
|
|
|
$stderr = stream_get_contents($pipes[2]);
|
|
fclose($pipes[2]);
|
|
|
|
$return_value = proc_close($process);
|
|
|
|
$error = (!empty($stderr) || $return_value != 0);
|
|
Log :: log(
|
|
($error?'ERROR':'DEBUG'),
|
|
"External command ".($error?"error":"result").":\n".
|
|
"\tCommand : $command\n".
|
|
"\tReturn code: $return_value\n".
|
|
"\tOutput:\n".
|
|
"\t\t- Stdout :\n$stdout\n\n".
|
|
"\t\t- Stderr :\n$stderr"
|
|
);
|
|
|
|
return array($return_value, $stdout, $stderr);
|
|
}
|
|
|
|
|
|
/**
|
|
* Check an AJAX request and trigger a fatal error on fail
|
|
*
|
|
* Check if session key is present and valid and set AJAX
|
|
* mode.
|
|
*
|
|
* @param string|null $session_key string The current session key (optional)
|
|
*
|
|
* @return void
|
|
**/
|
|
function check_ajax_request($session_key=null) {
|
|
Url :: api_mode(true);
|
|
|
|
if (Session :: check_key($session_key))
|
|
Tpl :: fatal_error('Invalid request');
|
|
|
|
if (Tpl :: debug_ajax())
|
|
Log :: debug("Ajax Request : ".vardump($_REQUEST));
|
|
}
|
|
|
|
/**
|
|
* Just mark message for translation
|
|
* @param string $msg The message to translate
|
|
* @return string The message without transformation
|
|
*/
|
|
function ___($msg) {
|
|
return $msg;
|
|
}
|
|
|
|
/**
|
|
* Format duration
|
|
* @param int|float $value The duration to format
|
|
* @param string|null $unit The unit of provide value (optional, default: 's')
|
|
* @param string|null $precision Minimal precision to displayed (optional, default: 's')
|
|
* @param string|null $separator The separator between time block (optional, default: null = one space)
|
|
* @return string|false The formatted duration as string, or false in case of error
|
|
*/
|
|
function format_duration($value, $unit=null, $precision=null, $separator=null) {
|
|
$units = array(
|
|
___('d') => 86400000000000,
|
|
___('h') => 3600000000000,
|
|
___('m') => 60000000000,
|
|
___('s') => 1000000000,
|
|
___('ms') => 1000000,
|
|
___('ns') => 1,
|
|
);
|
|
if (!$unit) $unit = 's';
|
|
if (!array_key_exists($unit, $units)) return false;
|
|
if (!$precision) $precision = 's';
|
|
if (!array_key_exists($precision, $units)) return false;
|
|
if ($value == 0) return "0".I18n :: _($precision);
|
|
$value = $value * $units[$unit];
|
|
$result = array();
|
|
foreach ($units as $unit => $factor) {
|
|
if ($value >= $factor) {
|
|
$vunit = floor($value/$factor);
|
|
$value = $value - ($vunit * $factor);
|
|
$result[] = $vunit._($unit);
|
|
}
|
|
if ($unit == $precision) break;
|
|
}
|
|
// 0 ?
|
|
if (empty($result)) return I18n :: _('Less than 1%s', I18n :: _($precision));
|
|
return implode(is_null($separator)?' ':strval($separator), $result);
|
|
}
|
|
|
|
/**
|
|
* Implode array's keys & values (ex: 'k1=v1, k2=v2, ...')
|
|
* @param array<string|int,mixed> $values Array to implode
|
|
* @param boolean $quoted Set to false to disable values quotting (optional, default: true)
|
|
* @param string $separator Values separator (opional, default: ", ")
|
|
* @param string $kv_separator Key/value separator (optional, default: "=")
|
|
* @return string Imploded array string
|
|
*/
|
|
function implode_with_keys($values, $quoted=true, $separator=', ', $kv_separator='=') {
|
|
$result = [];
|
|
$quoted = $quoted?'"':'';
|
|
foreach ($values as $key => $value)
|
|
$result[] = "$key$kv_separator$quoted$value$quoted";
|
|
return implode($separator, $result);
|
|
}
|
|
|
|
/**
|
|
* Generate UUID
|
|
* @return string UUID
|
|
*/
|
|
function generate_uuid() {
|
|
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
|
// 32 bits for "time_low"
|
|
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
|
|
|
|
// 16 bits for "time_mid"
|
|
mt_rand( 0, 0xffff ),
|
|
|
|
// 16 bits for "time_hi_and_version",
|
|
// four most significant bits holds version number 4
|
|
mt_rand( 0, 0x0fff ) | 0x4000,
|
|
|
|
// 16 bits, 8 bits for "clk_seq_hi_res",
|
|
// 8 bits for "clk_seq_low",
|
|
// two most significant bits holds zero and one for variant DCE1.1
|
|
mt_rand( 0, 0x3fff ) | 0x8000,
|
|
|
|
// 48 bits for "node"
|
|
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get system timezone
|
|
* @param bool $as_string Set to true to retrieve timezone name instead of a DateTimeZone object
|
|
* @return ($as_string is true ? string : \DateTimeZone) System timezone
|
|
*/
|
|
function get_system_timezone($as_string=false) {
|
|
$timezone = trim(
|
|
$_SERVER['TZ'] ??
|
|
(file_get_contents('/etc/timezone') ?: file_get_contents('/etc/localtime'))
|
|
);
|
|
return $as_string?$timezone:new \DateTimeZone($timezone);
|
|
}
|
|
|
|
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|