Move functions and logging stuff in EesyPHP namespace

This commit is contained in:
Benjamin Renard 2023-01-29 17:36:21 +01:00
parent 6fdc5447f1
commit 3b45768967
21 changed files with 895 additions and 523 deletions

View file

@ -5,4 +5,4 @@ require realpath(dirname(__FILE__).'/..')."/includes/core.php";
if (is_callable('handle_cli_args'))
handle_cli_args();
else
logging('FATAL', "An error occured initializing CLI : handle_cli_args() function not found.");
Log :: fatal("An error occured initializing CLI : handle_cli_args() function not found.");

View file

@ -7,6 +7,14 @@
"email": "info@easter-eggs.com"
}
],
"autoload": {
"psr-4": {
"EesyPHP\\": "src/"
},
"files": [
"src/functions.php"
]
},
"require": {
"envms/fluentpdo": "^1.1",
"pear/console_table": "^1.3",

View file

@ -1,16 +1,19 @@
<?php
use EesyPHP\Check;
use EesyPHP\Log;
$cli_commands=array();
function add_cli_command($command, $handler, $short_desc, $usage_args=false, $long_desc=false,
$override=false) {
global $cli_commands;
if (array_key_exists($command, $cli_commands) && !$override) {
logging('ERROR', _("The CLI command '%s' already exists."), $command);
Log :: error(_("The CLI command '%s' already exists."), $command);
return False;
}
if (!is_callable($handler)) {
logging('ERROR', _("The CLI command '%s' handler is not callable !"), $command);
Log :: error(_("The CLI command '%s' handler is not callable !"), $command);
return False;
}
@ -135,8 +138,8 @@ function handle_cli_args() {
if (!$cli_command)
usage();
logging(
'DEBUG', "Run %s command %s with argument(s) '%s'.",
Log :: debug(
"Run %s command %s with argument(s) '%s'.",
basename($argv[0]), $cli_command, implode("', '", $command_args)
);
@ -146,7 +149,7 @@ function handle_cli_args() {
exit($result?0:1);
}
catch(Exception $e) {
log_exception($e, _("An exception occured running command %s"), $cli_command);
Log :: exception($e, _("An exception occured running command %s"), $cli_command);
exit(1);
}
}
@ -208,7 +211,7 @@ function cli_list($command_args) {
$items = search_items($params);
if (!is_array($items)) {
logging("ERROR", "Invalid DB info return.\n");
Log :: error("Invalid DB info return.\n");
return False;
}
@ -257,14 +260,14 @@ add_cli_command(
);
function cli_show($command_args) {
if (count($command_args) != 1 || !check_id($command_args[0]))
if (count($command_args) != 1 || !Check :: id($command_args[0]))
usage(_('You must provide a valid ID.'));
$item_id = $command_args[0];
$item = get_item($item_id);
if (!$item)
logging('FATAL', _("Item #%s not found."), $item_id);
Log :: fatal(_("Item #%s not found."), $item_id);
print_item_info($item);
return True;
@ -281,14 +284,14 @@ function cli_delete($command_args) {
usage(_('You must provide item ID.'));
// Check URI
if (!check_id($command_args[0]))
logging('FATAL', _("Invalid item ID"));
if (!Check :: id($command_args[0]))
Log :: fatal(_("Invalid item ID"));
// Check exist
$item_id = $command_args[0];
$item = get_item($item_id);
if (!$item)
logging('FATAL', _("Item #%s not found."), $item_id);
Log :: fatal(_("Item #%s not found."), $item_id);
print_item_info($item);
@ -297,13 +300,13 @@ function cli_delete($command_args) {
$handle = fopen ("php://stdin","r");
$line = fgets($handle);
if(trim($line) != 'yes'){
logging('WARNING', _("User cancel"));
Log :: warning(_("User cancel"));
exit;
}
echo "\n";
if (!delete_item($item['id']))
logging('FATAL', _("An error occured deleting item #%d."), $item_id);
Log :: fatal(_("An error occured deleting item #%d."), $item_id);
return True;
}
@ -318,7 +321,7 @@ function cli_export($command_args) {
$fd = fopen((count($command_args) >= 1?$command_args[0]:'php://output'), 'w');
export_items($fd);
fclose($fd);
logging('INFO', "Items export to '".(count($command_args) >= 1?$command_args[0]:'STDOUT')."'.");
Log :: info("Items export to '".(count($command_args) >= 1?$command_args[0]:'STDOUT')."'.");
}
add_cli_command(
'export',
@ -331,8 +334,8 @@ function cli_restore($command_args) {
$fd = fopen((count($command_args) >= 1?$command_args[0]:'php://stdin'), 'r');
restore_items($fd);
fclose($fd);
logging(
'INFO', "Items restored from '%s'",
Log :: info(
"Items restored from '%s'",
(count($command_args) >= 1?$command_args[0]:'STDIN')
);
}
@ -354,7 +357,7 @@ function cli_cron($command_args) {
case '-m':
case '--max-age':
$i++;
if(!check_id($command_args[$i]))
if(!Check :: id($command_args[$i]))
usage('Invalid -m|--max-age clause');
$item_max_age = $command_args[$i];
break;
@ -368,37 +371,36 @@ function cli_cron($command_args) {
}
if (!is_int($item_max_age) || $item_max_age <= 0)
logging(
'FATAL',
Log :: fatal(
'Invalid $item_max_age value set in configuration: it\'s must be a positive integer.');
logging('DEBUG', "cli_cron(): item max age = $item_max_age day(s)");
Log :: debug("cli_cron(): item max age = $item_max_age day(s)");
$limit = time() - ($item_max_age * 86400);
logging('DEBUG', "Handle items expiration with creation date limit ".format_time($limit).".");
Log :: debug("Handle items expiration with creation date limit ".format_time($limit).".");
$items = search_items(array('all' => true));
$error = false;
foreach($items['items'] as $item) {
if ($item['date'] < $limit) {
if ($just_try) {
logging('DEBUG', 'Just-try mode: do not really delete item #%s (%s, creation date: %s)',
Log :: debug('Just-try mode: do not really delete item #%s (%s, creation date: %s)',
$item['id'], $item['name'], format_time($item['date'])
);
}
else if (delete_item($item['id'])) {
logging('INFO', 'Item #%s (%s) deleted (creation date: %s)',
Log :: info('Item #%s (%s) deleted (creation date: %s)',
$item['id'], $item['name'], format_time($item['date'])
);
}
else {
logging('ERROR', 'Fail to delete item "%s" (%s, creation date: %s)',
Log :: error('Fail to delete item "%s" (%s, creation date: %s)',
$item['id'], $item['name'], format_time($item['date'])
);
$error = true;
}
}
else {
logging('DEBUG', 'Item "%s" (%s) still valid (creation date: %s)',
Log :: debug('Item "%s" (%s) still valid (creation date: %s)',
$item['id'], $item['name'], format_time($item['date'])
);
}

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
// Root directory path
@ -43,7 +45,13 @@ $sentry_span = new SentrySpan('core.init', 'Core initialization');
if (isset($upload_tmp_dir))
ini_set('upload_tmp_dir', $upload_tmp_dir);
require_once('logging.php');
if (!isset($log_file))
die('Log file path not configured');
Log::init(
$log_file,
isset($log_level)?$log_level:null,
isset($log_php_errors_levels)?$log_php_errors_levels:null
);
require_once('functions.php');
require_once('session.php');

View file

@ -1,9 +1,10 @@
<?php
use EesyPHP\Log;
use Unidecode\Unidecode;
if (!isset($db_dsn)) {
logging('FATAL', 'Database DSN not configured');
Log :: fatal('Database DSN not configured');
exit(1);
}
@ -33,12 +34,12 @@ try {
}
}
logging('DEBUG',$msg);
Log :: debug($msg);
};
}
catch(Exception $e) {
logging('ERROR',"Fail to connect to DB (DSN : '$db_dsn') : ".$e->getMessage());
logging("FATAL", _('Unable to connect to the database.'));
Log :: error("Fail to connect to DB (DSN : '$db_dsn') : ".$e->getMessage());
Log :: fatal(_('Unable to connect to the database.'));
}
/*
@ -111,7 +112,7 @@ function get_items($orderby='id', $raw_values=false) {
}
}
catch (Exception $e) {
logging('ERROR', "Error retreiving items info from database : ".$e->getMessage());
Log :: error("Error retreiving items info from database : ".$e->getMessage());
}
return false;
}
@ -134,7 +135,7 @@ function get_item($id, $raw_values=false) {
}
}
catch (Exception $e) {
logging('ERROR', "Error retreiving item #$id info from database : ".$e->getMessage());
Log :: error("Error retreiving item #$id info from database : ".$e->getMessage());
}
return false;
}
@ -149,13 +150,13 @@ function add_item($values) {
if ($result !== false) {
$item = get_item($result);
logging('INFO', "New item #$result added");
Log :: info("New item #$result added");
trigger_hook('item_added', $item);
return $item;
}
}
catch (Exception $e) {
logging('ERROR', "Error creating item in database : ".$e->getMessage());
Log :: error("Error creating item in database : ".$e->getMessage());
}
return false;
}
@ -180,20 +181,20 @@ function update_item($id, $changes) {
-> execute();
if ($result !== false) {
logging('INFO', "Item #$id updated");
Log :: info("Item #$id updated");
trigger_hook('item_updated', $item);
return true;
}
}
catch (Exception $e) {
logging('ERROR', "Error updating item #$id in database : ".$e->getMessage());
Log :: error("Error updating item #$id in database : ".$e->getMessage());
}
return false;
}
function change_item_status($id, $status) {
if (update_item($id, array('status' => $status))) {
logging('INFO', "Status of item #$id changed to $status.");
Log :: info("Status of item #$id changed to $status.");
return true;
}
return false;
@ -211,12 +212,12 @@ function delete_item($id) {
-> execute();
if ($result !== false) {
logging('INFO', "Item #$id deleted");
Log :: info("Item #$id deleted");
return True;
}
}
catch (Exception $e) {
logging('ERROR', "Error deleting item #$id from database : ".$e->getMessage());
Log :: error("Error deleting item #$id from database : ".$e->getMessage());
}
return false;
}
@ -308,7 +309,7 @@ function search_items($params) {
-> execute();
if ($result === false) {
logging('ERROR', 'search_items() : search in DB return false');
Log :: error('search_items() : search in DB return false');
return false;
}
@ -344,7 +345,7 @@ function search_items($params) {
$result_count = $query_count -> execute();
if ($result_count === false) {
logging('DEBUG', 'search_items() : search for count in DB return false');
Log :: debug('search_items() : search for count in DB return false');
return False;
}
$count = $result_count -> fetch();
@ -360,7 +361,7 @@ function search_items($params) {
);
}
catch (Exception $e) {
log_exception(
Log :: exception(
$e, "An exception occured searching items with params %s infos from database : ",
preg_replace("/\n[ \t]*/", " ", print_r($params, true))
);
@ -403,12 +404,12 @@ function restore_items($fd=null) {
$result = $fpdo -> deleteFrom('item')
-> execute();
if ($result === false) {
logging('ERROR', "An unknown error occured truncating item table in database.");
Log :: error("An unknown error occured truncating item table in database.");
return false;
}
}
catch (Exception $e) {
logging('ERROR', "Error truncating item table in database : ".$e->getMessage());
Log :: error("Error truncating item table in database : ".$e->getMessage());
return false;
}
@ -450,18 +451,17 @@ function restore_items($fd=null) {
$restored++;
}
else {
logging('ERROR', "Unkwown error occured restoring item from line #$line :\n".print_r($values, true));
Log :: error("Unkwown error occured restoring item from line #$line :\n".print_r($values, true));
$error = true;
}
}
catch (Exception $e) {
logging(
'ERROR',
Log :: error(
"Error restoring item from line #$line : ".$e->getMessage()."\n".print_r($values, true));
$error = true;
}
}
logging('INFO', "$restored items restored");
Log :: info("$restored items restored");
// Trigger hooks
trigger_hook('items_restored');

View file

@ -1,85 +1,16 @@
<?php
use EesyPHP\Check;
use EesyPHP\Log;
/*
* 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
*/
@ -87,11 +18,11 @@ function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=nu
&$changes=null) {
$field_errors=array();
if (isset($_POST['submit'])) {
logging('DEBUG', 'POST data : '.vardump($_POST));
Log :: debug('POST data : '.vardump($_POST));
// Name
if (!$enabled_fields || in_array('name', $enabled_fields)) {
if (isset($_POST['name'])) {
if (check_name($_POST['name'])) {
if (Check :: name($_POST['name'])) {
$info['name'] = $_POST['name'];
}
else {
@ -118,10 +49,10 @@ function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=nu
isset($_POST['description']) &&
(!$enabled_fields || in_array('description', $enabled_fields))
) {
if (check_is_empty(trim($_POST['description']))) {
if (Check :: is_empty(trim($_POST['description']))) {
$info['description'] = null;
}
else if (check_description($_POST['description'])) {
else if (Check :: description($_POST['description'])) {
$info['description'] = $_POST['description'];
}
else {
@ -135,7 +66,7 @@ function handle_item_post_data(&$info, $enabled_fields=null, $required_fields=nu
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))
if (array_key_exists($field, $info) && !is_null($info[$field]) && !Check :: is_empty($info))
continue;
$field_errors[$field] = "Cette information est obligatoire.";
}
@ -251,23 +182,6 @@ function format_callable($callable) {
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
*/
@ -308,18 +222,18 @@ function delete_directory($dir, $recursive=true) {
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'.");
Log :: 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'.");
Log :: error("delete_directory($dir) : Fail to delete '$dir/$file'.");
return false;
}
}
}
else if (!empty($files)) {
logging('ERROR', "delete_directory($dir) : Directory is not empty.");
Log :: error("delete_directory($dir) : Directory is not empty.");
return false;
}
return rmdir($dir);
@ -345,7 +259,7 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t
$command = implode(' ', $command);
if ($escape_command_args)
$command = escapeshellcmd($command);
logging('DEBUG', "Run external command: '$command'");
Log :: debug("Run external command: '$command'");
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
@ -354,7 +268,7 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t
$process = proc_open($command, $descriptorspec, $pipes);
if (!is_resource($process)) {
logging('ERROR', "Fail to run external command: '$command'");
Log :: error("Fail to run external command: '$command'");
return false;
}
@ -372,7 +286,7 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t
$return_value = proc_close($process);
$error = (!empty($stderr) || $return_value != 0);
logging(
Log :: log(
($error?'ERROR':'DEBUG'),
"External command ".($error?"error":"result").":\n".
"\tCommand : $command\n".

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
$hooks = array();
/**
@ -44,22 +46,21 @@ function trigger_hook($event_name, $event_data=null) {
call_user_func_array($e['callable'],array($event, &$e['param']));
}
catch(Exception $e) {
log_exception(
Log :: exception(
$e, "An exception occured running hook ".format_callable($e['callable']).
" on event $event_name");
$return = false;
}
}
else {
logging(
'ERROR',
Log :: error(
"The hook ".format_callable($e['callable'])." on event $event_name is not callable.");
$return = false;
}
}
}
else
logging('DEBUG', "No hook registered for event $event_name.");
Log :: debug("No hook registered for event $event_name.");
// Handle 'all' event
if ($event_name != 'all') {

View file

@ -1,256 +0,0 @@
<?php
/*
* Configuration :
* // Log file
* $log_file='/path/to/app.log';
*
* // Log level (DEBUG / INFO / WARNING / ERROR / FATAL)
* $log_level='INFO';
*
* // Log PHP errors levels (as specified to set_error_handler())
* // $log_php_errors_levels = E_ALL & ~E_STRICT;
* // Log PHP errors levels (as specified to set_error_handler())
* // Default:
* // - In TRACE or DEBUG: E_ALL & ~E_STRICT
* // - Otherwise: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
* // $log_php_errors_levels = E_ALL & ~E_STRICT;
*/
// Log file descriptor (Do not change !!!)
$_log_file_fd = null;
// Log Levels
$_log_levels = array(
'TRACE' => 0,
'DEBUG' => 1,
'INFO' => 2,
'WARNING' => 3,
'ERROR' => 4,
'FATAL' => 5,
);
// Custom fatal error handler
$_fatal_error_handler = null;
/**
* Log a message
* @param string $level The message level (key of $_log_levels)
* @param string $message The message to log
* @return true
*/
function logging($level, $message) {
global $log_file, $_log_file_fd, $_log_levels, $log_level, $argv,
$_fatal_error_handler, $auth_user;
if (!array_key_exists($level, $_log_levels)) $level = 'INFO';
$level_id = $_log_levels[$level];
$log_level_id = $_log_levels[$log_level];
if ($level_id < $log_level_id) return true;
if(is_null($_log_file_fd)) {
$_log_file_fd = fopen($log_file, 'a');
}
// If more than 2 arguments passed, format message using sprintf
if (func_num_args() > 2) {
$message = call_user_func_array(
'sprintf',
array_merge(array($message), array_slice(func_get_args(), 2))
);
}
if (php_sapi_name() == "cli") {
$msg = implode(' - ', array(
date('Y/m/d H:i:s'),
basename($argv[0]),
$level,
$message
))."\n";
}
else {
$msg = array(
date('Y/m/d H:i:s'),
$_SERVER['REQUEST_URI'],
$_SERVER['REMOTE_ADDR'],
);
if (isset($auth_user))
$msg[] = ($auth_user['username']?$auth_user['username']:'anonymous');
$msg[] = $level;
$msg[] = $message;
$msg = implode(' - ', $msg)."\n";
}
fwrite($_log_file_fd, $msg);
if ($level == 'FATAL')
if (!is_null($_fatal_error_handler))
call_user_func($_fatal_error_handler, $message);
elseif (function_exists('fatal_error'))
fatal_error($message);
else
die("\n$message\n\n");
elseif (php_sapi_name() == "cli")
echo $msg;
return true;
}
// Set default log level (if not defined or invalid)
$default_log_level = 'WARNING';
if (!isset($log_level)) {
$log_level = $default_log_level;
}
elseif (!array_key_exists($log_level, $_log_levels)) {
$invalid_value = $log_level;
$log_level = $default_log_level;
logging(
$log_level, "Invalid log level value found in configuration (%s). ".
"Set as default (%s).", $invalid_value, $log_level);
}
/**
* Register a contextual fatal error handler
* @param null|callable $handler The fatal error handler (set as null to reset)
* @return void
*/
function register_fatal_error_handler($handler) {
// @phpstan-ignore-next-line
if ($handler && !is_callable($handler))
logging('FATAL', 'Fatal handler provided is not callable !');
global $_fatal_error_handler;
$_fatal_error_handler = ($handler?$handler:null);
}
/**
* Change of current log file
* @param string $file The new log file path
* @return bool
*/
function change_log_file($file) {
global $log_file, $_log_file_fd;
if ($file == $log_file) return True;
if ($_log_file_fd) {
fclose($_log_file_fd);
$_log_file_fd = false;
}
$log_file = $file;
return True;
}
/*
*******************************************************************************
* Handle exception logging
*******************************************************************************
*/
/**
* Get the current backtrace
* @param int $ignore_last The number of last levels to ignore
* @return string
*/
function get_debug_backtrace_context($ignore_last=0) {
$traces = debug_backtrace();
// Also ignore this function it self
$ignore_last++;
if (!is_array($traces) || count($traces) <= $ignore_last)
return "";
$msg = array();
for ($i=$ignore_last; $i < count($traces); $i++) {
$trace = array("#$i");
if (isset($traces[$i]['file']))
$trace[] = $traces[$i]['file'].(isset($traces[$i]['line'])?":".$traces[$i]['line']:"");
// @phpstan-ignore-next-line
if (isset($traces[$i]['class']) && isset($traces[$i]['function']))
$trace[] = implode(" ", array(
$traces[$i]['class'],
$traces[$i]['type'],
$traces[$i]['function']. "()"));
elseif (isset($traces[$i]['function']))
$trace[] = $traces[$i]['function']. "()";
$msg[] = implode(" - ", $trace);
}
return implode("\n", $msg);
}
/**
* Log an exception
* @param Throwable $exception
* @param string|null $prefix The prefix of the log message
* (optional, default: "An exception occured")
* @return void
*/
function log_exception($exception, $prefix=null) {
if (function_exists('log_in_sentry'))
log_in_sentry($exception);
// If more than 2 arguments passed, format prefix message using sprintf
if ($prefix && func_num_args() > 2) {
$prefix = call_user_func_array(
'sprintf',
array_merge(array($prefix), array_slice(func_get_args(), 2))
);
}
logging(
"ERROR", "%s:\n%s\n## %s:%d : %s",
($prefix?$prefix:"An exception occured"),
get_debug_backtrace_context(1),
$exception->getFile(), $exception->getLine(),
$exception->getMessage());
}
set_exception_handler('log_exception');
/*
*******************************************************************************
* Handle PHP error logging
*******************************************************************************
*/
/**
* Convert PHP error number to the corresponding label
* @param int $errno
* @return string
*/
function errno2type($errno) {
$constants = get_defined_constants();
if (is_array($constants))
foreach($constants as $label => $value)
if ($value == $errno && preg_match('/^E_(.*)$/', $label, $m))
return $m[1];
return 'UNKNOWN ERROR #'.$errno;
}
/**
* Log a PHP error
* Note: method design to be used as callable by set_error_handler()
* @param int $errno The error number
* @param string $errstr The error message
* @param string $errfile The filename that the error was raised in
* @param int $errline The line number where the error was raised
* @return false Return false to let the normal error handler continues.
*/
function log_php_error($errno, $errstr, $errfile, $errline) {
$msg = sprintf(
"A PHP error occured : [%s] %s\nFile : %s (line : %d)",
errno2type($errno), $errstr, $errfile, $errline
);
logging("ERROR", $msg);
if (function_exists('log_php_error_in_sentry'))
log_php_error_in_sentry($errno, $msg);
return False;
}
if (isset($log_php_errors_levels))
set_error_handler('log_php_error', $log_php_errors_levels);
elseif (in_array($log_level, array('DEBUG', 'TRACE')))
set_error_handler('log_php_error', E_ALL & ~E_STRICT);
else
set_error_handler('log_php_error', E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
// Load PHP PEAR Mail and Mail_mime libs
require_once(isset($php_mail_path)?$php_mail_path:"Mail.php");
require_once(isset($php_mail_mime_path)?$php_mail_mime_path:"Mail/mime.php");
@ -30,14 +32,14 @@ function send_mail($from, $to, $subject, $msg, $html=false, $attachments=null, $
$headers = array_merge($headers, $mail_headers);
}
logging(
'TRACE', 'Mail catch all: %s',
Log :: trace(
'Mail catch all: %s',
isset($mail_catch_all) && $mail_catch_all?
vardump($mail_catch_all):'not set'
);
if (isset($mail_catch_all) && $mail_catch_all) {
logging(
'DEBUG', 'Mail catch to %s',
Log :: debug(
'Mail catch to %s',
is_array($mail_catch_all)?implode(',', $mail_catch_all):$mail_catch_all
);
$msg .= sprintf(
@ -76,7 +78,7 @@ function send_mail($from, $to, $subject, $msg, $html=false, $attachments=null, $
foreach(array_keys($headers) as $header) {
if(in_array(strtoupper($header), array('BCC', 'CC'))) {
if (isset($mail_catch_all) && $mail_catch_all) {
logging('DEBUG', "Mail catched: remove $header header");
Log :: debug("Mail catched: remove $header header");
$msg .= sprintf(
(
$html?
@ -126,7 +128,7 @@ function send_mail($from, $to, $subject, $msg, $html=false, $attachments=null, $
if (PEAR::isError($ret)) {
$msg = "Error sending email: ".$ret -> getMessage();
logging('ERROR', $msg);
Log :: error($msg);
return false;
}
return true;

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
/*
* Configuration :
*
@ -17,7 +19,7 @@ if (isset($sentry_dsn) && $sentry_dsn) {
\Sentry\init([
'dsn' => $sentry_dsn,
'traces_sample_rate' => (
isset($sentry_traces_sample_rate) ?
isset($sentry_traces_sample_rate) ?
$sentry_traces_sample_rate : 0.2
),
]);
@ -35,21 +37,21 @@ if (isset($sentry_dsn) && $sentry_dsn) {
/**
* Log an exception or a message in Sentry
* @param string|Exception $msg
* @param string|Throwable $msg
* @return void
*/
function log_in_sentry($msg) {
global $sentry_dsn;
if (!isset($sentry_dsn) || !$sentry_dsn) {
logging('TRACE', 'Sentry DSN not configured, do not log this error');
Log :: trace('Sentry DSN not configured, do not log this error');
return;
}
if (is_string($msg)) {
logging('DEBUG', 'Error logged in Sentry');
Log :: debug('Error logged in Sentry');
\Sentry\captureMessage($msg);
}
elseif ($msg instanceof Exception) {
logging('DEBUG', 'Exception logged in Sentry');
Log :: debug('Exception logged in Sentry');
\Sentry\captureException($msg);
}
}

View file

@ -1,4 +1,7 @@
<?php
use EesyPHP\Log;
if (php_sapi_name() == "cli")
return true;
@ -19,18 +22,17 @@ if (!isset($_SESSION['session_key'])) {
// Handle session timeout
if (isset($session_timeout) && $session_timeout) {
if (!isset($_SESSION['session_last_access'])) {
logging('DEBUG', 'Set initial session last access');
Log :: debug('Set initial session last access');
$_SESSION['session_last_access'] = time();
}
elseif ($_SESSION['session_last_access'] > (time() - $session_timeout)) {
logging(
'DEBUG',
Log :: debug(
'Session timeout not expired, update session last access '.
'(Previous value : '.$_SESSION['session_last_access'].')');
$_SESSION['session_last_access'] = time();
}
else {
logging('INFO', 'Session destroyed due to inactivity');
Log :: info('Session destroyed due to inactivity');
session_destroy();
}
}

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
if (php_sapi_name() == "cli")
return true;
@ -57,7 +59,7 @@ elseif (method_exists($smarty,'registerPlugin')) {
}
}
else {
logging('FATAL', _('Smarty version not supported.'));
Log :: fatal(_('Smarty version not supported.'));
}
// Configure templates/templates_c directories
@ -65,8 +67,8 @@ if (
!isset($smarty_templates_dir)
|| !is_dir($smarty_templates_dir)
)
logging(
'FATAL', "Template directory not found (%s)",
Log :: fatal(
"Template directory not found (%s)",
isset($smarty_templates_dir)?$smarty_templates_dir:'not set');
else
$smarty->setTemplateDir($smarty_templates_dir);
@ -76,8 +78,8 @@ if (
|| !is_dir($smarty_templates_c_dir)
|| !is_writable($smarty_templates_c_dir)
)
logging(
'FATAL', "Template cache directory not found or not writable (%s)",
Log :: fatal(
"Template cache directory not found or not writable (%s)",
isset($smarty_templates_c_dir)?$smarty_templates_c_dir:'not set');
else
$smarty->setCompileDir($smarty_templates_c_dir);
@ -179,7 +181,7 @@ function _defineCommonTemplateVariables($template, $pagetitle) {
function display_template($template, $pagetitle=false) {
if (!$template)
logging("FATAL", _("No template specified."));
Log :: fatal(_("No template specified."));
// If refresh parameter is present, remove it and redirect
if (isset($_GET['refresh'])) {
@ -208,9 +210,9 @@ function display_template($template, $pagetitle=false) {
unset($_SESSION['messages']);
}
catch (Exception $e) {
log_exception($e, "Smarty - An exception occured displaying template '$template'");
Log :: exception($e, "Smarty - An exception occured displaying template '$template'");
if ($template != 'fatal_error.tpl')
logging("FATAL", _("An error occurred while displaying this page."));
Log :: fatal(_("An error occurred while displaying this page."));
}
$sentry_span->finish();
@ -234,7 +236,7 @@ function display_ajax_return($data=null, $pretty=false) {
unset($_SESSION['errors']);
}
if ($debug_ajax)
logging('DEBUG',"Ajax Response : ".vardump($data));
Log :: debug("Ajax Response : ".vardump($data));
header('Content-Type: application/json');
echo json_encode($data, (($pretty||isset($_REQUEST['pretty']))?JSON_PRETTY_PRINT:0));
exit();

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
/*
********************************************************************
* Translations CLI commands *
@ -56,7 +58,7 @@ function cli_extract_messages($command_args) {
false // do not escape command args (already done)
);
if (!is_array($php_files) || $php_files[0] != 0) {
logging('FATAL', _("Fail to list PHP files."));
Log :: fatal(_("Fail to list PHP files."));
}
// Extract messages from PHP files using xgettext
@ -73,7 +75,7 @@ function cli_extract_messages($command_args) {
$php_files[1] // Pass PHP files list via STDIN
);
if (!is_array($result) || $result[0] != 0)
logging('FATAL', _("Fail to extract messages from PHP files using xgettext."));
Log :: fatal(_("Fail to extract messages from PHP files using xgettext."));
// List JS files to parse
@ -83,7 +85,7 @@ function cli_extract_messages($command_args) {
false // do not escape command args (already done)
);
if (!is_array($js_files) || $js_files[0] != 0) {
logging('FATAL', _("Fail to list JS files."));
Log :: fatal(_("Fail to list JS files."));
}
// Extract messages from JS files using xgettext
@ -100,7 +102,7 @@ function cli_extract_messages($command_args) {
$js_files[1] // Pass JS files list via STDIN
);
if (!is_array($result) || $result[0] != 0)
logging('FATAL', _("Fail to extract messages from JS files using xgettext."));
Log :: fatal(_("Fail to extract messages from JS files using xgettext."));
// Extract messages from templates files using tsmarty2c.php
$result = run_external_command(
@ -111,8 +113,7 @@ function cli_extract_messages($command_args) {
)
);
if (!is_array($result) || $result[0] != 0)
logging(
'FATAL',
Log :: fatal(
_("Fail to extract messages from template files using tsmarty2c.php script."));
$fd = fopen("$root_lang_dir/headers.pot", 'w');
@ -139,7 +140,7 @@ function cli_extract_messages($command_args) {
"-o", "$root_lang_dir/messages.pot",
));
if (!is_array($result) || $result[0] != 0)
logging('FATAL', _("Fail to merge messages using msgcat."));
Log :: fatal(_("Fail to merge messages using msgcat."));
}
add_cli_command(
'extract_messages',
@ -162,14 +163,14 @@ function cli_update_messages($command_args) {
$compendium_args = array();
foreach ($command_args as $path) {
if (!file_exists($path))
logging('FATAL', _("Compendium file %s not found."), $path);
Log :: fatal(_("Compendium file %s not found."), $path);
$compendium_args[] = '-C';
$compendium_args[] = $path;
}
$pot_file = "$root_lang_dir/messages.pot";
if (!is_file($pot_file))
logging('FATAL', _("POT file not found (%s). Please run extract_messages first."), $pot_file);
Log :: fatal(_("POT file not found (%s). Please run extract_messages first."), $pot_file);
if ($dh = opendir($root_lang_dir)) {
$error = False;
@ -181,13 +182,13 @@ function cli_update_messages($command_args) {
)
continue;
logging('DEBUG', _("Lang directory '%s' found"), $file);
Log :: debug(_("Lang directory '%s' found"), $file);
// Check LC_MESSAGES directory exists
$lang = $file;
$lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ;
if (!is_dir($lang_dir)) {
logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
Log :: debug(_("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
$lang);
continue;
}
@ -202,7 +203,7 @@ function cli_update_messages($command_args) {
if (is_array($result) && $result[0] == 0) {
$created = true;
} else {
logging('ERROR', _("Fail to init messages in %s PO file using msginit (%s)."),
Log :: error(_("Fail to init messages in %s PO file using msginit (%s)."),
$lang, $po_file);
$error = True;
}
@ -220,20 +221,20 @@ function cli_update_messages($command_args) {
)
);
if (!is_array($result) || $result[0] != 0) {
logging('ERROR', _("Fail to update messages in %s PO file using msgmerge (%s)."),
Log :: error(_("Fail to update messages in %s PO file using msgmerge (%s)."),
$lang, $po_file);
$error = True;
}
}
elseif (!$created) {
logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."), $lang);
Log :: debug(_("PO file not found in lang '%s' directory, ignore it."), $lang);
}
}
closedir($dh);
return !$error;
}
logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
Log :: fatal(_("Fail to open root lang directory (%s)."), $root_dir_path);
return false;
}
add_cli_command(
@ -268,29 +269,28 @@ function cli_compile_messages($command_args) {
if (dirname($real_lang_dir) != '.' || !is_dir($root_lang_dir . '/' . $real_lang_dir))
continue;
$lang = $file;
logging('DEBUG', _("Lang alias symlink found: %s -> %s"), $lang, $real_lang_dir);
Log :: debug(_("Lang alias symlink found: %s -> %s"), $lang, $real_lang_dir);
// Create JSON catalog symlink (if not exists)
$js_link = "$root_dir_path/public_html/translations/$lang.js";
$link_target = "$real_lang_dir.js";
if (!file_exists($js_link)) {
if (symlink($link_target, $js_link)) {
logging('INFO', _("JSON catalog symlink for %s -> %s created (%s)"),
Log :: info(_("JSON catalog symlink for %s -> %s created (%s)"),
$lang, $real_lang_dir, $js_link);
}
else {
logging('ERROR', _("Fail to create JSON catalog symlink for %s -> %s (%s)"),
Log :: error(_("Fail to create JSON catalog symlink for %s -> %s (%s)"),
$lang, $real_lang_dir, $js_link);
$error = True;
}
}
elseif (readlink($js_link) == $link_target) {
logging('DEBUG', _("JSON catalog symlink for %s -> %s already exist (%s)"),
Log :: debug(_("JSON catalog symlink for %s -> %s already exist (%s)"),
$lang, $real_lang_dir, $js_link);
}
else {
logging(
'WARNING',
Log :: warning(
_("JSON catalog file for %s already exist, but it's not a symlink to %s (%s)"),
$lang, $real_lang_dir, $js_link
);
@ -299,13 +299,13 @@ function cli_compile_messages($command_args) {
continue;
}
logging('DEBUG', _("Lang directory '%s' found"), $file);
Log :: debug(_("Lang directory '%s' found"), $file);
// Check LC_MESSAGES directory exists
$lang = $file;
$lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ;
if (!is_dir($lang_dir)) {
logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
Log :: debug(_("LC_MESSAGES directory not found in lang '%s' directory, ignore it."),
$lang);
continue;
}
@ -313,7 +313,7 @@ function cli_compile_messages($command_args) {
// Test .PO file is present
$po_file = $lang_dir . '/' . TEXT_DOMAIN . '.po';
if (!is_file($po_file)) {
logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."),
Log :: debug(_("PO file not found in lang '%s' directory, ignore it."),
$lang);
continue;
}
@ -325,8 +325,7 @@ function cli_compile_messages($command_args) {
array("msgfmt", "-o", $mo_file, $po_file)
);
if (!is_array($result) || $result[0] != 0) {
logging(
'ERROR',
Log :: error(
_("Fail to compile messages from %s PO file as MO file using msgfmt (%s)."),
$lang, $po_file
);
@ -337,24 +336,24 @@ function cli_compile_messages($command_args) {
$json_catalog = po2json($lang, $po_file);
$js_file = "$root_dir_path/public_html/translations/$lang.js";
if(!$fd = fopen($js_file, 'w')) {
logging('ERROR', _("Fail to open %s JSON catalog file in write mode (%s)."),
Log :: error(_("Fail to open %s JSON catalog file in write mode (%s)."),
$lang, $js_file);
$error = True;
}
elseif (fwrite($fd, sprintf("translations_data = %s;", $json_catalog)) === false) {
logging('ERROR', _("Fail to write %s JSON catalog in file (%s)."),
Log :: error(_("Fail to write %s JSON catalog in file (%s)."),
$lang, $js_file);
$error = True;
}
else {
logging('INFO', _("%s JSON catalog writed (%s)."), $lang, $js_file);
Log :: info(_("%s JSON catalog writed (%s)."), $lang, $js_file);
}
}
closedir($dh);
return !$error;
}
logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
Log :: fatal(_("Fail to open root lang directory (%s)."), $root_dir_path);
return false;
}
add_cli_command(

View file

@ -1,5 +1,7 @@
<?php
use EesyPHP\Log;
// Gettext text domain
define('TEXT_DOMAIN', 'DEFAULT');
@ -14,7 +16,7 @@ define('TEXT_DOMAIN', 'DEFAULT');
function get_available_langs($as_locales=false) {
global $root_lang_dir;
if (!is_dir($root_lang_dir))
logging('FATAL', "Root land directory not found ($root_lang_dir)");
Log :: fatal("Root land directory not found ($root_lang_dir)");
$langs = array(($as_locales?'en_US.UTF8':'en'));
if ($dh = opendir($root_lang_dir)) {
while (($file = readdir($dh)) !== false) {
@ -31,7 +33,7 @@ function get_available_langs($as_locales=false) {
closedir($dh);
}
$langs = array_unique($langs);
logging('TRACE', 'Available '.($as_locales?'locales':'languages').': '.implode(', ', $langs));
Log :: trace('Available '.($as_locales?'locales':'languages').': '.implode(', ', $langs));
return $langs;
}
@ -80,7 +82,7 @@ function init_translation() {
$root_lang_dir = "$root_dir_path/lang";
if (!class_exists('Locale')) {
logging('ERROR', 'Locale PHP class does not exist. May be php-intl is not installed?');
Log :: error('Locale PHP class does not exist. May be php-intl is not installed?');
return;
}
@ -88,11 +90,11 @@ function init_translation() {
if (php_sapi_name() != "cli") {
if (isset($_REQUEST['lang']) && in_array($_REQUEST['lang'], $available_langs)) {
$lang = $_REQUEST['lang'];
logging('TRACE', "Select lang from request parameter: '$lang'");
Log :: trace("Select lang from request parameter: '$lang'");
}
elseif (isset($_SESSION['lang']) && in_array($_SESSION['lang'], $available_langs) && !isset($_REQUEST['reset_lang'])) {
$lang = $_SESSION['lang'];
logging('TRACE', "Restore lang from session: '$lang'");
Log :: trace("Restore lang from session: '$lang'");
}
else {
$lang = Locale::lookup(
@ -101,7 +103,7 @@ function init_translation() {
true,
Locale::getPrimaryLanguage($default_locale)
);
logging('TRACE', "Best lang found is '$lang'");
Log :: trace("Best lang found is '$lang'");
}
}
else {
@ -112,32 +114,32 @@ function init_translation() {
if ($sys_current)
$lang = Locale::getPrimaryLanguage($sys_current);
if (is_null($lang)) {
logging('TRACE', 'No configured lang detected from CLI env, use default.');
Log :: trace('No configured lang detected from CLI env, use default.');
$lang = Locale::getPrimaryLanguage($default_locale);
}
else
logging('TRACE', "Lang detected from CLI env : '$lang'");
Log :: trace("Lang detected from CLI env : '$lang'");
}
// Keep selected lang in session
$_SESSION['lang'] = $lang;
$locale = lang2locale($lang);
logging('TRACE', "Matching locale found with language '$lang' is '$locale'");
Log :: trace("Matching locale found with language '$lang' is '$locale'");
// Gettext firstly look the LANGUAGE env variable, so set it
if (!putenv("LANGUAGE=$locale"))
logging('ERROR', "Fail to set LANGUAGE variable in environnement to '$locale'");
Log :: error("Fail to set LANGUAGE variable in environnement to '$locale'");
// Set the locale
if (setlocale(LC_ALL, $locale) === false)
logging('ERROR', "An error occured setting locale to '$locale'");
Log :: error("An error occured setting locale to '$locale'");
// Configure and set the text domain
$fullpath = bindtextdomain(TEXT_DOMAIN, $root_lang_dir);
logging('TRACE', "Text domain fullpath is '$fullpath'.");
logging('TRACE', "Text domain is '".textdomain(TEXT_DOMAIN)."'.");
logging('TRACE', "Test: "._('Hello world !'));
Log :: trace("Text domain fullpath is '$fullpath'.");
Log :: trace("Text domain is '".textdomain(TEXT_DOMAIN)."'.");
Log :: trace("Test: "._('Hello world !'));
// JS translation file
$js_translation_file = "translations/$lang.js";

View file

@ -1,14 +1,17 @@
<?php
use EesyPHP\Check;
use EesyPHP\Log;
function get_item_from_url($id, $fatal=false) {
if (!check_id($id))
logging('FATAL', _('Invalid element identifier.'));
if (!Check :: id($id))
Log :: fatal(_('Invalid element identifier.'));
$item = get_item($id);
if(!is_array($item)) {
$error = sprintf(_("Item #% s not found."), $id);
if ($fatal)
logging('FATAL', $error);
Log :: fatal($error);
add_error($error);
return false;
}

View file

@ -1,5 +1,10 @@
<?php
use EesyPHP\Check;
use EesyPHP\Log;
use function EesyPHP\vardump;
if (php_sapi_name() == "cli")
return true;
@ -26,7 +31,7 @@ function handle_search($request) {
if (isset($_REQUEST['clear']) && $_REQUEST['clear']=='true')
redirect($request -> current_url);
}
logging('DEBUG', 'Request params : '.vardump($_REQUEST));
Log :: debug('Request params : '.vardump($_REQUEST));
$status_list['all'] = _('Any');
if (isset($_REQUEST['status'])) {
@ -39,7 +44,7 @@ function handle_search($request) {
if (isset($_REQUEST['pattern'])) {
if (trim($_REQUEST['pattern']) == '')
$_SESSION['search']['pattern'] = false;
else if (check_search_pattern($_REQUEST['pattern']))
else if (Check :: search_pattern($_REQUEST['pattern']))
$_SESSION['search']['pattern'] = $_REQUEST['pattern'];
else
$smarty -> assign('pattern_error', true);
@ -82,7 +87,7 @@ function handle_search($request) {
$_SESSION['search']['nb_by_page']=$nbs_by_page[0];
}
logging('DEBUG', 'Search params : '.vardump($_SESSION['search']));
Log :: debug('Search params : '.vardump($_SESSION['search']));
$result = search_items($_SESSION['search']);
if (!is_array($result))
fatal_error(
@ -146,8 +151,8 @@ function handle_create($request) {
add_error(_("An error occurred while saving this item."));
}
}
logging('DEBUG', 'Validated data : '.vardump($info));
logging('DEBUG', 'Fields errors : '.vardump($field_errors));
Log :: debug('Validated data : '.vardump($info));
Log :: debug('Fields errors : '.vardump($field_errors));
if (isset($_POST['submit']) && !empty($field_errors))
add_error(
_("There are errors preventing this item from being saved. ".
@ -178,7 +183,7 @@ function handle_modify($request) {
if ($value != $item[$key])
$changes[$key] = $value;
}
logging('DEBUG', 'Changes : '.vardump($changes));
Log :: debug('Changes : '.vardump($changes));
if (empty($changes)) {
add_message(_("You have not made any changes to element '% s'."), $item['name']);
redirect('item/'.$item['id']);
@ -191,8 +196,8 @@ function handle_modify($request) {
add_error(_("An error occurred while updating this item."));
}
}
logging('DEBUG', 'Validated data : '.vardump($info));
logging('DEBUG', 'Fields errors : '.vardump($field_errors));
Log :: debug('Validated data : '.vardump($info));
Log :: debug('Fields errors : '.vardump($field_errors));
$smarty->assign('submited', isset($_POST['submit']));
if (isset($_POST['submit']) && !empty($field_errors))
add_error(

View file

@ -1,5 +1,10 @@
<?php
use EesyPHP\Log;
use function EesyPHP\format_callable;
use function EesyPHP\vardump;
/*
* Configured URL patterns :
*
@ -67,8 +72,8 @@ function add_url_handler($pattern, $handler=null, $authenticated=null, $override
);
}
elseif ($override) {
logging(
'DEBUG', "URL : override pattern '%s' with handler '%s' (old handler = '%s')".
Log :: debug(
"URL : override pattern '%s' with handler '%s' (old handler = '%s')".
$pattern, format_callable($handler), vardump($url_patterns[$pattern])
);
$url_patterns[$pattern] = array(
@ -79,7 +84,7 @@ function add_url_handler($pattern, $handler=null, $authenticated=null, $override
);
}
else {
logging('DEBUG', "URL : pattern '$pattern' already defined : do not override.");
Log :: debug("URL : pattern '$pattern' already defined : do not override.");
}
}
}
@ -161,21 +166,19 @@ function get_request($default_url=null) {
global $url_patterns, $_404_url_handler;
$current_url = get_current_url();
if ($current_url === false) {
logging(
'FATAL',
Log :: fatal(
_('Unable to determine the requested page. '.
'If the problem persists, please contact support.')
);
exit();
}
if (!is_array($url_patterns)) {
logging('FATAL', 'URL : No URL patterns configured !');
Log :: fatal('URL : No URL patterns configured !');
exit();
}
logging('DEBUG', "URL : current url = '$current_url'");
logging(
'TRACE',
Log :: debug("URL : current url = '$current_url'");
Log :: trace(
"URL : check current url with the following URL patterns :\n - ".
implode("\n - ", array_keys($url_patterns))
);
@ -186,19 +189,18 @@ function get_request($default_url=null) {
// Reset last redirect
if (isset($_SESSION['last_redirect']))
unset($_SESSION['last_redirect']);
logging('TRACE', "URL : result :\n".vardump($request));
Log :: trace("URL : result :\n".vardump($request));
return $request;
}
}
if ($default_url !== false) {
logging('DEBUG', "Current url match with no pattern. Redirect to default url ('$default_url')");
Log :: debug("Current url match with no pattern. Redirect to default url ('$default_url')");
redirect($default_url);
exit();
}
// Error 404
$api_mode = (strpos($current_url, 'api/') === 0);
logging(
'DEBUG',
Log :: debug(
"Current URL match with no pattern. Use error 404 handler (API mode=$api_mode).");
return new UrlRequest(
$current_url,
@ -227,8 +229,7 @@ function url_match($pattern, $current_url=false, $methods=null) {
if (!$current_url) return False;
}
if (preg_match($pattern, $current_url, $m)) {
logging(
'DEBUG',
Log :: debug(
"URL : Match found with pattern '$pattern' :\n\t".
str_replace("\n", "\n\t", print_r($m, true)));
return $m;
@ -242,17 +243,16 @@ function url_match($pattern, $current_url=false, $methods=null) {
* @return string|false The current request URL or false if fail
**/
function get_current_url() {
logging('TRACE', "URL : request URI = '".$_SERVER['REQUEST_URI']."'");
Log :: trace("URL : request URI = '".$_SERVER['REQUEST_URI']."'");
$base = get_rewrite_base();
logging('TRACE', "URL : rewrite base = '$base'");
Log :: trace("URL : rewrite base = '$base'");
if ($_SERVER['REQUEST_URI'] == $base)
return '';
if (substr($_SERVER['REQUEST_URI'], 0, strlen($base)) != $base) {
logging(
'ERROR',
Log :: error(
"URL : request URI (".$_SERVER['REQUEST_URI'].") does not start with rewrite base ($base)");
return False;
}
@ -323,14 +323,13 @@ function redirect($go=false) {
// Prevent loop
if (isset($_SESSION['last_redirect']) && $_SESSION['last_redirect'] == $url)
logging(
'FATAL',
Log :: fatal(
_('Unable to determine the requested page (loop detected). '.
'If the problem persists, please contact support.'));
else
$_SESSION['last_redirect'] = $url;
logging('DEBUG',"redirect($go) => Redirect to : <$url>");
Log :: debug("redirect($go) => Redirect to : <$url>");
header("Location: $url");
exit();
}
@ -355,10 +354,10 @@ function handle_request($default_url=null) {
$request = get_request($default_url);
if (!is_callable($request -> handler)) {
logging(
'ERROR', "URL handler function %s does not exists !",
Log :: error(
"URL handler function %s does not exists !",
format_callable($request -> handler));
logging('FATAL', _("This request cannot be processed."));
Log :: fatal(_("This request cannot be processed."));
}
if ($request -> api_mode)
@ -371,15 +370,15 @@ function handle_request($default_url=null) {
if (function_exists('force_authentication'))
force_authentication();
else
logging('FATAL', _("Authentication required but force_authentication function is not defined."));
Log :: fatal(_("Authentication required but force_authentication function is not defined."));
try {
call_user_func($request -> handler, $request);
}
catch (Exception $e) {
log_exception(
Log :: exception(
$e, "An exception occured running URL handler function ".$request -> handler."()");
logging('FATAL', _("This request could not be processed correctly."));
Log :: fatal(_("This request could not be processed correctly."));
}
$sentry_span->finish();
}
@ -417,7 +416,7 @@ function check_ajax_request($session_key=null) {
fatal_error('Invalid request');
if ($debug_ajax)
logging('DEBUG',"Ajax Request : ".vardump($_REQUEST));
Log :: debug("Ajax Request : ".vardump($_REQUEST));
}
/**
@ -432,22 +431,20 @@ function get_absolute_url($relative_url=null) {
if (!is_string($relative_url))
$relative_url = get_current_url();
if ($public_root_url[0] == '/') {
logging(
'DEBUG',
Log :: debug(
"URL :: get_absolute_url($relative_url): configured public root URL is relative ".
"($public_root_url) => try to detect it from current request infos.");
$public_root_url = (
'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'?'s':'').'://'.
$_SERVER['HTTP_HOST'].$public_root_url);
logging(
'DEBUG',
Log :: debug(
"URL :: get_absolute_url($relative_url): detected public_root_url: $public_root_url");
}
if (substr($relative_url, 0, 1) == '/')
$relative_url = substr($relative_url, 1);
$url = remove_trailing_slash($public_root_url)."/$relative_url";
logging('DEBUG', "URL :: get_absolute_url($relative_url): result = $url");
Log :: debug("URL :: get_absolute_url($relative_url): result = $url");
return $url;
}
@ -539,7 +536,9 @@ class UrlRequest {
return urldecode($this->url_params[$key]);
}
// Unknown key, log warning
logging('WARNING', "__get($key): invalid property requested\n".get_debug_backtrace_context());
Log :: warning(
"__get($key): invalid property requested\n%s",
Log :: get_debug_backtrace_context());
}
/**

View file

@ -1,6 +1,7 @@
parameters:
level: 5
paths:
- src
- includes
- public_html
- bin

97
src/Check.php Normal file
View file

@ -0,0 +1,97 @@
<?php
namespace EesyPHP;
class Check {
public static function name($name) {
if (preg_match('/^[\w \-]{2,}$/iu',$name))
return true;
return false;
}
public static function id(&$id) {
if (is_int($id))
return true;
if (preg_match('/^[0-9]+$/', $id)) {
$id = intval($id);
return true;
}
return false;
}
public static function search_pattern($pattern) {
foreach(preg_split('/\s+/', trim($pattern)) as $word) {
if (!self :: id($word) && !self :: name($word))
return false;
}
return true;
}
public static function 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
}
public static function description($comment) {
if (preg_match("/^[\p{L}0-9\p{P}\p{Zs}\p{Zl}\p{Sc}\=\+]+$/uim", $comment))
return true;
return false;
}
public static function 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;
}
public static function 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;
}
}
}
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

365
src/Log.php Normal file
View file

@ -0,0 +1,365 @@
<?php
namespace EesyPHP;
use Throwable;
class Log {
/*
* Log file path
* @var string|null
*/
protected static $filepath = null;
/*
* Log file descriptor
* @var resource|null
*/
protected static $file_fd = null;
/*
* Log Levels
* @var array(string,int)
*/
protected static $levels = array(
'TRACE' => 0,
'DEBUG' => 1,
'INFO' => 2,
'WARNING' => 3,
'ERROR' => 4,
'FATAL' => 5,
);
/*
* Default log level
* @var string
*/
protected static string $default_level = 'WARNING';
/*
* Current log level
* @var string|null
*/
protected static $level = null;
/*
* Log PHP errors levels (as specified to set_error_handler())
* Default (set in self::init() method):
* - In TRACE or DEBUG: E_ALL & ~E_STRICT
* - Otherwise: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
*/
protected static $php_errors_levels = null;
// Custom fatal error handler
protected static $fatal_error_handler = null;
/*
* Initialization
* @param string $filepath
* @param string|null $level
* @param int|null $php_errors_levels
* @return void
*/
public static function init($filepath, $level=null, $php_errors_levels=null) {
self :: $filepath = $filepath;
// Set default log level (if not defined or invalid)
if (is_null($level)) {
self :: $level = self :: $default_level;
}
elseif (!array_key_exists($level, self :: $levels)) {
self :: $level = self :: $default_level;
self :: warning(
"Invalid log level value found in configuration (%s). ".
"Set as default (%s).", $level, self :: $default_level);
}
else {
self :: $level = $level;
}
// Log PHP errors
if (!is_null($php_errors_levels))
self :: $php_errors_levels = $php_errors_levels;
elseif (in_array(self :: $level, array('DEBUG', 'TRACE')))
self :: $php_errors_levels = E_ALL & ~E_STRICT;
else
self :: $php_errors_levels = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED;
set_error_handler(array('EesyPHP\\Log', 'on_php_error'), self :: $php_errors_levels);
// Log uncatched exceptions
set_exception_handler(array('EesyPHP\\Log', 'exception'));
}
/**
* Log a message
* @param string $level The message level (key of self :: $levels)
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function log($level, $message, ...$extra_args) {
global $auth_user, $argv;
if (!array_key_exists($level, self :: $levels)) $level = self :: $default_level;
if (self :: $levels[$level] < self :: $levels[self :: $level]) return true;
if(is_null(self :: $file_fd)) {
self :: $file_fd = fopen(self :: $filepath, 'a');
}
// Extra arguments passed, format message using sprintf
if ($extra_args) {
$message = call_user_func_array(
'sprintf',
array_merge(array($message), $extra_args)
);
}
if (php_sapi_name() == "cli") {
$msg = implode(' - ', array(
date('Y/m/d H:i:s'),
basename($argv[0]),
$level,
$message
))."\n";
}
else {
$msg = array(
date('Y/m/d H:i:s'),
$_SERVER['REQUEST_URI'],
$_SERVER['REMOTE_ADDR'],
);
if (isset($auth_user))
$msg[] = ($auth_user['username']?$auth_user['username']:'anonymous');
$msg[] = $level;
$msg[] = $message;
$msg = implode(' - ', $msg)."\n";
}
fwrite(self :: $file_fd, $msg);
if ($level == 'FATAL')
if (!is_null(self :: $fatal_error_handler))
call_user_func(self :: $fatal_error_handler, $message);
elseif (function_exists('fatal_error'))
fatal_error($message);
else
die("\n$message\n\n");
elseif (php_sapi_name() == "cli")
echo $msg;
return true;
}
/**
* Log a trace message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function trace($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('TRACE', $message), $extra_args)
);
}
/**
* Log a debug message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function debug($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('DEBUG', $message), $extra_args)
);
}
/**
* Log an info message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function info($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('INFO', $message), $extra_args)
);
}
/**
* Log an warning message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function warning($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('WARNING', $message), $extra_args)
);
}
/**
* Log an error message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function error($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('ERROR', $message), $extra_args)
);
}
/**
* Log an fatal message
* @param string $message The message to log
* @param array $extra_args Extra arguments to use to compute message using sprintf
* @return true
*/
public static function fatal($message, ...$extra_args) {
return call_user_func_array(
array('EesyPHP\\Log', 'log'),
array_merge(array('FATAL', $message), $extra_args)
);
}
/**
* Register a contextual fatal error handler
* @param null|callable $handler The fatal error handler (set as null to reset)
* @return void
*/
public static function register_fatal_error_handler($handler) {
// @phpstan-ignore-next-line
if ($handler && !is_callable($handler))
self :: fatal('Invalid fatal error handler provided: it is not callable !');
self :: $fatal_error_handler = ($handler?$handler:null);
}
/**
* Change of current log file
* @param string $file The new log file path
* @return bool
*/
public static function change_filepath($file) {
if ($file == self :: $filepath) return True;
if (self :: $file_fd) {
fclose(self :: $file_fd);
self :: $file_fd = null;
}
self :: $filepath = $file;
return True;
}
/*
*******************************************************************************
* Handle exception logging
*******************************************************************************
*/
/**
* Get the current backtrace
* @param int $ignore_last The number of last levels to ignore
* @return string
*/
public static function get_debug_backtrace_context($ignore_last=0) {
$traces = debug_backtrace();
// Also ignore this function it self
$ignore_last++;
if (!is_array($traces) || count($traces) <= $ignore_last)
return "";
$msg = array();
for ($i=$ignore_last; $i < count($traces); $i++) {
$trace = array("#$i");
if (isset($traces[$i]['file']))
$trace[] = $traces[$i]['file'].(isset($traces[$i]['line'])?":".$traces[$i]['line']:"");
// @phpstan-ignore-next-line
if (isset($traces[$i]['class']) && isset($traces[$i]['function']))
$trace[] = implode(" ", array(
$traces[$i]['class'],
$traces[$i]['type'],
$traces[$i]['function']. "()"));
elseif (isset($traces[$i]['function']))
$trace[] = $traces[$i]['function']. "()";
$msg[] = implode(" - ", $trace);
}
return implode("\n", $msg);
}
/**
* Log an exception
* @param Throwable $exception
* @param string|null $prefix The prefix of the log message
* (optional, default: "An exception occured")
* @return void
*/
public static function exception($exception, $prefix=null) {
if (function_exists('log_in_sentry'))
log_in_sentry($exception);
// If more than 2 arguments passed, format prefix message using sprintf
if ($prefix && func_num_args() > 2) {
$prefix = call_user_func_array(
'sprintf',
array_merge(array($prefix), array_slice(func_get_args(), 2))
);
}
self :: error(
"%s:\n%s\n## %s:%d : %s",
($prefix?$prefix:"An exception occured"),
self::get_debug_backtrace_context(1),
$exception->getFile(), $exception->getLine(),
$exception->getMessage());
}
/*
*******************************************************************************
* Handle PHP error logging
*******************************************************************************
*/
/**
* Convert PHP error number to the corresponding label
* @param int $errno
* @return string
*/
public static function errno2type($errno) {
$constants = get_defined_constants();
if (is_array($constants))
foreach($constants as $label => $value)
if ($value == $errno && preg_match('/^E_(.*)$/', $label, $m))
return $m[1];
return 'UNKNOWN ERROR #'.$errno;
}
/**
* Log a PHP error
* Note: method design to be used as callable by set_error_handler()
* @param int $errno The error number
* @param string $errstr The error message
* @param string $errfile The filename that the error was raised in
* @param int $errline The line number where the error was raised
* @return false Return false to let the normal error handler continues.
*/
public static function on_php_error($errno, $errstr, $errfile, $errline) {
$msg = sprintf(
"A PHP error occured : [%s] %s\nFile : %s (line : %d)",
self :: errno2type($errno), $errstr, $errfile, $errline
);
self :: error($msg);
if (function_exists('log_php_error_in_sentry'))
log_php_error_in_sentry($errno, $msg);
return False;
}
}
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

216
src/functions.php Normal file
View file

@ -0,0 +1,216 @@
<?php
namespace EesyPHP;
use EesyPHP\Log;
/*
* 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";
}
/*
* 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)) {
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)
*
* @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);
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);
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);
}
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab