@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class AphrontUnhandledExceptionResponse
4 extends AphrontStandaloneHTMLResponse {
5
6 private $exception;
7 private $showStackTraces;
8
9 public function setShowStackTraces($show_stack_traces) {
10 $this->showStackTraces = $show_stack_traces;
11 return $this;
12 }
13
14 public function getShowStackTraces() {
15 return $this->showStackTraces;
16 }
17
18 public function setException($exception) {
19 // NOTE: We accept an Exception or a Throwable.
20
21 // Log the exception unless it's specifically a silent malformed request
22 // exception.
23
24 $should_log = true;
25 if ($exception instanceof AphrontMalformedRequestException) {
26 if ($exception->getIsUnlogged()) {
27 $should_log = false;
28 }
29 }
30
31 if ($should_log) {
32 phlog($exception);
33 }
34
35 $this->exception = $exception;
36 return $this;
37 }
38
39 public function getHTTPResponseCode() {
40 return 500;
41 }
42
43 protected function getResources() {
44 return array(
45 'css/application/config/config-template.css',
46 'css/application/config/unhandled-exception.css',
47 );
48 }
49
50 protected function getResponseTitle() {
51 $ex = $this->exception;
52
53 if ($ex instanceof AphrontMalformedRequestException) {
54 return $ex->getTitle();
55 } else {
56 return pht('Unhandled Exception');
57 }
58 }
59
60 protected function getResponseBodyClass() {
61 return 'unhandled-exception';
62 }
63
64 private function getExceptionList() {
65 return $this->expandException($this->exception);
66 }
67
68 private function expandException($root) {
69 if ($root instanceof PhutilAggregateException) {
70 $list = array();
71
72 $list[] = $root;
73
74 foreach ($root->getExceptions() as $ex) {
75 foreach ($this->expandException($ex) as $child) {
76 $list[] = $child;
77 }
78 }
79
80 return $list;
81 }
82
83 return array($root);
84 }
85
86 protected function getResponseBody() {
87 $body = array();
88
89 foreach ($this->getExceptionList() as $ex) {
90 $body[] = $this->newHTMLMessage($ex);
91 }
92
93 return $body;
94 }
95
96 private function newHTMLMessage($ex) {
97 if ($ex instanceof AphrontMalformedRequestException) {
98 $title = $ex->getTitle();
99 } else {
100 $title = get_class($ex);
101 }
102
103 $body = $ex->getMessage();
104 $body = phutil_escape_html_newlines($body);
105
106 $classes = array();
107 $classes[] = 'unhandled-exception-detail';
108
109 $stack = null;
110 if ($this->getShowStackTraces()) {
111 try {
112 $stack = id(new AphrontStackTraceView())
113 ->setTrace($ex->getTrace());
114
115 $stack = hsprintf('%s', $stack);
116
117 $stack = phutil_tag(
118 'div',
119 array(
120 'class' => 'unhandled-exception-stack',
121 ),
122 $stack);
123
124 $classes[] = 'unhandled-exception-with-stack';
125 } catch (Exception $trace_exception) {
126 $stack = null;
127 } catch (Throwable $trace_exception) {
128 $stack = null;
129 }
130 }
131
132 return phutil_tag(
133 'div',
134 array(
135 'class' => implode(' ', $classes),
136 ),
137 array(
138 phutil_tag(
139 'h1',
140 array(
141 'class' => 'unhandled-exception-title',
142 ),
143 $title),
144 phutil_tag(
145 'div',
146 array(
147 'class' => 'unhandled-exception-body',
148 ),
149 $body),
150 $stack,
151 ));
152 }
153
154 protected function buildPlainTextResponseString() {
155 $messages = array();
156
157 foreach ($this->getExceptionList() as $exception) {
158 $messages[] = $this->newPlainTextMessage($exception);
159 }
160
161 return implode("\n\n", $messages);
162 }
163
164 private function newPlainTextMessage($exception) {
165 return pht(
166 '%s: %s',
167 get_class($exception),
168 $exception->getMessage());
169 }
170
171}