ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 {
42 
48  var $FATAL;
49 
55  var $WARNING;
56 
62  var $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  if (self::$whoops_handlers_registered) {
103  // Only register whoops error handlers once.
104  return;
105  }
106 
107  $ilRuntime = $this->getIlRuntime();
108  $this->whoops = $this->getWhoops();
109 
110  $this->whoops->pushHandler(new ilDelegatingHandler($this));
111 
112  if ($ilRuntime->shouldLogErrors()) {
113  $this->whoops->pushHandler($this->loggingHandler());
114  }
115 
116  $this->whoops->register();
117 
118  self::$whoops_handlers_registered = true;
119  }
120 
128  public function getHandler() {
129  // TODO: * Use Whoops in production mode? This would require an appropriate
130  // error-handler.
131  // * Check for context? The current implementation e.g. would output HTML for
132  // for SOAP.
133 
134  if ($this->isDevmodeActive()) {
135  return $this->devmodeHandler();
136  }
137 
138  return $this->defaultHandler();
139  }
140 
141  function getLastError()
142  {
143  return $this->error_obj;
144  }
145 
151  function errorHandler($a_error_obj)
152  {
153  global $log;
154 
155  // see bug 18499 (some calls to raiseError do not pass a code, which leads to security issues, if these calls
156  // are done due to permission checks)
157  if ($a_error_obj->getCode() == null)
158  {
159  $a_error_obj->code = $this->WARNING;
160  }
161 
162  $this->error_obj =& $a_error_obj;
163 //echo "-".$_SESSION["referer"]."-";
164  if ($_SESSION["failure"] && substr($a_error_obj->getMessage(), 0, 22) != "Cannot find this block")
165  {
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  {
179  if (DEVMODE == 1)
180  {
181  echo "<b>DEVMODE</b><br><br>";
182  echo "<b>Template Block not found.</b><br>";
183  echo "You used a template block in your code that is not available.<br>";
184  echo "Native Messge: <b>".$a_error_obj->getMessage()."</b><br>";
185  if (is_array($a_error_obj->backtrace))
186  {
187  echo "Backtrace:<br>";
188  foreach ($a_error_obj->backtrace as $b)
189  {
190  if ($b["function"] == "setCurrentBlock" &&
191  basename($b["file"]) != "class.ilTemplate.php")
192  {
193  echo "<b>";
194  }
195  echo "File: ".$b["file"].", ";
196  echo "Line: ".$b["line"].", ";
197  echo $b["function"]."()<br>";
198  if ($b["function"] == "setCurrentBlock" &&
199  basename($b["file"]) != "class.ilTemplate.php")
200  {
201  echo "</b>";
202  }
203  }
204  }
205  exit;
206  }
207  return;
208  }
209 
210  if (is_object($log) and $log->enabled == true)
211  {
212  $log->write($a_error_obj->getMessage());
213  #$log->logError($a_error_obj->getCode(),$a_error_obj->getMessage());
214  }
215 
216 //echo $a_error_obj->getCode().":"; exit;
217  if ($a_error_obj->getCode() == $this->FATAL)
218  {
219  trigger_error(stripslashes($a_error_obj->getMessage()), E_USER_ERROR);
220  exit();
221  }
222 
223  if ($a_error_obj->getCode() == $this->WARNING)
224  {
225  if ($this->DEBUG_ENV)
226  {
227  $message = $a_error_obj->getMessage();
228  }
229  else
230  {
231  $message = "Under Construction";
232  }
233 
234  $_SESSION["failure"] = $message;
235 
236  if (!defined("ILIAS_MODULE"))
237  {
238  ilUtil::redirect("error.php");
239  }
240  else
241  {
242  ilUtil::redirect("../error.php");
243  }
244  }
245 
246  if ($a_error_obj->getCode() == $this->MESSAGE)
247  {
248  $_SESSION["failure"] = $a_error_obj->getMessage();
249  // save post vars to session in case of error
250  $_SESSION["error_post_vars"] = $_POST;
251 
252  if (empty($_SESSION["referer"]))
253  {
254  $dirname = dirname($_SERVER["PHP_SELF"]);
255  $ilurl = parse_url(ILIAS_HTTP_PATH);
256  $subdir = substr(strstr($dirname,$ilurl["path"]),strlen($ilurl["path"]));
257  $updir = "";
258 
259  if ($subdir)
260  {
261  $num_subdirs = substr_count($subdir,"/");
262 
263  for ($i=1;$i<=$num_subdirs;$i++)
264  {
265  $updir .= "../";
266  }
267  }
268  ilUtil::redirect($updir."index.php");
269  }
270 
271  /* #12104
272  check if already GET-Parameters exists in Referer-URI
273  if (substr($_SESSION["referer"],-4) == ".php")
274  {
275  $glue = "?";
276  }
277  else
278  {
279  // this did break permanent links (".html&")
280  $glue = "&";
281  }
282  */
283  ilUtil::redirect($_SESSION["referer"]);
284  }
285  }
286 
287  function getMessage()
288  {
289  return $this->message;
290  }
291  function setMessage($a_message)
292  {
293  $this->message = $a_message;
294  }
295  function appendMessage($a_message)
296  {
297  if($this->getMessage())
298  {
299  $this->message .= "<br /> ";
300  }
301  $this->message .= $a_message;
302  }
303 
313  public static function _ilErrorWriter($errno, $errstr, $errfile, $errline)
314  {
315  global $ilLog;
316 
317  switch($errno)
318  {
319  case E_USER_ERROR:
320  $ilLog->write('PHP errror: '.$errstr.'. FATAL error on line '.$errline.' in file '.$errfile);
321  unset($ilLog);
322  exit(1);
323 
324  case E_USER_WARNING:
325  $ilLog->write('PHP warning: ['.$errno.'] '.$errstr.' on line '.$errline.' in file '.$errfile);
326  break;
327 
328  }
329  return true;
330  }
331 
336  protected function getIlRuntime() {
337  return ilRuntime::getInstance();
338  }
339 
344  protected function getWhoops() {
345  return new Run();
346  }
347 
352  protected function isDevmodeActive() {
353  return DEVMODE == 1;
354  }
355 
360  protected function defaultHandler() {
361  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
362  return new CallbackHandler(function($exception, Inspector $inspector, Run $run) {
363  global $lng;
364 
365  require_once("Services/Logging/classes/error/class.ilLoggingErrorSettings.php");
366  require_once("Services/Logging/classes/error/class.ilLoggingErrorFileStorage.php");
367  require_once("Services/Utilities/classes/class.ilUtil.php");
368 
369  $session_id = substr(session_id(),0,5);
370  $err_num = rand(1, 9999);
371  $file_name = $session_id."_".$err_num;
372 
374  if(!empty($logger->folder())) {
375  $lwriter = new ilLoggingErrorFileStorage($inspector, $logger->folder(), $file_name);
376  $lwriter->write();
377  }
378 
379  //Use $lng if defined or fallback to english
380  if($lng !== null) {
381  $lng->loadLanguageModule('logging');
382  $message = sprintf($lng->txt("log_error_message"), $file_name);
383 
384  if($logger->mail()) {
385  $message .= " ".sprintf($lng->txt("log_error_message_send_mail"), $logger->mail(), $file_name, $logger->mail());
386  }
387  } else {
388  $message = "Error ".$file_name." occurred.";
389 
390  if($logger->mail()) {
391  $message .= ' '.'Please send a mail to <a href="mailto:'.$logger->mail().'?subject=code: '.$file_name.'">'.$logger->mail().'%s</a>';
392  }
393  }
394 
395  ilUtil::sendFailure($message, true);
396  ilUtil::redirect("error.php");
397  });
398  }
399 
404  protected function devmodeHandler() {
405  global $ilLog;
406 
407  switch (ERROR_HANDLER) {
408  case "TESTING":
409  return new ilTestingHandler();
410  case "PLAIN_TEXT":
411  return new ilPlainTextHandler();
412  case "PRETTY_PAGE":
413  return new PrettyPageHandler();
414  default:
415  if ($ilLog) {
416  $ilLog->write("Unknown or undefined error handler '".ERROR_HANDLER."'. "
417  ."Falling back to PrettyPageHandler.");
418  }
419  return new PrettyPageHandler();
420  }
421  }
422 
427  protected function loggingHandler() {
428  // php7-todo : alex, 1.3.2016: Exception -> Throwable, please check
429  return new CallbackHandler(function($exception, Inspector $inspector, Run $run) {
434  global $ilLog;
435 
436  if(is_object($ilLog)) {
437  $message = $exception->getMessage().' in '.$exception->getFile().":".$exception->getLine();
438  $ilLog->error($exception->getCode().' '.$message);
439  }
440 
441  // Send to system logger
442  error_log($exception->getMessage());
443  });
444  }
445 
446  public function handlePreWhoops($level, $message, $file, $line)
447  {
448  global $ilLog;
449 
450  if ($level & error_reporting()) {
451 
452  // correct-with-php5-removal JL start
453  // ignore all E_STRICT that are E_NOTICE (or nothing at all) in PHP7
454  if (version_compare(PHP_VERSION, '7.0.0', '<')) {
455  if ($level == E_STRICT) {
456  if (!stristr($message, "should be compatible") &&
457  !stristr($message, "should not be called statically") &&
458  !stristr($message, "should not be abstract")) {
459  return true;
460  };
461  }
462  }
463  // correct-with-php5-removal end
464 
465  if (!$this->isDevmodeActive()) {
466  // log E_USER_NOTICE, E_STRICT, E_DEPRECATED, E_USER_DEPRECATED only
467  if ($level >= E_USER_NOTICE) {
468 
469  if ($ilLog) {
470  $severity = Whoops\Util\Misc::TranslateErrorCode($level);
471  $ilLog->write("\n\n".$severity." - ".$message."\n".$file." - line ".$line."\n");
472  }
473  return true;
474  }
475  }
476 
477  // trigger whoops error handling
478  if($this->whoops)
479  {
480  return $this->whoops->handleError($level, $message, $file, $line);
481  }
482  }
483 
484  return false;
485  }
486 
487 } // 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.
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.
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)
http redirect to other script
$_POST["username"]