Skip to content

Commit 39c7696

Browse files
feat: Add filename/line data to DEBUG messages
Also, tried to clean up the code formatting according to the guidelines _and_ added (most) entries for PHPDoc.
1 parent 8bb306e commit 39c7696

File tree

1 file changed

+178
-66
lines changed

1 file changed

+178
-66
lines changed

server/lib/app.inc.php

Lines changed: 178 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
2-
3-
/*
4-
Copyright (c) 2007, Till Brehm, projektfarm Gmbh
2+
/**
3+
Copyright (c) 2007-2022, Till Brehm, projektfarm Gmbh
54
All rights reserved.
65
76
Redistribution and use in source and binary forms, with or without modification,
@@ -28,35 +27,52 @@
2827
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2928
*/
3029

31-
//* Set timezone
32-
if(isset($conf['timezone']) && $conf['timezone'] != '') date_default_timezone_set($conf['timezone']);
30+
// Set timezone
31+
if(isset($conf['timezone']) && $conf['timezone'] != '') { // note: !empty($conf['timezone']) should give the same result and is more idiomatic for current versions of PHP (gwyneth 20220315)
32+
date_default_timezone_set($conf['timezone']);
33+
}
3334

35+
/**
36+
* Class for defining (mostly static) methods that are commonly used across the whole application.
37+
*
38+
* @category unknown
39+
* @package server
40+
* @author Till Brehm
41+
* @license bsd-3-clause
42+
* @link empty
43+
**/
3444
class app {
35-
36-
var $loaded_modules = array();
37-
var $loaded_plugins = array();
45+
/** @var array List of modules that have been loaded. */
46+
var $loaded_modules = [];
47+
/** @var array List of plugins that have been loaded. */
48+
var $loaded_plugins = [];
49+
/** @var callable Script calling this. */
3850
var $_calling_script = '';
39-
/**
40-
* @var db
41-
*/
51+
/** @var resource? Database used for ISPConfig3. */
4252
public $db;
4353

54+
/**
55+
* Class constructor, which depends on the global configuration stored in $conf.
56+
*
57+
* @param void
58+
* @return void
59+
*/
4460
function __construct() {
45-
61+
/** @var object Global object storing this application's configuration. */
4662
global $conf;
4763

4864
if($conf['start_db'] == true) {
49-
$this->load('db_'.$conf['db_type']);
65+
$this->load('db_' . $conf['db_type']);
5066
try {
5167
$this->db = new db;
52-
} catch (Exception $e) {
68+
} catch(Exception $e) {
5369
$this->db = false;
5470
}
5571

5672
/*
57-
Initialize the connection to the master DB,
58-
if we are in a multiserver setup
59-
*/
73+
Initialize the connection to the master DB,
74+
if we are in a multiserver setup
75+
*/
6076

6177
if($conf['dbmaster_host'] != '' && ($conf['dbmaster_host'] != $conf['db_host'] || ($conf['dbmaster_host'] == $conf['db_host'] && $conf['dbmaster_database'] != $conf['db_database']))) {
6278
try {
@@ -67,14 +83,19 @@ function __construct() {
6783
} else {
6884
$this->dbmaster = $this->db;
6985
}
70-
71-
7286
}
87+
} // end constructor
7388

74-
}
75-
89+
/**
90+
* Getter method for some of the (valid) proprieties.
91+
*
92+
* @param string $name A valid property name to get. Will be checked for validity first!
93+
*
94+
* @return mixed
95+
*/
7696
public function __get($name) {
77-
$valid_names = array('functions', 'getconf', 'letsencrypt', 'modules', 'plugins', 'services', 'system');
97+
/** @var array List of all possible proprieties that are valid to get. */
98+
$valid_names = ['functions', 'getconf', 'letsencrypt', 'modules', 'plugins', 'services', 'system'];
7899
if(!in_array($name, $valid_names)) {
79100
trigger_error('Undefined property ' . $name . ' of class app', E_USER_WARNING);
80101
}
@@ -89,14 +110,37 @@ public function __get($name) {
89110
}
90111
}
91112

113+
/**
114+
* Sets the calling script.
115+
*
116+
* @param callable $caller Calling script function.
117+
*
118+
* @return void
119+
*/
92120
function setCaller($caller) {
93121
$this->_calling_script = $caller;
94122
}
95123

124+
/**
125+
* Gets the calling script.
126+
*
127+
* Note that there is no error checking!
128+
*
129+
* @param void
130+
*
131+
* @return callable|null
132+
*/
96133
function getCaller() {
97134
return $this->_calling_script;
98135
}
99136

137+
/**
138+
* Emergency exit funcion.
139+
*
140+
* @param string $errmsg Error message to be displayedby the die() command on exit.
141+
*
142+
* @return void
143+
*/
100144
function forceErrorExit($errmsg = 'undefined') {
101145
global $conf;
102146

@@ -106,81 +150,145 @@ function forceErrorExit($errmsg = 'undefined') {
106150
die('Exiting because of error: ' . $errmsg);
107151
}
108152

153+
/**
154+
* Dynamic plugin loader and instantiator.
155+
*
156+
* This will include PHP scripts on demand, each representing a class to be loaded,
157+
* and if the process succeeds, it will retrieve an instance for the class.
158+
*
159+
* @param string $classes A list of plugin classes to be loaded (e.g. their files will be included)
160+
* and subsequently instantiated; it's a comma-separated string.
161+
*
162+
* @return void
163+
*/
109164
function uses($classes) {
110-
111165
global $conf;
112166

167+
/** @var array|null List of classes to be used, as an array, after successful 'explosion' */
113168
$cl = explode(',', $classes);
114169
if(is_array($cl)) {
115170
foreach($cl as $classname) {
116171
if(!@is_object($this->$classname)) {
117-
if(is_file($conf['classpath'].'/'.$classname.'.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'].'/'.$classname.'.inc.php'))) {
118-
include_once $conf['classpath'].'/'.$classname.'.inc.php';
172+
if(is_file($conf['classpath'] . '/' . $classname . '.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'] . '/' . $classname . '.inc.php'))) {
173+
include_once $conf['classpath'] . '/' . $classname . '.inc.php';
119174
$this->$classname = new $classname;
120175
}
121176
}
122177
}
123178
}
124179
}
125180

