PluginProbe
Lasso Lite – Affiliate Link Manager & Product Displays / 157
Lasso Lite – Affiliate Link Manager & Product Displays v157
157 155 156 154 153 152 151 150 149 148 trunk 0.9.9 104 105 106 107 108 109 110 111 112 113 114 115 116 All 56 releases
simple-urls / libs / Raven / Stacktrace.php

Stacktrace.php in Lasso Lite – Affiliate Link Manager & Product Displays 157, at libs/Raven/Stacktrace.php

312 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Small helper class to inspect the stacktrace
4 *
5 * @package raven
6 */
7 class Raven_Stacktrace
8 {
9 public static $statements = array(
10 'include',
11 'include_once',
12 'require',
13 'require_once',
14 );
15
16 public static function get_stack_info($frames,
17 $trace = false,
18 $errcontext = null,
19 $frame_var_limit = Raven_Client::MESSAGE_LIMIT,
20 $strip_prefixes = null,
21 $app_path = null,
22 $excluded_app_paths = null,
23 Raven_Serializer $serializer = null,
24 Raven_ReprSerializer $reprSerializer = null)
25 {
26 $serializer = $serializer ?: new Raven_Serializer();
27 $reprSerializer = $reprSerializer ?: new Raven_ReprSerializer();
28
29 /**
30 * PHP stores calls in the stacktrace, rather than executing context. Sentry
31 * wants to know "when Im calling this code, where am I", and PHP says "I'm
32 * calling this function" not "I'm in this function". Due to that, we shift
33 * the context for a frame up one, meaning the variables (which are the calling
34 * args) come from the previous frame.
35 */
36 $result = array();
37 for ($i = 0; $i < count($frames); $i++) {
38 $frame = isset($frames[$i]) ? $frames[$i] : array();
39 $nextframe = isset($frames[$i + 1]) ? $frames[$i + 1] : array();
40
41 if (!array_key_exists('file', $frame)) {
42 $context = array();
43
44 if (!empty($frame['class'])) {
45 $context['line'] = sprintf('%s%s%s', $frame['class'], $frame['type'], $frame['function']);
46
47 try {
48 $reflect = new ReflectionClass($frame['class']);
49 $context['filename'] = $filename = $reflect->getFileName();
50 } catch (ReflectionException $e) {
51 // Forget it if we run into errors, it's not worth it.
52 }
53 } elseif (!empty($frame['function'])) {
54 $context['line'] = sprintf('%s(anonymous)', $frame['function']);
55 } else {
56 $context['line'] = sprintf('(anonymous)');
57 }
58
59 if (empty($context['filename'])) {
60 $context['filename'] = $filename = '[Anonymous function]';
61 }
62
63 $abs_path = '';
64 $context['prefix'] = '';
65 $context['suffix'] = '';
66 $context['lineno'] = 0;
67 } else {
68 $context = self::read_source_file($frame['file'], $frame['line']);
69 $abs_path = $frame['file'];
70 }
71
72 // strip base path if present
73 $context['filename'] = self::strip_prefixes($context['filename'], $strip_prefixes);
74 if ($i === 0 && isset($errcontext)) {
75 // If we've been given an error context that can be used as the vars for the first frame.
76 $vars = $errcontext;
77 } else {
78 if ($trace) {
79 $vars = self::get_frame_context($nextframe, $frame_var_limit);
80 } else {
81 $vars = array();
82 }
83 }
84
85 $data = array(
86 'filename' => $context['filename'],
87 'lineno' => (int) $context['lineno'],
88 'function' => isset($nextframe['function']) ? $nextframe['function'] : null,
89 'pre_context' => $serializer->serialize($context['prefix']),
90 'context_line' => $serializer->serialize($context['line']),
91 'post_context' => $serializer->serialize($context['suffix']),
92 );
93
94 // detect in_app based on app path
95 if ($app_path) {
96 $norm_abs_path = @realpath($abs_path) ?: $abs_path;
97 if (!$abs_path) {
98 $in_app = false;
99 } else {
100 $in_app = (bool)(substr($norm_abs_path, 0, strlen($app_path)) === $app_path);
101 }
102 if ($in_app && $excluded_app_paths) {
103 foreach ($excluded_app_paths as $path) {
104 if (substr($norm_abs_path, 0, strlen($path)) === $path) {
105 $in_app = false;
106 break;
107 }
108 }
109 }
110 $data['in_app'] = $in_app;
111 }
112
113 // dont set this as an empty array as PHP will treat it as a numeric array
114 // instead of a mapping which goes against the defined Sentry spec
115 if (!empty($vars)) {
116 $cleanVars = array();
117 foreach ($vars as $key => $value) {
118 $value = $reprSerializer->serialize($value);
119 if (is_string($value) || is_numeric($value)) {
120 $cleanVars[(string)$key] = substr($value, 0, $frame_var_limit);
121 } else {
122 $cleanVars[(string)$key] = $value;
123 }
124 }
125 $data['vars'] = $cleanVars;
126 }
127
128 $result[] = $data;
129 }
130
131 return array_reverse($result);
132 }
133
134 public static function get_default_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
135 {
136 if (!isset($frame['args'])) {
137 return array();
138 }
139
140 $i = 1;
141 $args = array();
142 foreach ($frame['args'] as $arg) {
143 $args['param'.$i] = self::serialize_argument($arg, $frame_arg_limit);
144 $i++;
145 }
146 return $args;
147 }
148
149 public static function get_frame_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
150 {
151 if (!isset($frame['args'])) {
152 return array();
153 }
154
155 // The reflection API seems more appropriate if we associate it with the frame
156 // where the function is actually called (since we're treating them as function context)
157 if (!isset($frame['function'])) {
158 return self::get_default_context($frame, $frame_arg_limit);
159 }
160 if (strpos($frame['function'], '__lambda_func') !== false) {
161 return self::get_default_context($frame, $frame_arg_limit);
162 }
163 if (isset($frame['class']) && $frame['class'] == 'Closure') {
164 return self::get_default_context($frame, $frame_arg_limit);
165 }
166 if (strpos($frame['function'], '{closure}') !== false) {
167 return self::get_default_context($frame, $frame_arg_limit);
168 }
169 if (in_array($frame['function'], self::$statements)) {
170 if (empty($frame['args'])) {
171 // No arguments
172 return array();
173 } else {
174 // Sanitize the file path
175 return array(
176 'param1' => self::serialize_argument($frame['args'][0], $frame_arg_limit),
177 );
178 }
179 }
180 try {
181 if (isset($frame['class'])) {
182 if (method_exists($frame['class'], $frame['function'])) {
183 $reflection = new ReflectionMethod($frame['class'], $frame['function']);
184 } elseif ($frame['type'] === '::') {
185 $reflection = new ReflectionMethod($frame['class'], '__callStatic');
186 } else {
187 $reflection = new ReflectionMethod($frame['class'], '__call');
188 }
189 } elseif (function_exists($frame['function'])) {
190 $reflection = new ReflectionFunction($frame['function']);
191 } else {
192 return self::get_default_context($frame, $frame_arg_limit);
193 }
194 } catch (ReflectionException $e) {
195 return self::get_default_context($frame, $frame_arg_limit);
196 }
197
198 $params = $reflection->getParameters();
199
200 $args = array();
201 foreach ($frame['args'] as $i => $arg) {
202 $arg = self::serialize_argument($arg, $frame_arg_limit);
203 if (isset($params[$i])) {
204 // Assign the argument by the parameter name
205 $args[$params[$i]->name] = $arg;
206 } else {
207 $args['param'.$i] = $arg;
208 }
209 }
210
211 return $args;
212 }
213
214 private static function serialize_argument($arg, $frame_arg_limit)
215 {
216 if (is_array($arg)) {
217 $_arg = array();
218 foreach ($arg as $key => $value) {
219 if (is_string($value) || is_numeric($value)) {
220 $_arg[$key] = substr($value, 0, $frame_arg_limit);
221 } else {
222 $_arg[$key] = $value;
223 }
224 }
225 return $_arg;
226 } elseif (is_string($arg) || is_numeric($arg)) {
227 return substr($arg, 0, $frame_arg_limit);
228 } else {
229 return $arg;
230 }
231 }
232
233 private static function strip_prefixes($filename, $prefixes)
234 {
235 if ($prefixes === null) {
236 return $filename;
237 }
238 foreach ($prefixes as $prefix) {
239 if (substr($filename, 0, strlen($prefix)) === $prefix) {
240 return substr($filename, strlen($prefix));
241 }
242 }
243 return $filename;
244 }
245
246 private static function read_source_file($filename, $lineno, $context_lines = 5)
247 {
248 $frame = array(
249 'prefix' => array(),
250 'line' => '',
251 'suffix' => array(),
252 'filename' => $filename,
253 'lineno' => $lineno,
254 );
255
256 if ($filename === null || $lineno === null) {
257 return $frame;
258 }
259
260 // Code which is eval'ed have a modified filename.. Extract the
261 // correct filename + linenumber from the string.
262 $matches = array();
263 $matched = preg_match("/^(.*?)\\((\\d+)\\) : eval\\(\\)'d code$/",
264 $filename, $matches);
265 if ($matched) {
266 $frame['filename'] = $filename = $matches[1];
267 $frame['lineno'] = $lineno = $matches[2];
268 }
269
270 // In the case of an anonymous function, the filename is sent as:
271 // "</path/to/filename>(<lineno>) : runtime-created function"
272 // Extract the correct filename + linenumber from the string.
273 $matches = array();
274 $matched = preg_match("/^(.*?)\\((\\d+)\\) : runtime-created function$/",
275 $filename, $matches);
276 if ($matched) {
277 $frame['filename'] = $filename = $matches[1];
278 $frame['lineno'] = $lineno = $matches[2];
279 }
280
281 if (!file_exists($filename)) {
282 return $frame;
283 }
284
285 try {
286 $file = new SplFileObject($filename);
287 $target = max(0, ($lineno - ($context_lines + 1)));
288 $file->seek($target);
289 $cur_lineno = $target+1;
290 while (!$file->eof()) {
291 $line = rtrim($file->current(), "\r\n");
292 if ($cur_lineno == $lineno) {
293 $frame['line'] = $line;
294 } elseif ($cur_lineno < $lineno) {
295 $frame['prefix'][] = $line;
296 } elseif ($cur_lineno > $lineno) {
297 $frame['suffix'][] = $line;
298 }
299 $cur_lineno++;
300 if ($cur_lineno > $lineno + $context_lines) {
301 break;
302 }
303 $file->next();
304 }
305 } catch (RuntimeException $exc) {
306 return $frame;
307 }
308
309 return $frame;
310 }
311 }
312