ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 set_include_path("./Services/Database/lib/PEAR" . PATH_SEPARATOR . ini_get('include_path'));
26 if (!class_exists('PEAR')) {
27  require_once 'PEAR.php';
28 }
29 use Whoops\Run;
33 
34 class ilErrorHandling extends PEAR
35 {
41  public $DEBUG_ENV;
42 
48  public $FATAL;
49 
55  public $WARNING;
56 
62  public $MESSAGE;
63 
68  protected static $whoops_handlers_registered = false;
69 
74  public function __construct()
75  {
76  parent::__construct();
77 
78  // init vars
79  $this->DEBUG_ENV = true;
80  $this->FATAL = 1;
81  $this->WARNING = 2;
82  $this->MESSAGE = 3;
83 
84  $this->error_obj = false;
85 
86  $this->initWhoopsHandlers();
87 
88  // somehow we need to get rid of the whoops error handler
89  restore_error_handler();
90  set_error_handler(array($this, "handlePreWhoops"));
91  }
92 
101  protected function initWhoopsHandlers()
102  {
103  if (self::$whoops_handlers_registered) {
104  // Only register whoops error handlers once.
105  return;
106  }
107 
108  $ilRuntime = $this->getIlRuntime();
109  $this->whoops = $this->getWhoops();
110 
111  $this->whoops->pushHandler(new ilDelegatingHandler($this));
112 
113  if ($ilRuntime->shouldLogErrors()) {
114  $this->whoops->pushHandler($this->loggingHandler());
115  }
116 
117  $this->whoops->register();
118 
119  self::$whoops_handlers_registered = true;
120  }
121 
129  public function getHandler()
130  {
131  // TODO: * Use Whoops in production mode? This would require an appropriate
132  // error-handler.
133  // * Check for context? The current implementation e.g. would output HTML for
134  // for SOAP.
135 
136  if ($this->isDevmodeActive()) {
137  return $this->devmodeHandler();
138  }
139 
140  return $this->defaultHandler();
141  }
142 
143  public function getLastError()
144  {
145  return $this->error_obj;
146  }
147 
153  public function errorHandler($a_error_obj)
154  {
155  global $log;
156 
157  // see bug 18499 (some calls to raiseError do not pass a code, which leads to security issues, if these calls
158  // are done due to permission checks)
159  if ($a_error_obj->getCode() == null) {
160  $a_error_obj->code = $this->WARNING;
161  }
162 
163  $this->error_obj =&$a_error_obj;
164  //echo "-".$_SESSION["referer"]."-";
165  if ($_SESSION["failure"] && substr($a_error_obj->getMessage(), 0, 22) != "Cannot find this block") {
166  $m = "Fatal Error: Called raise error two times.<br>" .
167  "First error: " . $_SESSION["failure"] . '<br>' .
168  "Last Error:" . $a_error_obj->getMessage();
169  //return;
170  $log->write($m);
171  #$log->writeWarning($m);
172  #$log->logError($a_error_obj->getCode(), $m);
173  unset($_SESSION["failure"]);
174  die($m);
175  }
176 
177  if (substr($a_error_obj->getMessage(), 0, 22) == "Cannot find this block") {
178  if (DEVMODE == 1) {
179  echo "<b>DEVMODE</b><br><br>";
180  echo "<b>Template Block not found.</b><br>";
181  echo "You used a template block in your code that is not available.<br>";
182  echo "Native Messge: <b>" . $a_error_obj->getMessage() . "</b><br>";
183  if (is_array($a_error_obj->backtrace)) {
184  echo "Backtrace:<br>";
185  foreach ($a_error_obj->backtrace as $b) {
186  if ($b["function"] == "setCurrentBlock" &&
187  basename($b["file"]) != "class.ilTemplate.php") {
188  echo "<b>";
189  }
190  echo "File: " . $b["file"] . ", ";
191  echo "Line: " . $b["line"] . ", ";
192  echo $b["function"] . "()<br>";
193  if ($b["function"] == "setCurrentBlock" &&
194  basename($b["file"]) != "class.ilTemplate.php") {
195  echo "</b>";
196  }
197  }
198  }
199  exit;
200  }
201  return;
202  }
203 
204  if (is_object($log) and $log->enabled == true) {
205  $log->write($a_error_obj->getMessage());
206  #$log->logError($a_error_obj->getCode(),$a_error_obj->getMessage());
207  }
208 
209  //echo $a_error_obj->getCode().":"; exit;
210  if ($a_error_obj->getCode() == $this->FATAL) {
211  trigger_error(stripslashes($a_error_obj->getMessage()), E_USER_ERROR);
212  exit();
213  }
214 
215  if ($a_error_obj->getCode() == $this->WARNING) {
216  if ($this->DEBUG_ENV) {
217  $message = $a_error_obj->getMessage();
218  } else {
219  $message = "Under Construction";
220  }
221 
222  $_SESSION["failure"] = $message;
223 
224  if (!defined("ILIAS_MODULE")) {
225  ilUtil::redirect("error.php");
226  } else {
227  ilUtil::redirect("../error.php");
228  }
229  }
230 
231  if ($a_error_obj->getCode() == $this->MESSAGE) {
232  $_SESSION["failure"] = $a_error_obj->getMessage();
233  // save post vars to session in case of error
234  $_SESSION["error_post_vars"] = $_POST;
235 
236  if (empty($_SESSION["referer"])) {
237  $dirname = dirname($_SERVER["PHP_SELF"]);
238  $ilurl = parse_url(ILIAS_HTTP_PATH);
239  $subdir = substr(strstr($dirname, $ilurl["path"]), strlen($ilurl["path"]));
240  $updir = "";
241 
242  if ($subdir) {
243  $num_subdirs = substr_count($subdir, "/");
244 
245  for ($i=1;$i<=$num_subdirs;$i++) {
246  $updir .= "../";
247  }
248  }
249  ilUtil::redirect($updir . "index.php");
250  }
251 
252  /* #12104
253  check if already GET-Parameters exists in Referer-URI
254  if (substr($_SESSION["referer"],-4) == ".php")
255  {
256  $glue = "?";
257  }
258  else
259  {
260  // this did break permanent links (".html&")
261  $glue = "&";
262  }
263  */
264  ilUtil::redirect($_SESSION["referer"]);
265  }
266  }
267 
268  public function getMessage()
269  {
270  return $this->message;
271  }
272  public function setMessage($a_message)
273  {
274  $this->message = $a_message;
275  }
276  public function appendMessage($a_message)
277  {
278  if ($this->getMessage()) {
279  $this->message .= "<br /> ";
280  }
281  $this->message .= $a_message;
282  }
283 
293  public static function _ilErrorWriter($errno, $errstr, $errfile, $errline)
294  {
295  global $ilLog;
296 
297  switch ($errno) {
298  case E_USER_ERROR:
299  $ilLog->write('PHP errror: ' . $errstr . '. FATAL error on line ' . $errline . ' in file ' . $errfile);
300  unset($ilLog);
301  exit(1);
302 
303  case E_USER_WARNING:
304  $ilLog->write('PHP warning: [' . $errno . '] ' . $errstr . ' on line ' . $errline . ' in file ' . $errfile);
305  break;
306 
307  }
308  return true;
309  }
310 
315  protected function getIlRuntime()
316  {
317  return ilRuntime::getInstance();
318  }
319 
324  protected function getWhoops()
325  {
326  return new Run();
327  }
328 
333  protected function isDevmodeActive()
334  {
335  return defined("DEVMODE") && (int) DEVMODE === 1;
336  }
337 
342  protected function defaultHandler()
343  {
344  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
345  return new CallbackHandler(function ($exception, Inspector $inspector, Run $run) {
346  global $lng;
347 
348  require_once("Services/Logging/classes/error/class.ilLoggingErrorSettings.php");
349  require_once("Services/Logging/classes/error/class.ilLoggingErrorFileStorage.php");
350  require_once("Services/Utilities/classes/class.ilUtil.php");
351 
352  $session_id = substr(session_id(), 0, 5);
353  $random = new \ilRandom();
354  $err_num = $random->int(1, 9999);
355  $file_name = $session_id . "_" . $err_num;
356 
358  if (!empty($logger->folder())) {
359  $lwriter = new ilLoggingErrorFileStorage($inspector, $logger->folder(), $file_name);
360  $lwriter->write();
361  }
362 
363  //Use $lng if defined or fallback to english
364  if ($lng !== null) {
365  $lng->loadLanguageModule('logging');
366  $message = sprintf($lng->txt("log_error_message"), $file_name);
367 
368  if ($logger->mail()) {
369  $message .= " " . sprintf($lng->txt("log_error_message_send_mail"), $logger->mail(), $file_name, $logger->mail());
370  }
371  } else {
372  $message = "Error " . $file_name . " occurred.";
373 
374  if ($logger->mail()) {
375  $message .= ' ' . 'Please send a mail to <a href="mailto:' . $logger->mail() . '?subject=code: ' . $file_name . '">' . $logger->mail() . '%s</a>';
376  }
377  }
378 
380  ilUtil::redirect("error.php");
381  });
382  }
383 
388  protected function devmodeHandler()
389  {
390  global $ilLog;
391 
392  switch (ERROR_HANDLER) {
393  case "TESTING":
394  return new ilTestingHandler();
395  case "PLAIN_TEXT":
396  return new ilPlainTextHandler();
397  case "PRETTY_PAGE":
398  return new PrettyPageHandler();
399  default:
400  if ($ilLog) {
401  $ilLog->write("Unknown or undefined error handler '" . ERROR_HANDLER . "'. "
402  . "Falling back to PrettyPageHandler.");
403  }
404  return new PrettyPageHandler();
405  }
406  }
407 
412  protected function loggingHandler()
413  {
414  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
415  return new CallbackHandler(function ($exception, Inspector $inspector, Run $run) {
420  global $ilLog;
421 
422  if (is_object($ilLog)) {
423  $message = $exception->getMessage() . ' in ' . $exception->getFile() . ":" . $exception->getLine();
424  $ilLog->error($exception->getCode() . ' ' . $message);
425  }
426 
427  // Send to system logger
428  error_log($exception->getMessage());
429  });
430  }
431 
432  public function handlePreWhoops($level, $message, $file, $line)
433  {
434  global $ilLog;
435 
436  if ($level & error_reporting()) {
437 
438  // correct-with-php5-removal JL start
439  // ignore all E_STRICT that are E_NOTICE (or nothing at all) in PHP7
440  if (version_compare(PHP_VERSION, '7.0.0', '<')) {
441  if ($level == E_STRICT) {
442  if (!stristr($message, "should be compatible") &&
443  !stristr($message, "should not be called statically") &&
444  !stristr($message, "should not be abstract")) {
445  return true;
446  };
447  }
448  }
449  // correct-with-php5-removal end
450 
451  if (!$this->isDevmodeActive()) {
452  // log E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED only
453  if ($level >= E_USER_NOTICE) {
454  if ($ilLog) {
455  $severity = Whoops\Util\Misc::TranslateErrorCode($level);
456  $ilLog->write("\n\n" . $severity . " - " . $message . "\n" . $file . " - line " . $line . "\n");
457  }
458  return true;
459  }
460  }
461 
462  // trigger whoops error handling
463  if ($this->whoops) {
464  return $this->whoops->handleError($level, $message, $file, $line);
465  }
466  }
467 
468  return false;
469  }
470 } // 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"]
Saves error informations into file.
defaultHandler()
Get a default error handler.
getIlRuntime()
Get ilRuntime.
catch(Exception $e) $message
getHandler()
Get a handler for an error or exception.
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!!!
Create styles array
The data for the language used.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
handlePreWhoops($level, $message, $file, $line)
initWhoopsHandlers()
Initialize Error and Exception Handlers.
global $lng
Definition: privfeed.php:17
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.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
defined( 'APPLICATION_ENV')||define( 'APPLICATION_ENV'
Definition: bootstrap.php:27
static redirect($a_script)
$_POST["username"]