ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 
5 require_once 'Services/Environment/classes/class.ilRuntime.php';
6 
19 include_once 'PEAR.php';
20 
21 // TODO: This would clearly benefit from Autoloading...
22 require_once("./Services/Exceptions/lib/Whoops/Run.php");
23 require_once("./Services/Exceptions/lib/Whoops/Handler/HandlerInterface.php");
24 require_once("./Services/Exceptions/lib/Whoops/Handler/Handler.php");
25 require_once("./Services/Exceptions/lib/Whoops/Handler/CallbackHandler.php");
26 require_once("./Services/Exceptions/lib/Whoops/Handler/PrettyPageHandler.php");
27 require_once("./Services/Exceptions/lib/Whoops/Exception/Inspector.php");
28 require_once("./Services/Exceptions/lib/Whoops/Exception/ErrorException.php");
29 require_once("./Services/Exceptions/lib/Whoops/Exception/FrameCollection.php");
30 require_once("./Services/Exceptions/lib/Whoops/Exception/Frame.php");
31 require_once("./Services/Exceptions/lib/Whoops/Exception/Inspector.php");
32 require_once("./Services/Exceptions/lib/Whoops/Exception/Formatter.php");
33 require_once("./Services/Exceptions/lib/Whoops/Util/TemplateHelper.php");
34 require_once("./Services/Exceptions/lib/Whoops/Util/Misc.php");
35 
36 require_once("Services/Exceptions/classes/class.ilDelegatingHandler.php");
37 require_once("Services/Exceptions/classes/class.ilPlainTextHandler.php");
38 require_once("Services/Exceptions/classes/class.ilTestingHandler.php");
39 
40 use Whoops\Run;
44 
45 class ilErrorHandling extends PEAR
46 {
53 
59  var $FATAL;
60 
66  var $WARNING;
67 
73  var $MESSAGE;
74 
79  protected static $handlers_registered = false;
80 
85  function ilErrorHandling()
86  {
87  $this->PEAR();
88 
89  // init vars
90  $this->DEBUG_ENV = true;
91  $this->FATAL = 1;
92  $this->WARNING = 2;
93  $this->MESSAGE = 3;
94 
95  $this->error_obj = false;
96 
97  $this->initHandlers();
98  }
99 
108  protected function initHandlers() {
109  if (self::$handlers_registered) {
110  // Only register whoops error handlers once.
111  return;
112  }
113 
114  $ilRuntime = $this->getIlRuntime();
115  $whoops = $this->getWhoops();
116 
117  $whoops->pushHandler(new ilDelegatingHandler($this));
118 
119  if ($ilRuntime->shouldLogErrors()) {
120  $whoops->pushHandler($this->loggingHandler());
121  }
122 
123  $whoops->register();
124 
125  self::$handlers_registered = true;
126  }
127 
135  public function getHandler() {
136  // TODO: * Use Whoops in production mode? This would require an appropriate
137  // error-handler.
138  // * Check for context? The current implementation e.g. would output HTML for
139  // for SOAP.
140 
141  if ($this->isDevmodeActive()) {
142  return $this->devmodeHandler();
143  }
144 
145  return $this->defaultHandler();
146  }
147 
148  function getLastError()
149  {
150  return $this->error_obj;
151  }
152 
158  function errorHandler($a_error_obj)
159  {
160  global $log;
161 
162  // see bug 18499 (some calls to raiseError do not pass a code, which leads to security issues, if these calls
163  // are done due to permission checks)
164  if ($a_error_obj->getCode() == null)
165  {
166  $a_error_obj->code = $this->WARNING;
167  }
168 
169  $this->error_obj =& $a_error_obj;
170 //echo "-".$_SESSION["referer"]."-";
171  if ($_SESSION["failure"] && substr($a_error_obj->getMessage(), 0, 22) != "Cannot find this block")
172  {
173  $m = "Fatal Error: Called raise error two times.<br>".
174  "First error: ".$_SESSION["failure"].'<br>'.
175  "Last Error:". $a_error_obj->getMessage();
176  //return;
177  $log->write($m);
178  #$log->writeWarning($m);
179  #$log->logError($a_error_obj->getCode(), $m);
180  unset($_SESSION["failure"]);
181  die ($m);
182  }
183 
184  if (substr($a_error_obj->getMessage(), 0, 22) == "Cannot find this block")
185  {
186  if (DEVMODE == 1)
187  {
188  echo "<b>DEVMODE</b><br><br>";
189  echo "<b>Template Block not found.</b><br>";
190  echo "You used a template block in your code that is not available.<br>";
191  echo "Native Messge: <b>".$a_error_obj->getMessage()."</b><br>";
192  if (is_array($a_error_obj->backtrace))
193  {
194  echo "Backtrace:<br>";
195  foreach ($a_error_obj->backtrace as $b)
196  {
197  if ($b["function"] == "setCurrentBlock" &&
198  basename($b["file"]) != "class.ilTemplate.php")
199  {
200  echo "<b>";
201  }
202  echo "File: ".$b["file"].", ";
203  echo "Line: ".$b["line"].", ";
204  echo $b["function"]."()<br>";
205  if ($b["function"] == "setCurrentBlock" &&
206  basename($b["file"]) != "class.ilTemplate.php")
207  {
208  echo "</b>";
209  }
210  }
211  }
212  exit;
213  }
214  return;
215  }
216 
217  if (is_object($log) and $log->enabled == true)
218  {
219  $log->write($a_error_obj->getMessage());
220  #$log->logError($a_error_obj->getCode(),$a_error_obj->getMessage());
221  }
222 
223 //echo $a_error_obj->getCode().":"; exit;
224  if ($a_error_obj->getCode() == $this->FATAL)
225  {
226  trigger_error(stripslashes($a_error_obj->getMessage()), E_USER_ERROR);
227  exit();
228  }
229 
230  if ($a_error_obj->getCode() == $this->WARNING)
231  {
232  if ($this->DEBUG_ENV)
233  {
234  $message = $a_error_obj->getMessage();
235  }
236  else
237  {
238  $message = "Under Construction";
239  }
240 
241  $_SESSION["failure"] = $message;
242 
243  if (!defined("ILIAS_MODULE"))
244  {
245  ilUtil::redirect("error.php");
246  }
247  else
248  {
249  ilUtil::redirect("../error.php");
250  }
251  }
252 
253  if ($a_error_obj->getCode() == $this->MESSAGE)
254  {
255  $_SESSION["failure"] = $a_error_obj->getMessage();
256  // save post vars to session in case of error
257  $_SESSION["error_post_vars"] = $_POST;
258 
259  if (empty($_SESSION["referer"]))
260  {
261  $dirname = dirname($_SERVER["PHP_SELF"]);
262  $ilurl = parse_url(ILIAS_HTTP_PATH);
263  $subdir = substr(strstr($dirname,$ilurl["path"]),strlen($ilurl["path"]));
264  $updir = "";
265 
266  if ($subdir)
267  {
268  $num_subdirs = substr_count($subdir,"/");
269 
270  for ($i=1;$i<=$num_subdirs;$i++)
271  {
272  $updir .= "../";
273  }
274  }
275  ilUtil::redirect($updir."index.php");
276  }
277 
278  /* #12104
279  check if already GET-Parameters exists in Referer-URI
280  if (substr($_SESSION["referer"],-4) == ".php")
281  {
282  $glue = "?";
283  }
284  else
285  {
286  // this did break permanent links (".html&")
287  $glue = "&";
288  }
289  */
290  ilUtil::redirect($_SESSION["referer"]);
291  }
292  }
293 
294  function getMessage()
295  {
296  return $this->message;
297  }
298  function setMessage($a_message)
299  {
300  $this->message = $a_message;
301  }
302  function appendMessage($a_message)
303  {
304  if($this->getMessage())
305  {
306  $this->message .= "<br /> ";
307  }
308  $this->message .= $a_message;
309  }
310 
320  public static function _ilErrorWriter($errno, $errstr, $errfile, $errline)
321  {
322  global $ilLog;
323 
324  switch($errno)
325  {
326  case E_USER_ERROR:
327  $ilLog->write('PHP errror: '.$errstr.'. FATAL error on line '.$errline.' in file '.$errfile);
328  unset($ilLog);
329  exit(1);
330 
331  case E_USER_WARNING:
332  $ilLog->write('PHP warning: ['.$errno.'] '.$errstr.' on line '.$errline.' in file '.$errfile);
333  break;
334 
335  }
336  return true;
337  }
338 
343  protected function getIlRuntime() {
344  return ilRuntime::getInstance();
345  }
346 
351  protected function getWhoops() {
352  return new Run();
353  }
354 
359  protected function isDevmodeActive() {
360  return DEVMODE == 1;
361  }
362 
367  protected function defaultHandler() {
368  return new CallbackHandler(function(Exception $exception, Inspector $inspector, Run $run) {
369  if ($exception instanceof \Whoops\Exception\ErrorException
370  and $exception->getCode() == E_ERROR) {
371  global $tpl, $lng, $tree;
372  $_SESSION["failure"] = $exception->getMessage();
373  include("error.php");
374  exit();
375  }
376 
377  require_once("Services/Utilities/classes/class.ilUtil.php");
378  ilUtil::sendFailure($exception->getMessage(), true);
379  // many test installation have display_errors on, we do not need additional information in this case
380  // however in cases of warnings, whoops seems to get rid of these messages, but not in the case of fatals
381  // so we do not check for ini_get("display_errors"), but for headers_sent()
382  if (!headers_sent())
383  {
384  // #0019268, when not in setup
385  if (class_exists("ilInitialisation"))
386  {
387  ilInitialisation::initHTML();
388  global $tpl, $lng, $tree;
389  include("error.php"); // redirect will not display fatal error messages, since writing to session (sendFailure) will not work at this point
390  }
391  else // when in setup...
392  {
393  ilUtil::redirect("error.php");
394  }
395  }
396  });
397  }
398 
403  protected function devmodeHandler() {
404  global $ilLog;
405 
406  switch (ERROR_HANDLER) {
407  case "TESTING":
408  return new ilTestingHandler();
409  case "PLAIN_TEXT":
410  return new ilPlainTextHandler();
411  case "PRETTY_PAGE":
412  return new PrettyPageHandler();
413  default:
414  if ($ilLog) {
415  $ilLog->write("Unknown or undefined error handler '".ERROR_HANDLER."'. "
416  ."Falling back to PrettyPageHandler.");
417  }
418  return new PrettyPageHandler();
419  }
420  }
421 
426  protected function loggingHandler() {
427  // TODO: remove this, when PHP 5.3 support is dropped. Make logMessageFor protected then as well.
428  $self = $this;
429  return new CallbackHandler(function(Exception $exception, Inspector $inspector, Run $run) use ($self) {
434  global $ilLog;
435 
436  $log_message = $self->logMessageFor($exception, (bool)LOG_ERROR_TRACE);
437  if(is_object($ilLog)) {
438  // ak: default log level of write() is INFO, which is not appropriate -> set this to warning
439  // include_once './Services/Logging/classes/public/class.ilLogLevel.php'; // include may fail, see 19837 (maybe due to temp changed dir)
440  $ilLog->write($log_message, 300);
441  }
442 
443  // Send to system logger
444  error_log($log_message);
445  });
446  }
447 
457  public function logMessageFor(Exception $exception, $log_trace) {
458  assert('is_bool($log_trace)');
459  $prefix = "PHP Error: ";
460  if ($exception instanceof \Whoops\Exception\ErrorException) {
461  switch ($exception->getCode()) {
462  case E_ERROR:
463  case E_USER_ERROR:
464  $prefix = "PHP Fatal error: ";
465  }
466  }
467 
468  $msg = $prefix.$exception->getMessage()." in ".$exception->getFile()." on line ".$exception->getLine();
469 
470  if ($log_trace) {
471  $msg .= "\n".$exception->getTraceAsString();
472  }
473 
474  return $msg;
475  }
476 
477 } // END class.ilErrorHandling
478 ?>
logMessageFor(Exception $exception, $log_trace)
Get the error message to be logged.
isDevmodeActive()
Is the DEVMODE switched on?
initHandlers()
Initialize Error and Exception Handlers.
getWhoops()
Get an instance of Whoops/Run.
exit
Definition: login.php:54
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$_POST['username']
Definition: cron.php:12
ilErrorHandling()
Constructor public.
static getInstance()
$_SESSION["AccountId"]
global $tpl
Definition: ilias.php:8
defaultHandler()
Get a default error handler.
getIlRuntime()
Get ilRuntime.
PEAR($error_class=null)
Constructor.
Definition: PEAR.php:170
getHandler()
Get a handler for an error or exception.
Whoops - php errors for cool kids.
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.
global $lng
Definition: privfeed.php:40
devmodeHandler()
Get the handler to be used in DEVMODE.
A Whoops error handler for testing.
Wrapper for Closures passed as handlers.
static redirect($a_script)
http redirect to other script