1 <?php
2 3 4 5 6 7
8
9 namespace Cross\Exception;
10
11 use Cross\Http\Response;
12 use ReflectionMethod;
13 use ReflectionClass;
14 use SplFileObject;
15 use Exception;
16
17 18 19 20 21
22 abstract class CrossException extends Exception
23 {
24 25 26 27 28 29 30
31 function __construct($message = 'CrossPHP Exception', $code = null, Exception $previous = null)
32 {
33 parent::__construct($message, $code, $previous);
34 if (PHP_SAPI === 'cli') {
35 set_exception_handler(array($this, 'cliErrorHandler'));
36 } else {
37 set_exception_handler(array($this, 'errorHandler'));
38 }
39 }
40
41 42 43 44 45 46
47 function cpExceptionSource(Exception $e)
48 {
49 $file = $e->getFile();
50 $exception_line = $e->getLine();
51
52 $exception_file_source = array();
53 $exception_file_info = new SplFileObject($file);
54 foreach ($exception_file_info as $line => $code) {
55 $line += 1;
56 if ($line <= $exception_line + 6 && $line >= $exception_line - 6) {
57 $exception_file_source[$line] = self::highlightCode($code);
58 }
59 }
60
61 $result['main'] = array(
62 'file' => $file,
63 'line' => $exception_line,
64 'message' => $this->hiddenFileRealPath($e->getMessage()),
65 'show_file' => $this->hiddenFileRealPath($file),
66 'source' => $exception_file_source,
67 );
68
69 $trace = $e->getTrace();
70 $this->getTraceInfo($trace, $result['trace']);
71 if ($e->getPrevious()) {
72 $this->getTraceInfo($e->getPrevious()->getTrace(), $result['previous_trace']);
73 }
74
75 return $result;
76 }
77
78 79 80 81 82
83 function cliErrorHandler(Exception $e)
84 {
85 $trace_table = array();
86 $trace = $e->getTrace();
87 $this->getCliTraceInfo($trace, $trace_table);
88
89 $previous_trace = array();
90 if ($e->getPrevious()) {
91 $previous_trace = $e->getPrevious()->getTrace();
92 $this->getCliTraceInfo($previous_trace, $trace_table);
93 }
94
95 $result['line'] = $e->getLine();
96 $result['file'] = $e->getFile();
97 $result['message'] = $e->getMessage();
98
99 $result['trace'] = $trace;
100 $result['trace_table'] = $trace_table;
101 $result['previous_trace'] = $previous_trace;
102
103 Response::getInstance()->display($result, __DIR__ . '/tpl/cli_error.tpl.php');
104 }
105
106 107 108 109 110 111
112 protected function getTraceInfo(array $trace, &$content)
113 {
114 if (!empty($trace)) {
115 $this->alignmentTraceData($trace);
116 foreach ($trace as $tn => &$t) {
117 if (!isset($t['file'])) {
118 continue;
119 }
120
121 $i = 0;
122 $trace_file_info = new SplFileObject($t['file']);
123 foreach ($trace_file_info as $line => $code) {
124 $line += 1;
125 if (($line <= $t['end_line'] && $line >= $t['start_line']) && $i < 16) {
126 $t['source'][$line] = self::highlightCode($code);
127 $i++;
128 }
129 }
130
131 $content[] = $t;
132 }
133 }
134 }
135
136 137 138 139 140 141
142 protected function getCliTraceInfo(&$trace, &$trace_table)
143 {
144 if (!empty($trace)) {
145 $this->alignmentTraceData($trace);
146 foreach ($trace as &$t) {
147 foreach ($t as $type_name => &$trace_content) {
148 switch ($type_name) {
149 case 'file':
150 case 'line':
151 case 'function':
152 $line_max_width = max(strlen($type_name), strlen($trace_content));
153 if (($line_max_width % 2) != 0) {
154 $line_max_width += 5;
155 } else {
156 $line_max_width += 4;
157 }
158
159 if (!isset($trace_table[$type_name]) || $line_max_width > $trace_table[$type_name]) {
160 $trace_table[$type_name] = $line_max_width;
161 }
162 break;
163 default:
164 unset($t[$type_name]);
165 }
166 }
167 }
168 }
169 }
170
171 172 173 174 175 176
177 protected function hiddenFileRealPath($path)
178 {
179 return str_replace(array(PROJECT_REAL_PATH, CP_PATH, str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])),
180 array('Project->', 'Cross->', 'Index->'), $path);
181 }
182
183 184 185 186 187 188
189 private static function highlightCode($code)
190 {
191 $code = rtrim($code);
192 if (0 === strcasecmp(substr($code, 0, 5), '<?php ')) {
193 return highlight_string($code, true);
194 }
195
196 $highlight_code_fragment = highlight_string("<?php {$code}", true);
197 return str_replace('<?php', '', $highlight_code_fragment);
198 }
199
200 201 202 203 204
205 private function alignmentTraceData(array &$trace = array())
206 {
207 foreach ($trace as &$t) {
208 if (isset($t['file'])) {
209 $t['show_file'] = $this->hiddenFileRealPath($t['file']);
210 $t['start_line'] = max(1, $t['line'] - 6);
211 $t['end_line'] = $t['line'] + 6;
212 } elseif (isset($t['function']) && isset($t['class'])) {
213 try {
214 $rc = new ReflectionClass($t['class']);
215 $t['file'] = $rc->getFileName();
216 $t['show_file'] = $this->hiddenFileRealPath($rc->getFileName());
217
218 $rf = new ReflectionMethod($t['class'], $t['function']);
219 $t['start_line'] = $rf->getStartLine();
220 $t['end_line'] = $rf->getEndLine();
221 $t['line'] = sprintf("%s ~ %s", $t['start_line'], $t['end_line']);
222 } catch (Exception $e) {
223 continue;
224 }
225 } else {
226 continue;
227 }
228 }
229 }
230
231 232 233 234 235 236
237 abstract protected function errorHandler(Exception $e);
238 }
239