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
This commit is contained in:
parent
01759fb4c2
commit
6fdc5447f1
24 changed files with 2363 additions and 510 deletions
9
.pre-commit-config.yaml
Normal file
9
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Pre-commit hooks to run tests and ensure code is cleaned.
|
||||
# See https://pre-commit.com for more information
|
||||
repos:
|
||||
- repo: https://github.com/digitalpulp/pre-commit-php.git
|
||||
rev: 1.4.0
|
||||
hooks:
|
||||
- id: php-stan
|
||||
files: \.(php)$
|
||||
args: ['--configuration=phpstan.neon']
|
|
@ -1,13 +1,23 @@
|
|||
{
|
||||
"name": "ee/eesyphp",
|
||||
"description": "Easter-eggs easyphp PHP framework for simple web apps",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Easter-eggs",
|
||||
"email": "info@easter-eggs.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"envms/fluentpdo": "^1.1",
|
||||
"pear/console_table": "^1.3",
|
||||
"brenard/php-unidecode": "dev-master",
|
||||
"smarty/smarty": "3.1.34",
|
||||
"smarty-gettext/smarty-gettext": "^1.6",
|
||||
"smarty-gettext/tsmarty2c": "^0.2.1",
|
||||
"sepia/po-parser": "^6.0"
|
||||
"sepia/po-parser": "^6.0",
|
||||
"sentry/sdk": "^3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"overtrue/phplint": "^3.0"
|
||||
"phpstan/phpstan": "^1.9"
|
||||
}
|
||||
}
|
||||
|
|
1833
composer.lock
generated
1833
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -49,3 +49,11 @@ Some other variables exist to manage the email sending by the application :
|
|||
- `$mail_headers` : an PHP array of default headers add to mail sent by the application
|
||||
- `$mail_sender` : the email address of the sender of all emails sent by the application
|
||||
- `$mail_catch_all` : for debugging purpose, you can using this variable to specify an email address that will received all email sent by the application in place of the original recipient
|
||||
|
||||
## Web stats JS code inclusion
|
||||
|
||||
If you use a Web stats tool that need to include a piece of JS code in all page like [Matomo](https://matomo.org/), you could defined the `$webstats_js_code` variable.
|
||||
|
||||
## Sentry integration
|
||||
|
||||
If you want to enable the [Sentry](https://sentry.io) integration, you have to define the `$sentry_dsn`. This integration permit to report PHP errors (see `$sentry_php_error_types`) and exception as issues and to monitor performance of the application (see `$sentry_traces_sample_rate`).
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
Some Debian packages have to be installed :
|
||||
|
||||
```bash
|
||||
apt install git composer php-cli php-mail php-mail-mine php-net-smtp php-auth-sasl php-json php-mbstring php-intl smarty3
|
||||
apt install git composer php-cli php-mail php-mail-mine php-net-smtp php-auth-sasl php-json php-mbstring php-intl
|
||||
# for PostgreSQL DB backend
|
||||
apt install php-pgsql
|
||||
# for MySQL/MariaDB DB backend
|
||||
|
|
|
@ -5,7 +5,7 @@ function add_cli_command($command, $handler, $short_desc, $usage_args=false, $lo
|
|||
$override=false) {
|
||||
global $cli_commands;
|
||||
if (array_key_exists($command, $cli_commands) && !$override) {
|
||||
logging('ERROR', _("The CLI command '%s' already exists.", $command));
|
||||
logging('ERROR', _("The CLI command '%s' already exists."), $command);
|
||||
return False;
|
||||
}
|
||||
|
||||
|
@ -331,10 +331,10 @@ function cli_restore($command_args) {
|
|||
$fd = fopen((count($command_args) >= 1?$command_args[0]:'php://stdin'), 'r');
|
||||
restore_items($fd);
|
||||
fclose($fd);
|
||||
logging('INFO', sprint(
|
||||
"Items restored from '%s'",
|
||||
logging(
|
||||
'INFO', "Items restored from '%s'",
|
||||
(count($command_args) >= 1?$command_args[0]:'STDIN')
|
||||
));
|
||||
);
|
||||
}
|
||||
add_cli_command(
|
||||
'restore',
|
||||
|
@ -389,7 +389,6 @@ function cli_cron($command_args) {
|
|||
logging('INFO', 'Item #%s (%s) deleted (creation date: %s)',
|
||||
$item['id'], $item['name'], format_time($item['date'])
|
||||
);
|
||||
remove_item_attachments($item['id']);
|
||||
}
|
||||
else {
|
||||
logging('ERROR', 'Fail to delete item "%s" (%s, creation date: %s)',
|
||||
|
@ -416,3 +415,5 @@ add_cli_command(
|
|||
___("-m/--max-age Item expiration limit (in days, optional)"),
|
||||
)
|
||||
);
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -35,8 +35,39 @@ $log_level = 'INFO';
|
|||
// Debug Ajax request/response
|
||||
$debug_ajax = false;
|
||||
|
||||
// Smarty class path
|
||||
$smarty_path = 'smarty3/Smarty.class.php';
|
||||
// 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;
|
||||
|
||||
/*
|
||||
* Sentry configuration
|
||||
*/
|
||||
|
||||
// Sentry DSN
|
||||
$sentry_dsn = null;
|
||||
|
||||
// Log PHP errors in Sentry: list of errors types to logs
|
||||
// Note: must also match with $log_php_errors_levels.
|
||||
// See: https://www.php.net/manual/fr/errorfunc.constants.php
|
||||
$sentry_php_error_types = array(
|
||||
E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
|
||||
E_RECOVERABLE_ERROR,E_DEPRECATED,
|
||||
);
|
||||
|
||||
// Traces sample rate (between 0 and 1)
|
||||
// Note: this parameter permit to determine how many transactions (=~ access) are traced and
|
||||
// sent to Sentry, for instance, 0.2 meen that 20% of the transactions will be traced. In dev
|
||||
// mode, set to 1 if you want all transactions are sent to Sentry.
|
||||
// Default: 0.2
|
||||
$sentry_traces_sample_rate = 0.2;
|
||||
|
||||
/*
|
||||
* Smarty template configuration
|
||||
*/
|
||||
|
||||
// Smarty directories
|
||||
$smarty_templates_dir = "$root_dir_path/templates";
|
||||
$smarty_templates_c_dir = "$tmp_root_dir/templates_c";
|
||||
|
||||
|
@ -116,7 +147,9 @@ $mail_sender = "noreply@example.org";
|
|||
// Catch all e-mails sent to a configured e-mail address
|
||||
$mail_catch_all = false;
|
||||
|
||||
// Load local configuration file is present
|
||||
if (is_file("$root_dir_path/includes/config.local.php")) {
|
||||
require "$root_dir_path/includes/config.local.php";
|
||||
}
|
||||
/**
|
||||
* Web Stats JS code
|
||||
*/
|
||||
$webstats_js_code = '';
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
|
||||
// Root directory path
|
||||
if (__FILE__ != "") {
|
||||
$script = null;
|
||||
if (defined('__FILE__') && constant('__FILE__')) {
|
||||
$script = __FILE__;
|
||||
}
|
||||
else {
|
||||
|
@ -12,7 +13,8 @@ else {
|
|||
if (basename($script) == 'core.php')
|
||||
break;
|
||||
}
|
||||
$root_dir_path=realpath(dirname($script).'/../');
|
||||
if (!$script) die('Fail to detect root directory path');
|
||||
$root_dir_path = realpath(dirname($script).'/../');
|
||||
|
||||
// Include App's includes and vendor directories to PHP include paths
|
||||
set_include_path($root_dir_path.'/includes' . PATH_SEPARATOR . get_include_path());
|
||||
|
@ -27,13 +29,19 @@ $api_mode = false;
|
|||
require_once('translation.php');
|
||||
require_once('config.inc.php');
|
||||
|
||||
// Check $public_root_url end
|
||||
if (substr($public_root_url, -1)=='/') {
|
||||
$public_root_url=substr($public_root_url, 0, -1);
|
||||
// Load local configuration file is present
|
||||
if (is_file("$root_dir_path/includes/config.local.php")) {
|
||||
require "$root_dir_path/includes/config.local.php";
|
||||
}
|
||||
|
||||
require_once 'sentry.php';
|
||||
$sentry_transaction = new SentryTransaction();
|
||||
|
||||
$sentry_span = new SentrySpan('core.init', 'Core initialization');
|
||||
|
||||
// Define upload_tmp_dir
|
||||
ini_set('upload_tmp_dir',$upload_tmp_dir);
|
||||
if (isset($upload_tmp_dir))
|
||||
ini_set('upload_tmp_dir', $upload_tmp_dir);
|
||||
|
||||
require_once('logging.php');
|
||||
require_once('functions.php');
|
||||
|
@ -60,3 +68,6 @@ require_once('mail.php');
|
|||
init_translation();
|
||||
foreach($status_list as $key => $value)
|
||||
$status_list[$key] = _($value);
|
||||
$sentry_span->finish();
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
129
includes/db.php
129
includes/db.php
|
@ -2,8 +2,18 @@
|
|||
|
||||
use Unidecode\Unidecode;
|
||||
|
||||
if (!isset($db_dsn)) {
|
||||
logging('FATAL', 'Database DSN not configured');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = new PDO($db_dsn, $db_user, $db_pwd, $db_options);
|
||||
$pdo = new PDO(
|
||||
$db_dsn,
|
||||
isset($db_user)?$db_user:null,
|
||||
isset($db_pwd)?$db_pwd:null,
|
||||
isset($db_options)?$db_options:null
|
||||
);
|
||||
$pdo -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$fpdo = new FluentPDO($pdo);
|
||||
|
||||
|
@ -266,18 +276,18 @@ function search_items($params) {
|
|||
$orderby="$order $order_direction";
|
||||
|
||||
$limit = "";
|
||||
$page = 1;
|
||||
$nb_by_page = 10;
|
||||
$offset = 0;
|
||||
if (!isset($params['all'])) {
|
||||
$page=1;
|
||||
$nb_by_page=10;
|
||||
if (isset($params['page']) && $params['page']>0) {
|
||||
if (isset($params['nb_by_page']) && $params['nb_by_page']>0) {
|
||||
$nb_by_page=intval($params['nb_by_page']);
|
||||
$nb_by_page = intval($params['nb_by_page']);
|
||||
}
|
||||
$page=intval($params['page']);
|
||||
$page = intval($params['page']);
|
||||
}
|
||||
$offset=($page-1)*$nb_by_page;
|
||||
$limit=$nb_by_page;
|
||||
$offset = ($page-1)*$nb_by_page;
|
||||
$limit = $nb_by_page;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -297,60 +307,57 @@ function search_items($params) {
|
|||
-> offset($offset)
|
||||
-> execute();
|
||||
|
||||
if ($result !== false) {
|
||||
$rows = $result -> fetchAll();
|
||||
$items = array();
|
||||
foreach ($rows as $row) {
|
||||
$items[] = _format_row_info($row, array('date'));
|
||||
}
|
||||
if (is_array($items)) {
|
||||
if (empty($limit)) {
|
||||
return array(
|
||||
'count' => count($items),
|
||||
'first' => 1,
|
||||
'last' => count($items),
|
||||
'nb_pages' => 1,
|
||||
'page' => 1,
|
||||
'items' => $items
|
||||
);
|
||||
}
|
||||
else {
|
||||
$query_count = $fpdo -> from('item')
|
||||
-> select(null)
|
||||
-> select('count(*) as count');
|
||||
if (!empty($where))
|
||||
$query_count -> where($where);
|
||||
foreach ($patterns_where as $patterns_word)
|
||||
call_user_func_array(
|
||||
array($query_count, 'where'),
|
||||
array_merge(
|
||||
array('('.implode(' OR ', array_keys($patterns_word)).')'),
|
||||
array_values($patterns_word)
|
||||
)
|
||||
);
|
||||
|
||||
$result_count = $query_count -> execute();
|
||||
|
||||
if ($result_count === false) {
|
||||
logging('DEBUG', 'search_items() : search for count in DB return false');
|
||||
return False;
|
||||
}
|
||||
$count = $result_count -> fetch();
|
||||
return array(
|
||||
'count' => $count['count'],
|
||||
'first' => $offset+1,
|
||||
'last' => (
|
||||
$offset+$nb_by_page<$count['count']?
|
||||
$offset+$nb_by_page:$count['count']),
|
||||
'nb_pages' => ceil($count['count']/$nb_by_page),
|
||||
'page' => $page,
|
||||
'items' => $items,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if ($result === false) {
|
||||
logging('ERROR', 'search_items() : search in DB return false');
|
||||
return false;
|
||||
}
|
||||
|
||||
$rows = $result -> fetchAll();
|
||||
$items = array();
|
||||
foreach ($rows as $row) {
|
||||
$items[] = _format_row_info($row, array('date'));
|
||||
}
|
||||
if (isset($params['all'])) {
|
||||
return array(
|
||||
'count' => count($items),
|
||||
'first' => 1,
|
||||
'last' => count($items),
|
||||
'nb_pages' => 1,
|
||||
'page' => 1,
|
||||
'items' => $items
|
||||
);
|
||||
}
|
||||
$query_count = $fpdo -> from('item')
|
||||
-> select(null)
|
||||
-> select('count(*) as count');
|
||||
if (!empty($where))
|
||||
$query_count -> where($where);
|
||||
foreach ($patterns_where as $patterns_word)
|
||||
call_user_func_array(
|
||||
array($query_count, 'where'),
|
||||
array_merge(
|
||||
array('('.implode(' OR ', array_keys($patterns_word)).')'),
|
||||
array_values($patterns_word)
|
||||
)
|
||||
);
|
||||
|
||||
$result_count = $query_count -> execute();
|
||||
|
||||
if ($result_count === false) {
|
||||
logging('DEBUG', 'search_items() : search for count in DB return false');
|
||||
return False;
|
||||
}
|
||||
$count = $result_count -> fetch();
|
||||
return array(
|
||||
'count' => $count['count'],
|
||||
'first' => $offset+1,
|
||||
'last' => (
|
||||
$offset+$nb_by_page<$count['count']?
|
||||
$offset+$nb_by_page:$count['count']),
|
||||
'nb_pages' => ceil($count['count']/$nb_by_page),
|
||||
'page' => $page,
|
||||
'items' => $items,
|
||||
);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
log_exception(
|
||||
|
@ -461,3 +468,5 @@ function restore_items($fd=null) {
|
|||
|
||||
return !$error;
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -232,6 +232,25 @@ function vardump($data) {
|
|||
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":
|
||||
|
@ -322,7 +341,7 @@ function delete_directory($dir, $recursive=true) {
|
|||
* error
|
||||
**/
|
||||
function run_external_command($command, $data_stdin=null, $escape_command_args=true) {
|
||||
if (array($command))
|
||||
if (is_array($command))
|
||||
$command = implode(' ', $command);
|
||||
if ($escape_command_args)
|
||||
$command = escapeshellcmd($command);
|
||||
|
@ -365,3 +384,5 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t
|
|||
|
||||
return array($return_value, $stdout, $stderr);
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -25,7 +25,7 @@ function register_hook($event, $callable, $param=NULL) {
|
|||
/**
|
||||
* Run triggered actions on specific event
|
||||
*
|
||||
* @param $event string Event name
|
||||
* @param $event string HookEvent name
|
||||
*
|
||||
* @return boolean True if all triggered actions succefully runned, false otherwise
|
||||
*/
|
||||
|
@ -35,16 +35,16 @@ function trigger_hook($event_name, $event_data=null) {
|
|||
|
||||
if (isset($hooks[$event_name]) && is_array($hooks[$event_name])) {
|
||||
if ($event_name == 'all')
|
||||
$event = new Event($event_data['event_name'], $event_data['event_data']);
|
||||
$event = new HookEvent($event_data['event_name'], $event_data['event_data']);
|
||||
else
|
||||
$event = new Event($event_name, $event_data);
|
||||
$event = new HookEvent($event_name, $event_data);
|
||||
foreach ($hooks[$event_name] as $e) {
|
||||
if (is_callable($e['callable'])) {
|
||||
try {
|
||||
call_user_func_array($e['callable'],array($event, &$e['param']));
|
||||
}
|
||||
catch(Exception $e) {
|
||||
logException(
|
||||
log_exception(
|
||||
$e, "An exception occured running hook ".format_callable($e['callable']).
|
||||
" on event $event_name");
|
||||
$return = false;
|
||||
|
@ -75,7 +75,7 @@ function trigger_hook($event_name, $event_data=null) {
|
|||
return $return;
|
||||
}
|
||||
|
||||
class Event implements JsonSerializable {
|
||||
class HookEvent implements JsonSerializable {
|
||||
private $name;
|
||||
private $data;
|
||||
|
||||
|
@ -101,3 +101,5 @@ class Event implements JsonSerializable {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -7,14 +7,23 @@
|
|||
*
|
||||
* // 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_file_fd = null;
|
||||
|
||||
// Log Levels
|
||||
$_log_levels=array(
|
||||
$_log_levels = array(
|
||||
'TRACE' => 0,
|
||||
'DEBUG' => 1,
|
||||
'INFO' => 2,
|
||||
|
@ -23,13 +32,22 @@ $_log_levels=array(
|
|||
'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, $auth_user;
|
||||
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];
|
||||
|
||||
if (!array_key_exists($log_level, $_log_levels)) $log_level = 'INFO';
|
||||
$log_level_id = $_log_levels[$log_level];
|
||||
|
||||
if ($level_id < $log_level_id) return true;
|
||||
|
@ -66,10 +84,12 @@ function logging($level, $message) {
|
|||
$msg = implode(' - ', $msg)."\n";
|
||||
}
|
||||
|
||||
fwrite($_log_file_fd , $msg);
|
||||
fwrite($_log_file_fd, $msg);
|
||||
|
||||
if ($level == 'FATAL')
|
||||
if (function_exists('fatal_error'))
|
||||
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");
|
||||
|
@ -79,6 +99,38 @@ function logging($level, $message) {
|
|||
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;
|
||||
|
@ -90,7 +142,17 @@ function change_log_file($file) {
|
|||
return True;
|
||||
}
|
||||
|
||||
// Handle exception logging
|
||||
/*
|
||||
*******************************************************************************
|
||||
* 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();
|
||||
|
||||
|
@ -105,6 +167,7 @@ function get_debug_backtrace_context($ignore_last=0) {
|
|||
$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'],
|
||||
|
@ -118,7 +181,16 @@ function get_debug_backtrace_context($ignore_last=0) {
|
|||
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(
|
||||
|
@ -135,13 +207,50 @@ function log_exception($exception, $prefix=null) {
|
|||
}
|
||||
set_exception_handler('log_exception');
|
||||
|
||||
// Handle PHP error logging
|
||||
function log_php_eror($errno, $errstr, $errfile, $errline) {
|
||||
logging("ERROR", "A PHP error occured : [%d] %s\nFile : %s (line : %d)",
|
||||
$errno, $errstr, $errfile, $errline);
|
||||
/*
|
||||
*******************************************************************************
|
||||
* 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 ($log_level == 'DEBUG')
|
||||
set_error_handler('log_php_eror', E_ALL & ~E_STRICT);
|
||||
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_eror', E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
set_error_handler('log_php_error', E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -1,24 +1,72 @@
|
|||
<?php
|
||||
|
||||
// Load PHP PEAR Mail and Mail_mime libs
|
||||
require_once($php_mail_path);
|
||||
require_once($php_mail_mime_path);
|
||||
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");
|
||||
|
||||
function send_mail($from, $to, $subject, $msg, $headers=array(), $attachments=array(),
|
||||
$crlf="\r\n") {
|
||||
global $mail_send_method, $mail_headers, $mail_send_params, $mail_catch_all, $mail_sender;
|
||||
$mail_obj = Mail::factory($mail_send_method, $mail_send_params);
|
||||
/**
|
||||
* Send an email
|
||||
*
|
||||
* @param string|null $from Email sender
|
||||
* @param string|array<string> $to Email recipient(s)
|
||||
* @param string $subject Email subject
|
||||
* @param string $msg Email body
|
||||
* @param boolean $html Set to true to send an HTML email (default: false)
|
||||
* @param array<string,string>|null $attachments Email attachments as an array with
|
||||
* filepath as key and filename as value
|
||||
* @param array<string,string>|null $headers Email headers
|
||||
* @param string|null $encoding Email encoding (default: utf8)
|
||||
* @param string|null $eol End of line string (default : \n)
|
||||
*
|
||||
* @return boolean true If mail was sent, false otherwise
|
||||
*/
|
||||
function send_mail($from, $to, $subject, $msg, $html=false, $attachments=null, $headers=null,
|
||||
$encoding=null, $eol=null) {
|
||||
global $mail_send_method, $mail_headers, $mail_send_params, $mail_sender, $mail_catch_all;
|
||||
$mail_obj = & Mail::factory($mail_send_method, $mail_send_params);
|
||||
|
||||
if ($mail_catch_all) {
|
||||
$msg .= sprintf(
|
||||
_("\n\n\nMail initialy intended for %s."),
|
||||
(is_array($to)?implode(',', $to):$to));
|
||||
$to = $mail_catch_all;
|
||||
if (!$headers) $headers = array();
|
||||
if(isset($mail_headers) && is_array($mail_headers)) {
|
||||
$headers = array_merge($headers, $mail_headers);
|
||||
}
|
||||
|
||||
if(is_array($mail_headers)) {
|
||||
$headers = array_merge($headers,$mail_headers);
|
||||
logging(
|
||||
'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',
|
||||
is_array($mail_catch_all)?implode(',', $mail_catch_all):$mail_catch_all
|
||||
);
|
||||
$msg .= sprintf(
|
||||
(
|
||||
$html?
|
||||
_("</hr><p><small>Mail initialy intended for %s.</small></p>"):
|
||||
_("\n\n\nMail initialy intended for %s.")
|
||||
),
|
||||
(is_array($to)?implode(',', $to):$to));
|
||||
$headers["X-Orig-To"] = $to;
|
||||
$to = (
|
||||
is_array($mail_catch_all)?
|
||||
implode(',', $mail_catch_all):$mail_catch_all
|
||||
);
|
||||
}
|
||||
|
||||
if ($subject) {
|
||||
$headers["Subject"] = $subject;
|
||||
}
|
||||
|
||||
if (isset($headers['From'])) {
|
||||
if (!$from)
|
||||
$from = $headers['From'];
|
||||
unset($headers['From']);
|
||||
}
|
||||
elseif (!$from) {
|
||||
$from = $mail_sender;
|
||||
}
|
||||
|
||||
$headers["To"] = $to;
|
||||
|
||||
$to = array (
|
||||
|
@ -26,33 +74,43 @@ function send_mail($from, $to, $subject, $msg, $headers=array(), $attachments=ar
|
|||
);
|
||||
|
||||
foreach(array_keys($headers) as $header) {
|
||||
if(strtoupper($header) == 'BCC') {
|
||||
$to['BCC'] = $headers[$header];
|
||||
}
|
||||
elseif(strtoupper($header) == 'CC') {
|
||||
$to['CC'] = $headers[$header];
|
||||
if(in_array(strtoupper($header), array('BCC', 'CC'))) {
|
||||
if (isset($mail_catch_all) && $mail_catch_all) {
|
||||
logging('DEBUG', "Mail catched: remove $header header");
|
||||
$msg .= sprintf(
|
||||
(
|
||||
$html?
|
||||
_("<p><small>%s: %s</small></p>"):
|
||||
_("\n%s: %s")
|
||||
),
|
||||
strtoupper($header),
|
||||
(is_array($headers[$header])?implode(',', $headers[$header]):$headers[$header]));
|
||||
unset($headers[$header]);
|
||||
continue;
|
||||
}
|
||||
$to[strtoupper($header)] = $headers[$header];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$encoding) $encoding = "utf8";
|
||||
$mime = new Mail_mime(
|
||||
array(
|
||||
'eol' => $crlf,
|
||||
'text_charset' => 'utf8',
|
||||
'head_charset' => 'utf8',
|
||||
'eol' => ($eol?$eol:"\n"),
|
||||
($html?'html_charset':'text_charset') => $encoding,
|
||||
'head_charset' => $encoding,
|
||||
)
|
||||
);
|
||||
if ($from) {
|
||||
$mime->setFrom($from);
|
||||
}
|
||||
elseif ($mail_sender) {
|
||||
$mime->setFrom($mail_sender);
|
||||
}
|
||||
|
||||
if ($subject) {
|
||||
if ($from)
|
||||
$mime->setFrom($from);
|
||||
|
||||
if ($subject)
|
||||
$mime->setSubject($subject);
|
||||
}
|
||||
$mime->setTXTBody($msg);
|
||||
|
||||
if ($html)
|
||||
$mime->setHTMLBody($msg);
|
||||
else
|
||||
$mime->setTXTBody($msg);
|
||||
|
||||
if (is_array($attachments) && !empty($attachments)) {
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
|
@ -64,11 +122,14 @@ function send_mail($from, $to, $subject, $msg, $headers=array(), $attachments=ar
|
|||
$body = $mime->get();
|
||||
$headers = $mime->headers($headers);
|
||||
|
||||
$ret = $mail_obj -> send($to,$headers,$body);
|
||||
$ret = $mail_obj -> send($to, $headers, $body);
|
||||
|
||||
if ($ret instanceof PEAR_Error) {
|
||||
logging('ERROR',"Error sending email to $to : ".$ret -> getMessage());
|
||||
return False;
|
||||
if (PEAR::isError($ret)) {
|
||||
$msg = "Error sending email: ".$ret -> getMessage();
|
||||
logging('ERROR', $msg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
213
includes/sentry.php
Normal file
213
includes/sentry.php
Normal file
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Configuration :
|
||||
*
|
||||
* // Log PHP errors in Sentry: list of errors types to logs
|
||||
* // Note: must also match with $log_php_errors_levels.
|
||||
* // See: https://www.php.net/manual/fr/errorfunc.constants.php
|
||||
* $sentry_php_error_types = array(
|
||||
* E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR,
|
||||
* E_RECOVERABLE_ERROR,E_DEPRECATED,
|
||||
* );
|
||||
*/
|
||||
|
||||
// Init Sentry (if its DSN is configured)
|
||||
if (isset($sentry_dsn) && $sentry_dsn) {
|
||||
\Sentry\init([
|
||||
'dsn' => $sentry_dsn,
|
||||
'traces_sample_rate' => (
|
||||
isset($sentry_traces_sample_rate) ?
|
||||
$sentry_traces_sample_rate : 0.2
|
||||
),
|
||||
]);
|
||||
|
||||
\Sentry\configureScope(function (\Sentry\State\Scope $scope): void {
|
||||
global $auth_user;
|
||||
$scope->setUser([
|
||||
'id' => isset($auth_user) && $auth_user?$auth_user['uid']:null,
|
||||
'email' => isset($auth_user) && $auth_user?$auth_user['mail']:null,
|
||||
'segment' => isset($auth_user) && $auth_user?$auth_user['type']:null,
|
||||
'ip_address' => $_SERVER['REMOTE_ADDR'],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an exception or a message in Sentry
|
||||
* @param string|Exception $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');
|
||||
return;
|
||||
}
|
||||
if (is_string($msg)) {
|
||||
logging('DEBUG', 'Error logged in Sentry');
|
||||
\Sentry\captureMessage($msg);
|
||||
}
|
||||
elseif ($msg instanceof Exception) {
|
||||
logging('DEBUG', 'Exception logged in Sentry');
|
||||
\Sentry\captureException($msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a PHP error in Sentry
|
||||
* @param int $errno The error number
|
||||
* @param string $msg The error message
|
||||
* @return void
|
||||
*/
|
||||
function log_php_error_in_sentry($errno, $msg) {
|
||||
global $sentry_php_error_types;
|
||||
if (
|
||||
isset($sentry_php_error_types)
|
||||
&& is_array($sentry_php_error_types)
|
||||
&& in_array($errno, $sentry_php_error_types)
|
||||
)
|
||||
log_in_sentry($msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performance monitoring
|
||||
*/
|
||||
|
||||
class SentryTransaction {
|
||||
|
||||
/**
|
||||
* The Sentry transaction object
|
||||
* @var \Sentry\Tracing\Transaction
|
||||
*/
|
||||
private $transaction;
|
||||
|
||||
/**
|
||||
* The Sentry transaction context object
|
||||
* @var \Sentry\Tracing\TransactionContext
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* Constructor: start a Sentry transaction
|
||||
* @param string|null $op The operation name
|
||||
* @param string|null $name The transaction name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($op=null, $name=null) {
|
||||
// Setup context for the full transaction
|
||||
$this->context = new \Sentry\Tracing\TransactionContext();
|
||||
$this->context->setName(
|
||||
$name?$name:
|
||||
(php_sapi_name()=='cli'?'CLI execution':'HTTP request')
|
||||
);
|
||||
$this->context->setOp(
|
||||
$op?$op:
|
||||
(php_sapi_name()=='cli'?'cli.command':'http.request')
|
||||
);
|
||||
|
||||
// Start the transaction
|
||||
$this->transaction = \Sentry\startTransaction($this->context);
|
||||
|
||||
// Set the current transaction as the current span so we can retrieve it later
|
||||
\Sentry\SentrySdk::getCurrentHub()->setSpan($this->transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor: Stop the current Sentry transaction
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct() {
|
||||
SentrySpan :: finishAll();
|
||||
$this->transaction->finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal Sentry Span object implementation
|
||||
* This internal implementation principally permit to keep trace of new span parent
|
||||
* and list of started spans.
|
||||
*/
|
||||
class SentrySpan {
|
||||
|
||||
/**
|
||||
* Keep trace of started Sentry spans
|
||||
* @var array<int,mixed>
|
||||
*/
|
||||
private static $_started_spans = array();
|
||||
|
||||
/**
|
||||
* The unique ID of the Sentry span
|
||||
* Note: internal ID used as key in self::$_started_spans
|
||||
* @var int|null
|
||||
*/
|
||||
private $id = null;
|
||||
|
||||
/**
|
||||
* The parent of the Sentry span
|
||||
* @var mixed
|
||||
*/
|
||||
private $parent = null;
|
||||
|
||||
/**
|
||||
* The context of the Sentry span
|
||||
* @var null|\Sentry\Tracing\SpanContext
|
||||
*/
|
||||
private $context = null;
|
||||
|
||||
/**
|
||||
* The Sentry span object
|
||||
* @var mixed
|
||||
*/
|
||||
private $span = null;
|
||||
|
||||
/**
|
||||
* Sentry span constructor
|
||||
* @param string|null $op The operation name
|
||||
* @param string|null $name The span name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($op, $name) {
|
||||
$this -> parent = \Sentry\SentrySdk::getCurrentHub()->getSpan();
|
||||
// Check if we have a parent span (this is the case if we started a transaction earlier)
|
||||
if (is_null($this -> parent)) return;
|
||||
|
||||
while (is_null($this -> id)) {
|
||||
$this -> id = rand();
|
||||
if (isset(self :: $_started_spans[$this -> id]))
|
||||
$this -> id = null;
|
||||
}
|
||||
$this -> context = new \Sentry\Tracing\SpanContext();
|
||||
$this -> context->setOp($op);
|
||||
$this -> context->setDescription($name);
|
||||
$this -> span = $this->parent->startChild($this -> context);
|
||||
|
||||
// Set the current span to the span we just started
|
||||
\Sentry\SentrySdk::getCurrentHub()->setSpan($this -> span);
|
||||
|
||||
self :: $_started_spans[$this -> id] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the span (if started)
|
||||
* @return void
|
||||
*/
|
||||
public function finish() {
|
||||
if (!$this -> span) return;
|
||||
$this -> span -> finish();
|
||||
unset(self::$_started_spans[$this -> id]);
|
||||
\Sentry\SentrySdk::getCurrentHub()->setSpan($this -> parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish all started spans
|
||||
* @see SentryTransaction::__destruct()
|
||||
* @return void
|
||||
*/
|
||||
public static function finishAll() {
|
||||
foreach (array_reverse(self :: $_started_spans) as $id => $span)
|
||||
$span -> finish();
|
||||
}
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
|
@ -17,7 +17,7 @@ if (!isset($_SESSION['session_key'])) {
|
|||
}
|
||||
|
||||
// Handle session timeout
|
||||
if ($session_timeout) {
|
||||
if (isset($session_timeout) && $session_timeout) {
|
||||
if (!isset($_SESSION['session_last_access'])) {
|
||||
logging('DEBUG', 'Set initial session last access');
|
||||
$_SESSION['session_last_access'] = time();
|
||||
|
@ -40,3 +40,5 @@ function check_session_key($value=null) {
|
|||
$value = $_REQUEST['session_key'];
|
||||
return ($value && $_SESSION['session_key'] == $value);
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
if (php_sapi_name() == "cli")
|
||||
return true;
|
||||
|
||||
require_once($smarty_path);
|
||||
$smarty = new Smarty();
|
||||
|
||||
/*
|
||||
|
@ -62,8 +61,26 @@ else {
|
|||
}
|
||||
|
||||
// Configure templates/templates_c directories
|
||||
$smarty->setTemplateDir($smarty_templates_dir);
|
||||
$smarty->setCompileDir($smarty_templates_c_dir);
|
||||
if (
|
||||
!isset($smarty_templates_dir)
|
||||
|| !is_dir($smarty_templates_dir)
|
||||
)
|
||||
logging(
|
||||
'FATAL', "Template directory not found (%s)",
|
||||
isset($smarty_templates_dir)?$smarty_templates_dir:'not set');
|
||||
else
|
||||
$smarty->setTemplateDir($smarty_templates_dir);
|
||||
|
||||
if (
|
||||
!isset($smarty_templates_c_dir)
|
||||
|| !is_dir($smarty_templates_c_dir)
|
||||
|| !is_writable($smarty_templates_c_dir)
|
||||
)
|
||||
logging(
|
||||
'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);
|
||||
|
||||
// Enable Smarty security
|
||||
smarty_enable_security_mode(
|
||||
|
@ -77,13 +94,13 @@ smarty_enable_security_mode(
|
|||
);
|
||||
|
||||
// Defined some global template variables
|
||||
$smarty->assign('public_root_url', $public_root_url);
|
||||
$smarty->assign('main_pagetitle', $main_pagetitle);
|
||||
$smarty->assign('public_root_url', isset($public_root_url)?$public_root_url:'/');
|
||||
$smarty->assign('main_pagetitle', isset($main_pagetitle)?$main_pagetitle:null);
|
||||
$smarty->assign('session_key', $_SESSION['session_key']);
|
||||
|
||||
// Handle in-page errors & messages
|
||||
if (!isset($_SESSION['errors']))
|
||||
$_SESSION['errors']=array();
|
||||
$_SESSION['errors'] = array();
|
||||
function add_error($error) {
|
||||
// If more than one arguments passed, format error message using sprintf
|
||||
if (func_num_args() > 1) {
|
||||
|
@ -96,7 +113,7 @@ function add_error($error) {
|
|||
}
|
||||
|
||||
if (!isset($_SESSION['messages']))
|
||||
$_SESSION['messages']=array();
|
||||
$_SESSION['messages'] = array();
|
||||
function add_message($message) {
|
||||
// If more than one arguments passed, format message using sprintf
|
||||
if (func_num_args() > 1) {
|
||||
|
@ -113,42 +130,72 @@ if (isset($included_css_files) && is_array($included_css_files)) {
|
|||
$_css = $included_css_files;
|
||||
}
|
||||
else {
|
||||
$_css=array();
|
||||
$_css = array();
|
||||
}
|
||||
function add_css_file($files) {
|
||||
function add_css_file() {
|
||||
global $_css;
|
||||
if (!is_array($files)) $files=array($files);
|
||||
foreach ($files as $file)
|
||||
if (!in_array($file, $_css))
|
||||
$_css[]=$file;
|
||||
foreach (func_get_args() as $files) {
|
||||
if (!is_array($files)) $files=array($files);
|
||||
foreach ($files as $file) {
|
||||
if (!in_array($file, $_css))
|
||||
$_css[]=$file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_js=array();
|
||||
function add_js_file($files) {
|
||||
$_js = array();
|
||||
function add_js_file() {
|
||||
global $_js;
|
||||
if (!is_array($files)) $files=array($files);
|
||||
foreach ($files as $file)
|
||||
if (!in_array($file, $_js))
|
||||
$_js[]=$file;
|
||||
foreach (func_get_args() as $files) {
|
||||
if (!is_array($files)) $files = array($files);
|
||||
foreach ($files as $file) {
|
||||
if (!in_array($file, $_js))
|
||||
$_js[] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _defineCommonTemplateVariables($template, $pagetitle) {
|
||||
global $smarty, $_css, $_js, $status_list, $auth_user, $admin;
|
||||
global $smarty, $_css, $_js, $status_list, $auth_user, $admin, $webstats_js_code;
|
||||
$smarty->assign('pagetitle', $pagetitle);
|
||||
|
||||
// Messages
|
||||
$smarty -> assign('errors', (isset($_SESSION['errors'])?$_SESSION['errors']:array()));
|
||||
$smarty -> assign('messages', (isset($_SESSION['messages'])?$_SESSION['messages']:array()));
|
||||
|
||||
// Files inclusions
|
||||
$smarty -> assign('css', $_css);
|
||||
$smarty -> assign('js', $_js);
|
||||
|
||||
// Authenticated user info
|
||||
if (isset($auth_user))
|
||||
$smarty->assign('auth_user', $auth_user);
|
||||
$smarty->assign('errors', $_SESSION['errors']);
|
||||
$smarty->assign('messages', $_SESSION['messages']);
|
||||
$smarty->assign('css', $_css);
|
||||
$smarty->assign('js', $_js);
|
||||
|
||||
// Webstats JS code
|
||||
$smarty->assign(
|
||||
'webstats_js_code',
|
||||
isset($webstats_js_code)?$webstats_js_code:null);
|
||||
}
|
||||
|
||||
function display_template($template, $pagetitle=false) {
|
||||
if (!$template)
|
||||
logging("FATAL", _("No template specified."));
|
||||
|
||||
// If refresh parameter is present, remove it and redirect
|
||||
if (isset($_GET['refresh'])) {
|
||||
unset($_GET['refresh']);
|
||||
$url = get_current_url();
|
||||
if (!empty($_GET))
|
||||
$url .= '?'.http_build_query($_GET);
|
||||
redirect($url);
|
||||
return;
|
||||
}
|
||||
|
||||
$sentry_span = new SentrySpan('smarty.display_template', "Display Smarty template");
|
||||
|
||||
global $smarty;
|
||||
// If more than 2 arguments passed, format pagetitle using sprintf
|
||||
if ($pagetitle & func_num_args() > 2) {
|
||||
if ($pagetitle && func_num_args() > 2) {
|
||||
$pagetitle = call_user_func_array(
|
||||
'sprintf',
|
||||
array_merge(array($pagetitle), array_slice(func_get_args(), 2))
|
||||
|
@ -163,8 +210,11 @@ function display_template($template, $pagetitle=false) {
|
|||
catch (Exception $e) {
|
||||
log_exception($e, "Smarty - An exception occured displaying template '$template'");
|
||||
if ($template != 'fatal_error.tpl')
|
||||
logging("FATAL", _("An error occurred while viewing this page."));
|
||||
logging("FATAL", _("An error occurred while displaying this page."));
|
||||
}
|
||||
|
||||
$sentry_span->finish();
|
||||
|
||||
}
|
||||
|
||||
function display_ajax_return($data=null, $pretty=false) {
|
||||
|
@ -269,3 +319,5 @@ function smarty_encodeJsonBase64($params, $smarty) {
|
|||
echo base64_encode(json_encode($params['data']));
|
||||
}
|
||||
smarty_register_function('encodeJsonBase64','smarty_encodeJsonBase64');
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -154,7 +154,7 @@ add_cli_command(
|
|||
* all PO file in lang/[lang]/LC_MESSAGES.
|
||||
*
|
||||
* @param array $command_args The command arguments
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
function cli_update_messages($command_args) {
|
||||
global $root_dir_path, $root_lang_dir, $smarty_templates_dir;
|
||||
|
@ -234,6 +234,7 @@ function cli_update_messages($command_args) {
|
|||
}
|
||||
|
||||
logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
|
||||
return false;
|
||||
}
|
||||
add_cli_command(
|
||||
'update_messages',
|
||||
|
@ -248,7 +249,7 @@ add_cli_command(
|
|||
* to corresponding MO files and as JSON catalog (for translation in JS).
|
||||
*
|
||||
* @param array $command_args The command arguments
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
function cli_compile_messages($command_args) {
|
||||
global $root_dir_path, $root_lang_dir, $smarty_templates_dir;
|
||||
|
@ -354,6 +355,7 @@ function cli_compile_messages($command_args) {
|
|||
return !$error;
|
||||
}
|
||||
logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path);
|
||||
return false;
|
||||
}
|
||||
add_cli_command(
|
||||
'compile_messages',
|
||||
|
@ -368,3 +370,5 @@ add_cli_command(
|
|||
"directories to MO files and as JSON catalogs in public_html/translations."
|
||||
)
|
||||
);
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -145,3 +145,5 @@ function init_translation() {
|
|||
add_js_file(array("lib/babel.js", "js/translation.js", $js_translation_file));
|
||||
}
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -14,3 +14,5 @@ function get_item_from_url($id, $fatal=false) {
|
|||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -258,3 +258,5 @@ function handle_delete($request) {
|
|||
redirect('item');
|
||||
}
|
||||
add_url_handler('|^item/(?P<id>[0-9]+)/delete$|', 'handle_delete');
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
129
includes/url.php
129
includes/url.php
|
@ -26,15 +26,16 @@ $url_patterns =array();
|
|||
/**
|
||||
* Add an URL pattern
|
||||
*
|
||||
* @param $pattern string The URL pattern (required)
|
||||
* @param $handler callable The URL pattern handler (must be callable, required)
|
||||
* @param $authenticated boolean Permit to define if this URL is accessible only for
|
||||
* authenticated users (optional, default: true if the special
|
||||
* force_authentication function is defined, false otherwise)
|
||||
* @param $override boolean Allow override if a command already exists with the
|
||||
* same name (optional, default: false)
|
||||
* @param $api_mode boolean Enable API mode (optional, default: false)
|
||||
* @param $methods array|null HTTP method (optional, default: array('GET', 'POST'))
|
||||
* @param string|array $pattern The URL pattern or an array of patterns (required)
|
||||
* @param callable $handler The URL pattern handler (must be callable, required)
|
||||
* @param boolean $authenticated Permit to define if this URL is accessible only for
|
||||
* authenticated users (optional, default: true if the
|
||||
* special force_authentication function is defined,
|
||||
* false otherwise)
|
||||
* @param boolean $override Allow override if a command already exists with the
|
||||
* same name (optional, default: false)
|
||||
* @param boolean $api_mode Enable API mode (optional, default: false)
|
||||
* @param array|string|null $methods HTTP method (optional, default: array('GET', 'POST'))
|
||||
**/
|
||||
function add_url_handler($pattern, $handler=null, $authenticated=null, $override=true,
|
||||
$api_mode=false, $methods=null) {
|
||||
|
@ -67,9 +68,9 @@ function add_url_handler($pattern, $handler=null, $authenticated=null, $override
|
|||
}
|
||||
elseif ($override) {
|
||||
logging(
|
||||
'DEBUG',
|
||||
"URL : override pattern '$pattern' with handler '$handler' ".
|
||||
"(old handler = '".$url_patterns[$pattern]."')");
|
||||
'DEBUG', "URL : override pattern '%s' with handler '%s' (old handler = '%s')".
|
||||
$pattern, format_callable($handler), vardump($url_patterns[$pattern])
|
||||
);
|
||||
$url_patterns[$pattern] = array(
|
||||
'handler' => $handler,
|
||||
'authenticated' => $authenticated,
|
||||
|
@ -133,9 +134,9 @@ function error_page($request=null, $error_code=null) {
|
|||
/**
|
||||
* Error 404 handler
|
||||
*
|
||||
* @param[in] $request UrlRequest|null The request (optional, default: null)
|
||||
* @param UrlRequest|null $request The request (optional, default: null)
|
||||
*
|
||||
* @retval void
|
||||
* @return void
|
||||
**/
|
||||
function error_404($request=null) {
|
||||
error_page($request, 404);
|
||||
|
@ -174,7 +175,7 @@ function get_request($default_url=null) {
|
|||
|
||||
logging('DEBUG', "URL : current url = '$current_url'");
|
||||
logging(
|
||||
'DEBUG',
|
||||
'TRACE',
|
||||
"URL : check current url with the following URL patterns :\n - ".
|
||||
implode("\n - ", array_keys($url_patterns))
|
||||
);
|
||||
|
@ -185,7 +186,7 @@ function get_request($default_url=null) {
|
|||
// Reset last redirect
|
||||
if (isset($_SESSION['last_redirect']))
|
||||
unset($_SESSION['last_redirect']);
|
||||
logging('DEBUG', "URL : result :\n".varDump($request, 1));
|
||||
logging('TRACE', "URL : result :\n".vardump($request));
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
@ -212,9 +213,9 @@ function get_request($default_url=null) {
|
|||
/**
|
||||
* Check if the current requested URL match with a specific pattern
|
||||
*
|
||||
* @param $pattern string The URL pattern
|
||||
* @param $current_url string|false The current URL (optional)
|
||||
* @param $methods array|null HTTP method (optional, default: no check)
|
||||
* @param string $pattern The URL pattern
|
||||
* @param string|false $current_url The current URL (optional)
|
||||
* @param array|null $methods HTTP method (optional, default: no check)
|
||||
*
|
||||
* @return array|false The URL info if pattern matched, false otherwise.
|
||||
**/
|
||||
|
@ -229,7 +230,7 @@ function url_match($pattern, $current_url=false, $methods=null) {
|
|||
logging(
|
||||
'DEBUG',
|
||||
"URL : Match found with pattern '$pattern' :\n\t".
|
||||
str_replace("\n", "\n\t", print_r($m, 1)));
|
||||
str_replace("\n", "\n\t", print_r($m, true)));
|
||||
return $m;
|
||||
}
|
||||
return False;
|
||||
|
@ -241,10 +242,10 @@ 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('DEBUG', "URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
||||
logging('TRACE', "URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
||||
|
||||
$base = get_rewrite_base();
|
||||
logging('DEBUG', "URL : rewrite base = '$base'");
|
||||
logging('TRACE', "URL : rewrite base = '$base'");
|
||||
|
||||
if ($_SERVER['REQUEST_URI'] == $base)
|
||||
return '';
|
||||
|
@ -289,7 +290,7 @@ function get_rewrite_base() {
|
|||
/**
|
||||
* Trigger redirect to specified URL (or homepage if omited)
|
||||
*
|
||||
* @param $go string|false The destination URL
|
||||
* @param string|false $go The destination URL
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
|
@ -298,6 +299,15 @@ function redirect($go=false) {
|
|||
|
||||
if ($go===false)
|
||||
$go = "";
|
||||
// If more than one argument passed, format URL using sprintf & urlencode parameters
|
||||
elseif (func_num_args() > 1)
|
||||
$go = call_user_func_array(
|
||||
'sprintf',
|
||||
array_merge(
|
||||
array($go),
|
||||
array_map('urlencode', array_slice(func_get_args(), 1))
|
||||
)
|
||||
);
|
||||
|
||||
if (is_absolute_url($go))
|
||||
$url = $go;
|
||||
|
@ -332,7 +342,7 @@ function redirect($go=false) {
|
|||
* invoke the force_authentication() special function (or trigger a fatal error
|
||||
* if it's not defined).
|
||||
*
|
||||
* @param $default_url string|null The default URL if current one does not
|
||||
* @param string|null $default_url The default URL if current one does not
|
||||
* match with any configured pattern.
|
||||
*
|
||||
* @return void
|
||||
|
@ -340,10 +350,14 @@ function redirect($go=false) {
|
|||
function handle_request($default_url=null) {
|
||||
global $smarty, $api_mode;
|
||||
|
||||
$sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request');
|
||||
|
||||
$request = get_request($default_url);
|
||||
|
||||
if (!is_callable($request -> handler)) {
|
||||
logging('ERROR', "URL handler function ".$request -> handler."() does not exists !");
|
||||
logging(
|
||||
'ERROR', "URL handler function %s does not exists !",
|
||||
format_callable($request -> handler));
|
||||
logging('FATAL', _("This request cannot be processed."));
|
||||
}
|
||||
|
||||
|
@ -360,19 +374,20 @@ function handle_request($default_url=null) {
|
|||
logging('FATAL', _("Authentication required but force_authentication function is not defined."));
|
||||
|
||||
try {
|
||||
return call_user_func($request -> handler, $request);
|
||||
call_user_func($request -> handler, $request);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
log_exception(
|
||||
$e, "An exception occured running URL handler function ".$request -> handler."()");
|
||||
logging('FATAL', _("This request could not be processed correctly."));
|
||||
}
|
||||
$sentry_span->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing slash in specified URL
|
||||
*
|
||||
* @param $url string The URL
|
||||
* @param string $url The URL
|
||||
*
|
||||
* @return string The specified URL without trailing slash
|
||||
**/
|
||||
|
@ -390,13 +405,13 @@ function remove_trailing_slash($url) {
|
|||
* Check if session key is present and valid and set AJAX
|
||||
* mode.
|
||||
*
|
||||
* @param $session_key string The current session key (optional)
|
||||
* @param string|null $session_key string The current session key (optional)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function check_ajax_request($session_key=null) {
|
||||
global $ajax, $debug_ajax;
|
||||
$ajax=true;
|
||||
$ajax = true;
|
||||
|
||||
if (check_session_key($session_key))
|
||||
fatal_error('Invalid request');
|
||||
|
@ -408,7 +423,7 @@ function check_ajax_request($session_key=null) {
|
|||
/**
|
||||
* Get the public absolute URL
|
||||
*
|
||||
* @param $relative_url string|null Relative URL to convert (Default: current URL)
|
||||
* @param string|null $relative_url Relative URL to convert (Default: current URL)
|
||||
*
|
||||
* @return string The public absolute URL
|
||||
**/
|
||||
|
@ -430,7 +445,7 @@ function get_absolute_url($relative_url=null) {
|
|||
}
|
||||
|
||||
if (substr($relative_url, 0, 1) == '/')
|
||||
$relative_url = substr($url, 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");
|
||||
return $url;
|
||||
|
@ -439,7 +454,7 @@ function get_absolute_url($relative_url=null) {
|
|||
/**
|
||||
* Check if specified URL is absolute
|
||||
*
|
||||
* @param $url string The URL to check
|
||||
* @param string $url The URL to check
|
||||
*
|
||||
* @return boolean True if specified URL is absolute, False otherwise
|
||||
**/
|
||||
|
@ -450,12 +465,12 @@ function is_absolute_url($url) {
|
|||
/**
|
||||
* Add parameter in specified URL
|
||||
*
|
||||
* @param &$url string The reference of the URL
|
||||
* @param $param string The parameter name
|
||||
* @param $value string The parameter value
|
||||
* @param $encode boolean Set if parameter value must be URL encoded (optional, default: true)
|
||||
* @param string &$url The reference of the URL
|
||||
* @param string $param The parameter name
|
||||
* @param string $value The parameter value
|
||||
* @param boolean $encode Set if parameter value must be URL encoded (optional, default: true)
|
||||
*
|
||||
* @return string|null The completed URL
|
||||
* @return string The completed URL
|
||||
*/
|
||||
function add_url_parameter(&$url, $param, $value, $encode=true) {
|
||||
if (strpos($url, '?') === false)
|
||||
|
@ -503,7 +518,7 @@ class UrlRequest {
|
|||
/**
|
||||
* Get request info
|
||||
*
|
||||
* @param $key string The name of the info
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return mixed The value
|
||||
**/
|
||||
|
@ -527,15 +542,39 @@ class UrlRequest {
|
|||
logging('WARNING', "__get($key): invalid property requested\n".get_debug_backtrace_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request info
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
* @param mixed $value The value of the info
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public function __set($key, $value) {
|
||||
if ($key == 'referer')
|
||||
$_SERVER['HTTP_REFERER'] = $value;
|
||||
elseif ($key == 'http_method')
|
||||
$_SERVER['REQUEST_METHOD'] = $value;
|
||||
else
|
||||
$this->url_params[$key] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check is request info is set
|
||||
*
|
||||
* @param $key string The name of the info
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return boolval True is info is set, False otherwise
|
||||
* @return bool True is info is set, False otherwise
|
||||
**/
|
||||
public function __isset($key) {
|
||||
if (in_array($key, array('current_url', 'handler', 'authenticated')))
|
||||
if (
|
||||
in_array(
|
||||
$key, array('current_url', 'handler', 'authenticated',
|
||||
'api_mode', 'referer', 'http_method')
|
||||
)
|
||||
)
|
||||
return True;
|
||||
return array_key_exists($key, $this->url_params);
|
||||
}
|
||||
|
@ -543,9 +582,9 @@ class UrlRequest {
|
|||
/**
|
||||
* Get request parameter
|
||||
*
|
||||
* @param $parameter string The name of the parameter
|
||||
* @param $decode string If true, the parameter value will be urldecoded
|
||||
* (optional, default: true)
|
||||
* @param string $parameter The name of the parameter
|
||||
* @param bool $decode If true, the parameter value will be urldecoded
|
||||
* (optional, default: true)
|
||||
*
|
||||
* @return mixed The value or false if parameter does not exists
|
||||
**/
|
||||
|
@ -570,3 +609,5 @@ class UrlRequest {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
31
phpstan.neon
Normal file
31
phpstan.neon
Normal file
|
@ -0,0 +1,31 @@
|
|||
parameters:
|
||||
level: 5
|
||||
paths:
|
||||
- includes
|
||||
- public_html
|
||||
- bin
|
||||
excludePaths:
|
||||
- includes/config.local.php
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#Instantiated class Mail_mime not found\\.#"
|
||||
path: includes/mail.php
|
||||
-
|
||||
message: "#Call to method (setFrom|setSubject|setHTMLBody|setTXTBody|get|headers|addAttachment)\\(\\) on an unknown class Mail_mime\\.#"
|
||||
path: includes/mail.php
|
||||
-
|
||||
message: "#Call to static method factory\\(\\) on an unknown class Mail\\.#"
|
||||
path: includes/mail.php
|
||||
-
|
||||
message: "#Call to static method isError\\(\\) on an unknown class PEAR\\.#"
|
||||
path: includes/mail.php
|
||||
-
|
||||
message: "#Variable \\$root_dir_path might not be defined\\.#"
|
||||
paths:
|
||||
- includes/config.inc.php
|
||||
- "#Access to private property UrlRequest::\\$(handler|api_mode|authenticated)\\.#"
|
||||
-
|
||||
message: "#Variable \\$status_list might not be defined\\.#"
|
||||
paths:
|
||||
- includes/cli.php
|
||||
|
|
@ -5,3 +5,5 @@ include 'url-public.php';
|
|||
|
||||
$default_url='';
|
||||
handle_request();
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -97,5 +97,6 @@
|
|||
<script language="javascript" src="{$file}"></script>
|
||||
{/foreach}
|
||||
|
||||
{if $webstats_js_code}{$webstats_js_code}{/if}
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue