trace = $trace; return $this; } public function render() { $user = $this->getUser(); $trace = $this->trace; $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; $callsigns = array( 'arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P', ); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'].'::'; } if (isset($part['function'])) { $where .= $part['function'].'()'; } if ($file) { if (isset($callsigns[$lib])) { $attrs = array('title' => $file); try { $attrs['href'] = $user->loadEditorLink( '/src/'.$relative, $part['line'], $callsigns[$lib]); } catch (Exception $ex) { // The database can be inaccessible. } if (empty($attrs['href'])) { $attrs['href'] = sprintf($path, $callsigns[$lib]). str_replace(DIRECTORY_SEPARATOR, '/', $relative). '$'.$part['line']; $attrs['target'] = '_blank'; } $file_name = phutil_tag( 'a', $attrs, $relative); } else { $file_name = phutil_tag( 'span', array( 'title' => $file, ), $relative); } $file_name = hsprintf('%s : %d', $file_name, $part['line']); } else { $file_name = phutil_tag('em', array(), '(Internal)'); } $rows[] = array( $depth--, $lib, $file_name, $where, ); } $table = new AphrontTableView($rows); $table->setHeaders( array( 'Depth', 'Library', 'File', 'Where', )); $table->setColumnClasses( array( 'n', '', '', 'wide', )); return phutil_tag( 'div', array('class' => 'exception-trace'), array( phutil_tag( 'div', array('class' => 'exception-trace-header'), pht('Stack Trace')), $table->render(), )); } }