ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
Inspector.php
Go to the documentation of this file.
1<?php
7namespace Whoops\Exception;
8
10
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
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}
An exception for terminatinating execution or to throw for unit testing.
Wraps ErrorException; mostly used for typing (at least now) to easily cleanup the stack trace of redu...
Exposes a fluent interface for dealing with an ordered list of stack-trace frames.
getTrace($e)
Gets the backtrace from an exception.
Definition: Inspector.php:194
getFrameFromError(ErrorException $exception)
Given an error, generates an array in the format generated by ErrorException.
Definition: Inspector.php:243
isValidNextFrame(array $frame)
Determine if the frame can be used to fill in previous frame'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
hasPreviousException()
Does the wrapped Exception has a previous Exception?
Definition: Inspector.php:97
getExceptionDocrefUrl()
Returns a url to the php-manual related to the underlying error - when available.
Definition: Inspector.php:65
getFrames()
Returns an iterator for the inspected exception's frames.
Definition: Inspector.php:125
getFrameFromException($exception)
Given an exception, generates an array in the format generated by Exception::getTrace()
Definition: Inspector.php:225
static isLevelFatal($level)
Determine if an error level is fatal (halts execution)
Definition: Misc.php:67
$i
Definition: disco.tpl.php:19
catch(Exception $e) $message
Whoops - php errors for cool kids.
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file