ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.ilErrorHandling.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3 /* Copyright (c) 2015 Richard Klees, Extended GPL, see docs/LICENSE */
4 /* Copyright (c) 2016 Stefan Hecken, Extended GPL, see docs/LICENSE */
5 
6 require_once 'Services/Environment/classes/class.ilRuntime.php';
7 
22 require_once("Services/Exceptions/classes/class.ilDelegatingHandler.php");
23 require_once("Services/Exceptions/classes/class.ilPlainTextHandler.php");
24 require_once("Services/Exceptions/classes/class.ilTestingHandler.php");
25 
26 use Whoops\Run;
30 
31 class ilErrorHandling extends PEAR
32 {
38  public $DEBUG_ENV;
39 
45  public $FATAL;
46 
52  public $WARNING;
53 
59  public $MESSAGE;
60 
65  protected static $whoops_handlers_registered = false;
66 
71  public function __construct()
72  {
73  parent::__construct();
74 
75  // init vars
76  $this->DEBUG_ENV = true;
77  $this->FATAL = 1;
78  $this->WARNING = 2;
79  $this->MESSAGE = 3;
80 
81  $this->error_obj = false;
82 
83  $this->initWhoopsHandlers();
84 
85  // somehow we need to get rid of the whoops error handler
86  restore_error_handler();
87  set_error_handler(array($this, "handlePreWhoops"));
88  }
89 
98  protected function initWhoopsHandlers()
99  {
100  if (self::$whoops_handlers_registered) {
101  // Only register whoops error handlers once.
102  return;
103  }
104 
105  $ilRuntime = $this->getIlRuntime();
106  $this->whoops = $this->getWhoops();
107 
108  $this->whoops->pushHandler(new ilDelegatingHandler($this));
109 
110  if ($ilRuntime->shouldLogErrors()) {
111  $this->whoops->pushHandler($this->loggingHandler());
112  }
113 
114  $this->whoops->register();
115 
116  self::$whoops_handlers_registered = true;
117  }
118 
126  public function getHandler()
127  {
128  // TODO: * Use Whoops in production mode? This would require an appropriate
129  // error-handler.
130  // * Check for context? The current implementation e.g. would output HTML for
131  // for SOAP.
132 
133  if ($this->isDevmodeActive()) {
134  return $this->devmodeHandler();
135  }
136 
137  return $this->defaultHandler();
138  }
139 
140  public function getLastError()
141  {
142  return $this->error_obj;
143  }
144 
150  public function errorHandler($a_error_obj)
151  {
152  global $log;
153 
154  // see bug 18499 (some calls to raiseError do not pass a code, which leads to security issues, if these calls
155  // are done due to permission checks)
156  if ($a_error_obj->getCode() == null) {
157  $a_error_obj->code = $this->WARNING;
158  }
159 
160  $this->error_obj = &$a_error_obj;
161  //echo "-".$_SESSION["referer"]."-";
162  if ($_SESSION["failure"] && substr($a_error_obj->getMessage(), 0, 22) != "Cannot find this block") {
163  $m = "Fatal Error: Called raise error two times.<br>" .
164  "First error: " . $_SESSION["failure"] . '<br>' .
165  "Last Error:" . $a_error_obj->getMessage();
166  //return;
167  $log->write($m);
168  #$log->writeWarning($m);
169  #$log->logError($a_error_obj->getCode(), $m);
170  unset($_SESSION["failure"]);
171  die($m);
172  }
173 
174  if (substr($a_error_obj->getMessage(), 0, 22) == "Cannot find this block") {
175  if (DEVMODE == 1) {
176  echo "<b>DEVMODE</b><br><br>";
177  echo "<b>Template Block not found.</b><br>";
178  echo "You used a template block in your code that is not available.<br>";
179  echo "Native Messge: <b>" . $a_error_obj->getMessage() . "</b><br>";
180  if (is_array($a_error_obj->backtrace)) {
181  echo "Backtrace:<br>";
182  foreach ($a_error_obj->backtrace as $b) {
183  if ($b["function"] == "setCurrentBlock" &&
184  basename($b["file"]) != "class.ilTemplate.php") {
185  echo "<b>";
186  }
187  echo "File: " . $b["file"] . ", ";
188  echo "Line: " . $b["line"] . ", ";
189  echo $b["function"] . "()<br>";
190  if ($b["function"] == "setCurrentBlock" &&
191  basename($b["file"]) != "class.ilTemplate.php") {
192  echo "</b>";
193  }
194  }
195  }
196  exit;
197  }
198  return;
199  }
200 
201  if (is_object($log) and $log->enabled == true) {
202  $log->write($a_error_obj->getMessage());
203  #$log->logError($a_error_obj->getCode(),$a_error_obj->getMessage());
204  }
205 
206  //echo $a_error_obj->getCode().":"; exit;
207  if ($a_error_obj->getCode() == $this->FATAL) {
208  trigger_error(stripslashes($a_error_obj->getMessage()), E_USER_ERROR);
209  exit();
210  }
211 
212  if ($a_error_obj->getCode() == $this->WARNING) {
213  if ($this->DEBUG_ENV) {
214  $message = $a_error_obj->getMessage();
215  } else {
216  $message = "Under Construction";
217  }
218 
219  $_SESSION["failure"] = $message;
220 
221  if (!defined("ILIAS_MODULE")) {
222  ilUtil::redirect("error.php");
223  } else {
224  ilUtil::redirect("../error.php");
225  }
226  }
227 
228  if ($a_error_obj->getCode() == $this->MESSAGE) {
229  $_SESSION["failure"] = $a_error_obj->getMessage();
230  // save post vars to session in case of error
231  $_SESSION["error_post_vars"] = $_POST;
232 
233  if (empty($_SESSION["referer"])) {
234  $dirname = dirname($_SERVER["PHP_SELF"]);
235  $ilurl = parse_url(ILIAS_HTTP_PATH);
236  $subdir = substr(strstr($dirname, $ilurl["path"]), strlen($ilurl["path"]));
237  $updir = "";
238 
239  if ($subdir) {
240  $num_subdirs = substr_count($subdir, "/");
241 
242  for ($i = 1;$i <= $num_subdirs;$i++) {
243  $updir .= "../";
244  }
245  }
246  ilUtil::redirect($updir . "index.php");
247  }
248 
249  /* #12104
250  check if already GET-Parameters exists in Referer-URI
251  if (substr($_SESSION["referer"],-4) == ".php")
252  {
253  $glue = "?";
254  }
255  else
256  {
257  // this did break permanent links (".html&")
258  $glue = "&";
259  }
260  */
261  ilUtil::redirect($_SESSION["referer"]);
262  }
263  }
264 
265  public function getMessage()
266  {
267  return $this->message;
268  }
269  public function setMessage($a_message)
270  {
271  $this->message = $a_message;
272  }
273  public function appendMessage($a_message)
274  {
275  if ($this->getMessage()) {
276  $this->message .= "<br /> ";
277  }
278  $this->message .= $a_message;
279  }
280 
290  public static function _ilErrorWriter($errno, $errstr, $errfile, $errline)
291  {
292  global $ilLog;
293 
294  switch ($errno) {
295  case E_USER_ERROR:
296  $ilLog->write('PHP errror: ' . $errstr . '. FATAL error on line ' . $errline . ' in file ' . $errfile);
297  unset($ilLog);
298  exit(1);
299 
300  case E_USER_WARNING:
301  $ilLog->write('PHP warning: [' . $errno . '] ' . $errstr . ' on line ' . $errline . ' in file ' . $errfile);
302  break;
303 
304  }
305  return true;
306  }
307 
312  protected function getIlRuntime()
313  {
314  return ilRuntime::getInstance();
315  }
316 
321  protected function getWhoops()
322  {
323  return new Run();
324  }
325 
330  protected function isDevmodeActive()
331  {
332  return defined("DEVMODE") && (int) DEVMODE === 1;
333  }
334 
339  protected function defaultHandler()
340  {
341  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
342  return new CallbackHandler(function ($exception, Inspector $inspector, Run $run) {
343  global $lng;
344 
345  require_once("Services/Logging/classes/error/class.ilLoggingErrorSettings.php");
346  require_once("Services/Logging/classes/error/class.ilLoggingErrorFileStorage.php");
347  require_once("Services/Utilities/classes/class.ilUtil.php");
348 
349  $session_id = substr(session_id(), 0, 5);
350  $random = new \ilRandom();
351  $err_num = $random->int(1, 9999);
352  $file_name = $session_id . "_" . $err_num;
353 
355  if (!empty($logger->folder())) {
356  $lwriter = new ilLoggingErrorFileStorage($inspector, $logger->folder(), $file_name);
357  $lwriter->write();
358  }
359 
360  //Use $lng if defined or fallback to english
361  if ($lng !== null) {
362  $lng->loadLanguageModule('logging');
363  $message = sprintf($lng->txt("log_error_message"), $file_name);
364 
365  if ($logger->mail()) {
366  $message .= " " . sprintf($lng->txt("log_error_message_send_mail"), $logger->mail(), $file_name, $logger->mail());
367  }
368  } else {
369  $message = 'Sorry, an error occured. A logfile has been created which can be identified via the code "' . $file_name . '"';
370 
371  if ($logger->mail()) {
372  $message .= ' ' . 'Please send a mail to <a href="mailto:' . $logger->mail() . '?subject=code: ' . $file_name . '">' . $logger->mail() . '</a>';
373  }
374  }
375 
377  ilUtil::redirect("error.php");
378  });
379  }
380 
385  protected function devmodeHandler()
386  {
387  global $ilLog;
388 
389  switch (ERROR_HANDLER) {
390  case "TESTING":
391  return new ilTestingHandler();
392  case "PLAIN_TEXT":
393  return new ilPlainTextHandler();
394  case "PRETTY_PAGE":
395  return new PrettyPageHandler();
396  default:
397  if ($ilLog) {
398  $ilLog->write("Unknown or undefined error handler '" . ERROR_HANDLER . "'. "
399  . "Falling back to PrettyPageHandler.");
400  }
401  return new PrettyPageHandler();
402  }
403  }
404 
409  protected function loggingHandler()
410  {
411  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
412  return new CallbackHandler(function ($exception, Inspector $inspector, Run $run) {
417  global $ilLog;
418 
419  if (is_object($ilLog)) {
420  $message = $exception->getMessage() . ' in ' . $exception->getFile() . ":" . $exception->getLine();
421  $message .= $exception->getTraceAsString();
422  $ilLog->error($exception->getCode() . ' ' . $message);
423  }
424 
425  // Send to system logger
426  error_log($exception->getMessage());
427  });
428  }
429 
430  public function handlePreWhoops($level, $message, $file, $line)
431  {
432  global $ilLog;
433 
434  if ($level & error_reporting()) {
435 
436  // correct-with-php5-removal JL start
437  // ignore all E_STRICT that are E_NOTICE (or nothing at all) in PHP7
438  if (version_compare(PHP_VERSION, '7.0.0', '<')) {
439  if ($level == E_STRICT) {
440  if (!stristr($message, "should be compatible") &&
441  !stristr($message, "should not be called statically") &&
442  !stristr($message, "should not be abstract")) {
443  return true;
444  };
445  }
446  }
447  // correct-with-php5-removal end
448 
449  if (!$this->isDevmodeActive()) {
450  // log E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED only
451  if ($level >= E_USER_NOTICE) {
452  if ($ilLog) {
453  $severity = Whoops\Util\Misc::TranslateErrorCode($level);
454  $ilLog->write("\n\n" . $severity . " - " . $message . "\n" . $file . " - line " . $line . "\n");
455  }
456  return true;
457  }
458  }
459 
460  // trigger whoops error handling
461  if ($this->whoops) {
462  return $this->whoops->handleError($level, $message, $file, $line);
463  }
464  }
465 
466  return false;
467  }
468 } // END class.ilErrorHandling
isDevmodeActive()
Is the DEVMODE switched on?
getWhoops()
Get an instance of Whoops/Run.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static getInstance()
__construct()
Constructor public.
$_SESSION["AccountId"]
$log
Definition: sabredav.php:21
Saves error informations into file.
Error Handling & global info handling uses PEAR error class.
defaultHandler()
Get a default error handler.
getIlRuntime()
Get ilRuntime.
catch(Exception $e) $message
getHandler()
Get a handler for an error or exception.
$lng
errorHandler($a_error_obj)
defines what has to happen in case of error private
static _ilErrorWriter($errno, $errstr, $errfile, $errline)
This is used in Soap calls to write PHP error in ILIAS Logfile Not used yet!!!
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
handlePreWhoops($level, $message, $file, $line)
exit
Definition: backend.php:16
initWhoopsHandlers()
Initialize Error and Exception Handlers.
devmodeHandler()
Get the handler to be used in DEVMODE.
$i
Definition: disco.tpl.php:19
A Whoops error handler for testing.
Wrapper for Closures passed as handlers.
static redirect($a_script)
$_POST["username"]