eesyphp/includes/functions.php
Benjamin Renard 6fdc5447f1 Some improvments from recent works on apps based on its "framework"
* Code cleaning and fix some small errors using Phpstan
* Configure pre-commit to run Phpstan before each commit
* Some little improvments and logging, mail, smarty & URL libs
* Add Sentry integration
* Add Webstat JS code inclusion
* Install Smarty dependency using composer

Breaking changes:
* Rename Event class as HookEvent to avoid conflict with PECL event
* URL with refresh GET parameter now automatically trigger redirection without it
 after page loading to avoid to keep it in URL
2023-01-29 11:51:41 +01:00

389 lines
10 KiB
PHP

<?php
/*
* Check values helpers
*/
function check_name($name) {
if (preg_match('/^[\w \-]{2,}$/iu',$name))
return true;
return false;
}
function check_id(&$id) {
if (is_int($id))
return true;
if (preg_match('/^[0-9]+$/', $id)) {
$id = intval($id);
return true;
}
return false;
}
function check_search_pattern($pattern) {
foreach(preg_split('/\s+/', trim($pattern)) as $word) {
if (!check_id($word) && !check_name($word))
return false;
}
return true;
}
function check_time(&$time) {
if (!is_int($time)) {
if (preg_match('/^[0-9]+$/', $time))
$time = intval($time);
else
return false;
}
return ($time >= 1577833200); // 2020-01-01 - date of birth of this soft
}
function check_status($status) {
global $status_list;
return array_key_exists($status, $status_list);
}
function check_description($comment) {
if (preg_match("/^[\p{L}0-9\p{P}\p{Zs}\p{Zl}\p{Sc}\=\+]+$/uim", $comment))
return true;
return false;
}
function check_email($value, $domain=NULL, $checkDns=true) {
$regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/';
if (!preg_match($regex, $value)) {
return false;
}
$nd = explode('@', $value);
$nd=$nd[1];
if ($domain) {
if(is_array($domain)) {
if (!in_array($nd,$domain)) {
return false;
}
}
else {
if($nd!=$domain) {
return false;
}
}
}
if ($checkDns && function_exists('checkdnsrr')) {
if (!(checkdnsrr($nd, 'MX') || checkdnsrr($nd, 'A'))) {
return false;
}
}
return true;
}
/*
* Handling item POST data
*/
function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=null, &$item=null,
&$changes=null) {
$field_errors=array();
if (isset($_POST['submit'])) {
logging('DEBUG', 'POST data : '.vardump($_POST));
// Name
if (!$enabled_fields || in_array('name', $enabled_fields)) {
if (isset($_POST['name'])) {
if (check_name($_POST['name'])) {
$info['name'] = $_POST['name'];
}
else {
$field_errors['name'] = "Ce nom est invalide.";
}
}
else {
$field_errors['name'] = "Cette information est obligatoire.";
}
}
// status
if (!$enabled_fields || in_array('status', $enabled_fields)) {
if (isset($_POST['status']) && check_status($_POST['status'])) {
$info['status'] = $_POST['status'];
}
else {
$field_errors['status'] = "Cette information est obligatoire.";
}
}
// description
if (
isset($_POST['description']) &&
(!$enabled_fields || in_array('description', $enabled_fields))
) {
if (check_is_empty(trim($_POST['description']))) {
$info['description'] = null;
}
else if (check_description($_POST['description'])) {
$info['description'] = $_POST['description'];
}
else {
$field_errors['description'] = "Cette description est invalide.";
}
}
}
// Check custom required fields
if (is_array($required_fields)) {
foreach ($required_fields as $field) {
if (array_key_exists($field, $field_errors))
continue;
if (array_key_exists($field, $info) && !is_null($info[$field]) && !check_is_empty($info))
continue;
$field_errors[$field] = "Cette information est obligatoire.";
}
}
if (empty($field_errors) && is_array($item) && !is_null($changes)) {
$changes = array();
foreach ($info as $key => $value) {
if ($value != $item[$key])
$changes[$key] = $value;
}
}
return $field_errors;
}
/*
* Parser/formater values helpers
*/
$_date_format = "%d/%m/%Y";
$_date_time_format = "%d/%m/%Y %H:%M:%S";
function format_time($time, $with_time=true) {
global $_date_format, $_date_time_format;
if ($with_time)
return strftime($_date_time_format, $time);
return strftime($_date_format, $time);
}
function parse_date($date, $with_time=true) {
global $_date_format, $_date_time_format;
if ($with_time)
$ptime = strptime($date, $_date_time_format);
else
$ptime = strptime($date, $_date_format);
if(is_array($ptime)) {
return mktime(
$ptime['tm_hour'],
$ptime['tm_min'],
$ptime['tm_sec'],
$ptime['tm_mon']+1,
$ptime['tm_mday'],
$ptime['tm_year']+1900
);
}
return false;
}
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";
}
function can_modify($item) {
return can_do(
$item,
array('pending')
);
}
function can_archive($item) {
return can_do(
$item,
array('refused', 'validated')
);
}
function can_delete($item) {
return can_do(
$item,
array('archived')
);
}
function can_do($item, $status=array()) {
return in_array($item['status'], $status);
}
/*
* 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);
}
function check_is_empty($val) {
switch(gettype($val)) {
case "boolean":
case "integer":
case "double":
case "object":
case "resource":
return False;
case "array":
case "string":
if ($val == "0") return false;
return empty($val);
case "NULL":
return True;
}
}
/*
* Generic file/directory helpers
*/
function dump_file($file_path, $max_age=3600) {
if (is_file($file_path)) {
header('Content-Type: '.mime_content_type($file_path));
$last_modified_time = filemtime($file_path);
$etag = md5_file($file_path);
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();
}
header("HTTP/1.1 404 Not found");
exit();
}
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)) {
logging('ERROR', "delete_directory($dir) : Fail to delete sub-directory '$dir/$file'.");
return false;
}
}
else if (!unlink("$dir/$file")) {
logging('ERROR', "delete_directory($dir) : Fail to delete '$dir/$file'.");
return false;
}
}
}
else if (!empty($files)) {
logging('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)
*
* @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) {
if (is_array($command))
$command = implode(' ', $command);
if ($escape_command_args)
$command = escapeshellcmd($command);
logging('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);
if (!is_resource($process)) {
logging('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);
logging(
($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);
}
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab