ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Inspector.php
Go to the documentation of this file.
1 <?php
7 namespace Whoops\Exception;
8 
10 
11 class Inspector
12 {
16  private $exception;
17 
21  private $frames;
22 
27 
31  public function __construct($exception)
32  {
33  $this->exception = $exception;
34  }
35 
39  public function getException()
40  {
41  return $this->exception;
42  }
43 
47  public function getExceptionName()
48  {
49  return get_class($this->exception);
50  }
51 
55  public function getExceptionMessage()
56  {
57  return $this->extractDocrefUrl($this->exception->getMessage())['message'];
58  }
59 
65  public function getExceptionDocrefUrl()
66  {
67  return $this->extractDocrefUrl($this->exception->getMessage())['url'];
68  }
69 
70  private function extractDocrefUrl($message)
71  {
72  $docref = [
73  'message' => $message,
74  'url' => null,
75  ];
76 
77  // php embbeds urls to the manual into the Exception message with the following ini-settings defined
78  // http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
79  if (!ini_get('html_errors') || !ini_get('docref_root')) {
80  return $docref;
81  }
82 
83  $pattern = "/\[<a href='([^']+)'>(?:[^<]+)<\/a>\]/";
84  if (preg_match($pattern, $message, $matches)) {
85  // -> strip those automatically generated links from the exception message
86  $docref['message'] = preg_replace($pattern, '', $message, 1);
87  $docref['url'] = $matches[1];
88  }
89 
90  return $docref;
91  }
92 
97  public function hasPreviousException()
98  {
99  return $this->previousExceptionInspector || $this->exception->getPrevious();
100  }
101 
108  {
109  if ($this->previousExceptionInspector === null) {
110  $previousException = $this->exception->getPrevious();
111 
112  if ($previousException) {
113  $this->previousExceptionInspector = new Inspector($previousException);
114  }
115  }
116 
118  }
119 
125  public function getFrames()
126  {
127  if ($this->frames === null) {
128  $frames = $this->getTrace($this->exception);
129 
130  // Fill empty line/file info for call_user_func_array usages (PHP Bug #44428)
131  foreach ($frames as $k => $frame) {
132  if (empty($frame['file'])) {
133  // Default values when file and line are missing
134  $file = '[internal]';
135  $line = 0;
136 
137  $next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1] : [];
138 
139  if ($this->isValidNextFrame($next_frame)) {
140  $file = $next_frame['file'];
141  $line = $next_frame['line'];
142  }
143 
144  $frames[$k]['file'] = $file;
145  $frames[$k]['line'] = $line;
146  }
147  }
148 
149  // Find latest non-error handling frame index ($i) used to remove error handling frames
150  $i = 0;
151  foreach ($frames as $k => $frame) {
152  if ($frame['file'] == $this->exception->getFile() && $frame['line'] == $this->exception->getLine()) {
153  $i = $k;
154  }
155  }
156 
157  // Remove error handling frames
158  if ($i > 0) {
159  array_splice($frames, 0, $i);
160  }
161 
162  $firstFrame = $this->getFrameFromException($this->exception);
163  array_unshift($frames, $firstFrame);
164 
165  $this->frames = new FrameCollection($frames);
166 
167  if ($previousInspector = $this->getPreviousExceptionInspector()) {
168  // Keep outer frame on top of the inner one
169  $outerFrames = $this->frames;
170  $newFrames = clone $previousInspector->getFrames();
171  // I assume it will always be set, but let's be safe
172  if (isset($newFrames[0])) {
173  $newFrames[0]->addComment(
174  $previousInspector->getExceptionMessage(),
175  'Exception message:'
176  );
177  }
178  $newFrames->prependFrames($outerFrames->topDiff($newFrames));
179  $this->frames = $newFrames;
180  }
181  }
182 
183  return $this->frames;
184  }
185 
194  protected function getTrace($e)
195  {
196  $traces = $e->getTrace();
197 
198  // Get trace from xdebug if enabled, failure exceptions only trace to the shutdown handler by default
199  if (!$e instanceof \ErrorException) {
200  return $traces;
201  }
202 
203  if (!Misc::isLevelFatal($e->getSeverity())) {
204  return $traces;
205  }
206 
207  if (!extension_loaded('xdebug') || !xdebug_is_enabled()) {
208  return [];
209  }
210 
211  // Use xdebug to get the full stack trace and remove the shutdown handler stack trace
212  $stack = array_reverse(xdebug_get_function_stack());
213  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
214  $traces = array_diff_key($stack, $trace);
215 
216  return $traces;
217  }
218 
225  protected function getFrameFromException($exception)
226  {
227  return [
228  'file' => $exception->getFile(),
229  'line' => $exception->getLine(),
230  'class' => get_class($exception),
231  'args' => [
232  $exception->getMessage(),
233  ],
234  ];
235  }
236 
244  {
245  return [
246  'file' => $exception->getFile(),
247  'line' => $exception->getLine(),
248  'class' => null,
249  'args' => [],
250  ];
251  }
252 
260  protected function isValidNextFrame(array $frame)
261  {
262  if (empty($frame['file'])) {
263  return false;
264  }
265 
266  if (empty($frame['line'])) {
267  return false;
268  }
269 
270  if (empty($frame['function']) || !stristr($frame['function'], 'call_user_func')) {
271  return false;
272  }
273 
274  return true;
275  }
276 }
getFrameFromException($exception)
Given an exception, generates an array in the format generated by Exception::getTrace() ...
Definition: Inspector.php:225
isValidNextFrame(array $frame)
Determine if the frame can be used to fill in previous frame&#39;s missing info happens for call_user_fun...
Definition: Inspector.php:260
getPreviousExceptionInspector()
Returns an Inspector for a previous Exception, if any.
Definition: Inspector.php:107
getFrameFromError(ErrorException $exception)
Given an error, generates an array in the format generated by ErrorException.
Definition: Inspector.php:243
getFrames()
Returns an iterator for the inspected exception&#39;s frames.
Definition: Inspector.php:125
hasPreviousException()
Does the wrapped Exception has a previous Exception?
Definition: Inspector.php:97
Exposes a fluent interface for dealing with an ordered list of stack-trace frames.
getExceptionDocrefUrl()
Returns a url to the php-manual related to the underlying error - when available. ...
Definition: Inspector.php:65
Whoops - php errors for cool kids.
Create styles array
The data for the language used.
getTrace($e)
Gets the backtrace from an exception.
Definition: Inspector.php:194
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
Wraps ErrorException; mostly used for typing (at least now) to easily cleanup the stack trace of redu...
static isLevelFatal($level)
Determine if an error level is fatal (halts execution)
Definition: Misc.php:67