181+
/**
182+
* Dynamic plugin loader (no instantation).
183+
*
184+
* Similar to uses() but does _not_ instantate a new class; files are merely included.
185+
* die() is called on a failure to include the file for a class.
186+
*
187+
* @param string $classes A list of plugin classes to be loaded (e.g. their files will be included);
188+
* it's a comma-separated string.
189+
*
190+
* @return void
191+
*/
126192
function load($classes) {
127-
128193
global $conf;
129194

195+
/** @var array|null List of classes to be loaded, as an array, after successful 'explosion' */
130196
$cl = explode(',', $classes);
131197
if(is_array($cl)) {
132198
foreach($cl as $classname) {
133-
if(is_file($conf['classpath'].'/'.$classname.'.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'].'/'.$classname.'.inc.php'))) {
134-
include_once $conf['classpath'].'/'.$classname.'.inc.php';
199+
if(is_file($conf['classpath'] . '/' . $classname . '.inc.php') && (DEVSYSTEM || !is_link($conf['classpath'] . '/' . $classname . '.inc.php'))) {
200+
include_once $conf['classpath'] . '/' . $classname . '.inc.php';
135201
} else {
136-
die('Unable to load: '.$conf['classpath'].'/'.$classname.'.inc.php');
202+
die('Unable to load: ' . $conf['classpath'] . '/' . $classname . '.inc.php');
137203
}
138204
}
139205
}
140206
}
141207

142-
/*
143-
0 = DEBUG
144-
1 = WARNING
145-
2 = ERROR
146-
*/
147-
208+
/**
209+
* Logs a message with a certain priority to the different log backends.
210+
*
211+
* This method will check if the priority is equal or larger than what the user has
212+
* defined as the minimum logging level, and will output to several logging facilities:
213+
* - At the very least, the message will _usually_ go to stdout;
214+
* - It may optionally also go to the file log (usually `/var/log/ispconfig/ispconfig.log`)
215+
* which will be created if it doesn't exist;
216+
* - When the $dblog parameter is set to true (the default), the message will also be logged
217+
* to the database;
218+
* - If the system is configured to send email messages to the administrator,
219+
* this method will also handle those (assuming, again, that the priority matches).
220+
*
221+
* Debugging messages will also have the name of the calling module/script as well as a line number
222+
* to assist error tracking (gwyneth 20220315). This incurs in a slight performance hit.
223+
*
224+
* @param string $msg The message to be logged.
225+
* @param int $priority Should be set to 0 = DEBUG, 1 = WARNING or 2 = ERROR; anything else
226+
* will skip setting the priority textual variable.
227+
* @param bool $dblog Should the message also be logged to the database? (Default is _true_)
228+
*
229+
* @return void
230+
*
231+
* @note The error() method below seems to write to an invalid priority (3), which will cause
232+
* no message priority text to be emitted, and will _force_ a database write and/or sending
233+
* an email to the administrator.
234+
*/
148235
function log($msg, $priority = 0, $dblog = true) {
149-
150236
global $conf;
151237

238+
/**
239+
* @var string $file_line_caller
240+
*
241+
* For debugging, deal with retrieving caller information from the stack. (gwyneth 20220315)
242+
* See https://stackoverflow.com/q/1252529/1035977 (including the precious comments!) for an explanation
243+
* of how this works.
244+
**/
245+
$file_line_caller = "";
246+
/** @var string Defined here because recent versions of PHP are stricter with scoping issues. (gwyneth 20220315) */
247+
$priority_txt = '';
248+
152249
switch ($priority) {
153250
case 0:
154-
$priority_txt = 'DEBUG';
155-
break;
251+
$priority_txt = 'DEBUG';
252+
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); // we don't need _all_ data, so we save some processing time here (gwyneth 20220315)
253+
$caller = array_shift($bt);
254+
$file_line_caller = '[' . strtr(basename($caller['file'], '.php'), '_', ' ') . ':' . $caller['line'] . '] ';
255+
break;
156256
case 1:
157-
$priority_txt = 'WARNING';
158-
break;
257+
$priority_txt = 'WARNING';
258+
break;
159259
case 2:
160-
$priority_txt = 'ERROR';
161-
break;
260+
$priority_txt = 'ERROR';
261+
break;
262+
// Note: $this->error() seems to use case 3 to deliberately skip setting a priority text.
263+
// It will also *force* a write to the logs and/or send emails. (gwyneth 20220315)
162264
}
163-
$log_msg = @date('d.m.Y-H:i').' - '.$priority_txt.' - '. $msg;
164265

