$abs_path, 'filename' => $context['filename'], 'lineno' => (int) $context['lineno'], 'module' => $module, 'function' => $nextframe['function'], 'pre_context' => $context['prefix'], 'context_line' => $context['line'], 'post_context' => $context['suffix'], ); // dont set this as an empty array as PHP will treat it as a numeric array // instead of a mapping which goes against the defined Sentry spec if (!empty($vars)) { $cleanVars = array(); foreach ($vars as $key => $value) { if (is_string($value) || is_numeric($value)) { $cleanVars[$key] = substr($value, 0, $frame_var_limit); } else { $cleanVars[$key] = $value; } } $data['vars'] = $cleanVars; } $result[] = $data; } return array_reverse($result); } public static function get_caller_frame_context($frame) { if (!isset($frame['args'])) { return array(); } $i = 1; $args = array(); foreach ($frame['args'] as $arg) { $args['param'.$i] = $arg; $i++; } return $args; } public static function get_frame_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT) { // The reflection API seems more appropriate if we associate it with the frame // where the function is actually called (since we're treating them as function context) if (!isset($frame['function'])) { return array(); } if (!isset($frame['args'])) { return array(); } if (strpos($frame['function'], '__lambda_func') !== false) { return array(); } if (isset($frame['class']) && $frame['class'] == 'Closure') { return array(); } if (strpos($frame['function'], '{closure}') !== false) { return array(); } if (in_array($frame['function'], self::$statements)) { if (empty($frame['args'])) { // No arguments return array(); } else { // Sanitize the file path return array($frame['args'][0]); } } try { if (isset($frame['class'])) { if (method_exists($frame['class'], $frame['function'])) { $reflection = new ReflectionMethod($frame['class'], $frame['function']); } elseif ($frame['type'] === '::') { $reflection = new ReflectionMethod($frame['class'], '__callStatic'); } else { $reflection = new ReflectionMethod($frame['class'], '__call'); } } else { $reflection = new ReflectionFunction($frame['function']); } } catch (ReflectionException $e) { return array(); } $params = $reflection->getParameters(); $args = array(); foreach ($frame['args'] as $i => $arg) { if (isset($params[$i])) { // Assign the argument by the parameter name if (is_array($arg)) { foreach ($arg as $key => $value) { if (is_string($value) || is_numeric($value)) { $arg[$key] = substr($value, 0, $frame_arg_limit); } } } $args[$params[$i]->name] = $arg; } else { // TODO: Sentry thinks of these as context locals, so they must be named // Assign the argument by number // $args[$i] = $arg; } } return $args; } private static function read_source_file($filename, $lineno, $context_lines = 5) { $frame = array( 'prefix' => array(), 'line' => '', 'suffix' => array(), 'filename' => $filename, 'lineno' => $lineno, ); if ($filename === null || $lineno === null) { return $frame; } // Code which is eval'ed have a modified filename.. Extract the // correct filename + linenumber from the string. $matches = array(); $matched = preg_match("/^(.*?)\((\d+)\) : eval\(\)'d code$/", $filename, $matches); if ($matched) { $frame['filename'] = $filename = $matches[1]; $frame['lineno'] = $lineno = $matches[2]; } // In the case of an anonymous function, the filename is sent as: // "() : runtime-created function" // Extract the correct filename + linenumber from the string. $matches = array(); $matched = preg_match("/^(.*?)\((\d+)\) : runtime-created function$/", $filename, $matches); if ($matched) { $frame['filename'] = $filename = $matches[1]; $frame['lineno'] = $lineno = $matches[2]; } try { $file = new SplFileObject($filename); $target = max(0, ($lineno - ($context_lines + 1))); $file->seek($target); $cur_lineno = $target+1; while (!$file->eof()) { $line = rtrim($file->current(), "\r\n"); if ($cur_lineno == $lineno) { $frame['line'] = $line; } elseif ($cur_lineno < $lineno) { $frame['prefix'][] = $line; } elseif ($cur_lineno > $lineno) { $frame['suffix'][] = $line; } $cur_lineno++; if ($cur_lineno > $lineno + $context_lines) { break; } $file->next(); } } catch (RuntimeException $exc) { return $frame; } return $frame; } }