266+
/** @var string Formatted message to be sent to the logging subsystems. */
267+
$log_msg = @date('d.m.Y-H:i') . ' - ' . $priority_txt .' ' . $file_line_caller . '- '. $msg;
268+
269+
// Check if the user-set priority defines that this message should be output at all.
165270
if($priority >= $conf['log_priority']) {
166-
//if (is_writable($conf["log_file"])) {
167-
if (!$fp = fopen($conf['log_file'], 'a')) {
271+
// Prepare to add a line on the logfile, or to create the logfile in
272+
// append mode if it doesn't exist yet. Failure means that die() is called.
273+
274+
//if(is_writable($conf["log_file"])) {
275+
if(!$fp = fopen($conf['log_file'], 'a')) {
168276
die('Unable to open logfile.');
169277
}
170278

171-
if (!fwrite($fp, $log_msg."\r\n")) {
279+
if(!fwrite($fp, $log_msg . '\r\n')) {
172280
die('Unable to write to logfile.');
173281
}
174282

175-
echo $log_msg."\n";
283+
echo $log_msg . "\n";
176284
fclose($fp);
177285

178-
// Log to database
286+
// Log to database.
179287
if($dblog === true && isset($this->dbmaster)) {
180288
$server_id = $conf['server_id'];
181289
$loglevel = $priority;
182290
$message = $msg;
183-
$datalog_id = (isset($this->modules->current_datalog_id) && $this->modules->current_datalog_id > 0)?$this->modules->current_datalog_id:0;
291+
$datalog_id = (isset($this->modules->current_datalog_id) && $this->modules->current_datalog_id > 0)? $this->modules->current_datalog_id : 0;
184292
if($datalog_id > 0) {
185293
$tmp_rec = $this->dbmaster->queryOneRecord("SELECT count(syslog_id) as number FROM sys_log WHERE datalog_id = ? AND loglevel = ?", $datalog_id, LOGLEVEL_ERROR);
186294
//* Do not insert duplicate errors into the web log.
@@ -198,18 +306,18 @@ function log($msg, $priority = 0, $dblog = true) {
198306
// die("Unable to write to logfile.");
199307
//}
200308

201-
202309
} // if
203310

311+
// Send an email to the administrator if the current priority demands it.
204312
if(isset($conf['admin_notify_priority']) && $priority >= $conf['admin_notify_priority'] && $conf['admin_mail'] != '') {
205313
if($conf['hostname'] != 'localhost' && $conf['hostname'] != '') {
206314
$hostname = $conf['hostname'];
207315
} else {
208316
$hostname = exec('hostname -f');
209317
}
210-
// send notification to admin
318+
// Send notification to admin.
211319
$mailBody = $hostname . " - " . $log_msg;
212-
$mailSubject = substr("[" . $hostname . "]" . " " . $log_msg, 0, 70).'...';
320+
$mailSubject = substr("[" . $hostname . "]" . " " . $log_msg, 0, 70) . '...';
213321
$mailHeaders = "MIME-Version: 1.0" . "\n";
214322
$mailHeaders .= "Content-type: text/plain; charset=utf-8" . "\n";
215323
$mailHeaders .= "Content-Transfer-Encoding: 8bit" . "\n";
@@ -218,26 +326,30 @@ function log($msg, $priority = 0, $dblog = true) {
218326

219327
mail($conf['admin_mail'], $mailSubject, $mailBody, $mailHeaders);
220328
}
221-
} // func
222-
223-
224-
/*
225-
0 = DEBUG
226-
1 = WARNING
227-
2 = ERROR
228-
*/
329+
} // func log
229330

331+
/**
332+
* Logs a message with an undefined priority (3) and dies.
333+
*
334+
* This method writes to an invalid/undefined priority level (3), which will cause
335+
* no message priority text to be emitted, but will _force_ a database write and/or sending
336+
* an email to the administrator.
337+
*
338+
* @param string $msg The message to be logged.
339+
*
340+
* @return void
341+
*/
230342
function error($msg) {
231-
$this->log($msg, 3);
343+
$this->log($msg, 3); // isn't this supposed to be error code 2? (gwyneth 20220315)
232344
die($msg);
233345
}
234-
235346
}
236347

237-
/*
238-
Initialize application (app) object
239-
*/
240-
348+
/**
349+
* @var \app $app
350+
*
351+
* Initialize application object.
352+
*/
241353
$app = new app;
242354

243355
?>

0 commit comments

Comments
 (0)