r3864 copy recent exceptions-related commits into trunk
[racktables] / inc / exceptions.php
1 <?php
2
3 // The default approach is to treat an error as fatal, in which case
4 // some message is output and the user is left there. Inheriting classes
5 // represent more specific cases, some of which can be handled in a
6 // "softer" way (see below).
7 class RackTablesError extends Exception
8 {
9 const INTERNAL = 2;
10 const DB_WRITE_FAILED = 3;
11 const NOT_AUTHENTICATED = 4;
12 const NOT_AUTHORIZED = 5;
13 const MISCONFIGURED = 6;
14 protected final function genHTMLPage ($title, $text)
15 {
16 header ('Content-Type: text/html; charset=UTF-8');
17 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
18 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
19 echo "<head><title>${title}</title>";
20 echo "</head><body>${text}</body></html>";
21 }
22 public function dispatch()
23 {
24 $msgheader = array
25 (
26 self::NOT_AUTHENTICATED => 'Not authenticated',
27 self::MISCONFIGURED => 'Configuration error',
28 self::INTERNAL => 'Internal error',
29 self::DB_WRITE_FAILED => 'Database write failed',
30 self::NOT_AUTHORIZED => 'Permission denied',
31 );
32 $msgbody = array
33 (
34 self::NOT_AUTHENTICATED => '<h2>This system requires authentication. You should use a username and a password.</h2>',
35 self::MISCONFIGURED => '<h2>Configuration error</h2><br>' . $this->message,
36 self::INTERNAL => '<h2>Internal error</h2><br>' . $this->message,
37 self::DB_WRITE_FAILED => '<h2>Database write failed</h2><br>' . $this->message,
38 self::NOT_AUTHORIZED => '<h2>Permission denied</h2><br>' . $this->message,
39 );
40 switch ($this->code)
41 {
42 case self::NOT_AUTHENTICATED:
43 header ('WWW-Authenticate: Basic realm="' . getConfigVar ('enterprise') . ' RackTables access"');
44 header ("HTTP/1.1 401 Unauthorized");
45 case self::MISCONFIGURED:
46 case self::INTERNAL:
47 case self::DB_WRITE_FAILED:
48 case self::NOT_AUTHORIZED:
49 $this->genHTMLPage ($msgheader[$this->code], $msgbody[$this->code]);
50 break;
51 default:
52 throw new RackTablesError ('Dispatching error, unknown code ' . $this->code, RackTablesError::INTERNAL);
53 }
54 }
55 }
56
57 // this simplifies construction of RackTablesError, but is never caught
58 class EntityNotFoundException extends RackTablesError
59 {
60 function __construct($entity, $id)
61 {
62 parent::__construct ("Object '$entity'#'$id' does not exist");
63 }
64 public function dispatch()
65 {
66 RackTablesError::genHTMLPage ('Missing record', "<h2>Missing record</h2><br>" . $this->message);
67 }
68 }
69
70 // this simplifies construction of RackTablesError, but is never caught
71 class InvalidArgException extends RackTablesError
72 {
73 function __construct ($name, $value, $reason=NULL)
74 {
75 $message = "Argument '${name}' of value '".var_export($value,true)."' is invalid.";
76 if (!is_null($reason))
77 $message .= ' ('.$reason.')';
78 parent::__construct ($message, parent::INTERNAL);
79 }
80 }
81
82 // this simplifies construction and helps in catching "soft"
83 // errors (invalid input from the user)
84 class InvalidRequestArgException extends RackTablesError
85 {
86 function __construct ($name, $value, $reason=NULL)
87 {
88 $message = "Request parameter '${name}' of value '".var_export($value,true)."' is invalid.";
89 if (!is_null($reason))
90 $message .= ' ('.$reason.')';
91 parent::__construct ($message);
92 }
93 public function dispatch()
94 {
95 RackTablesError::genHTMLPage ('Assertion failed', '<h2>Assertion failed</h2><br>' . $this->message);
96 }
97 }
98
99 // this wraps certain known PDO errors and is caught in process.php
100 // as a "soft" error
101 class RTDBConstraintError extends RackTablesError
102 {
103 public function dispatch()
104 {
105 RackTablesError::genHTMLPage ('Database constraint violation', '<h2>Constraint violation</h2><br>' . $this->message);
106 }
107 }
108
109 // gateway failure is a common case of a "soft" error, some functions do catch this
110 class RTGatewayError extends RackTablesError
111 {
112 public function dispatch()
113 {
114 RackTablesError::genHTMLPage ('Gateway error', '<h2>Gateway error</h2><br>' . $this->message);
115 }
116 }
117
118 function dumpArray($arr)
119 {
120 echo '<table class="exceptionParametersDump">';
121 foreach($arr as $key=>$value)
122 {
123 echo "<tr><th>$key</th><td>$value</td></tr>";
124 }
125 echo '</table>';
126 }
127
128 function stringTrace($trace)
129 {
130 $ret = '';
131 foreach($trace as $line) {
132 $ret .= $line['file'].':'.$line['line'].' '.$line['function'].'(';
133 $f = true;
134 if (isset($line['args']) and is_array($line['args'])) foreach ($line['args'] as $arg) {
135 if (!$f) $ret .= ', ';
136 if (is_string($arg))
137 $printarg = "'".$arg."'";
138 elseif (is_null($arg))
139 $printarg = 'NULL';
140 elseif (is_array($arg))
141 $printarg = print_r($arg, 1);
142 else
143 $printarg = $arg;
144 $ret .= $printarg;
145 $f = false;
146 }
147 $ret .= ")\n";
148 }
149 return $ret;
150 }
151
152 function printPDOException($e)
153 {
154 header("HTTP/1.1 500 Internal Server Error");
155 header ('Content-Type: text/html; charset=UTF-8');
156 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
157 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
158 echo "<head><title> PDO Exception </title>\n";
159 echo '</head> <body>';
160 echo '<h2>Pdo exception: '.get_class($e).'</h2><code>'.$e->getMessage().'</code> (<code>'.$e->getCode().'</code>)';
161 echo '<p>at file <code>'.$e->getFile().'</code>, line <code>'.$e->getLine().'</code></p><pre>';
162 echo stringTrace($e->getTrace());
163 echo '</pre>';
164 echo '<h2>Error info:</h2>';
165 echo '<pre>';
166 print_r($e->errorInfo);
167 echo '</pre>';
168 echo '<h2>Parameters:</h2>';
169 echo '<h3>GET</h3>';
170 dumpArray($_GET);
171 echo '<h3>POST</h3>';
172 dumpArray($_POST);
173 echo '<h3>COOKIE</h3>';
174 dumpArray($_COOKIE);
175 echo '</body></html>';
176
177 }
178
179 function printGenericException($e)
180 {
181 header("HTTP/1.1 500 Internal Server Error");
182 header ('Content-Type: text/html; charset=UTF-8');
183 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
184 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
185 echo "<head><title> Exception </title>\n";
186 echo "<link rel=stylesheet type='text/css' href=pi.css />\n";
187 echo "<link rel=icon href='pix/racktables.ico' type='image/x-icon' />";
188 echo '</head> <body>';
189 echo '<h2>Uncaught exception: '.get_class($e).'</h2><code>'.$e->getMessage().'</code> (<code>'.$e->getCode().'</code>)';
190 echo '<p>at file <code>'.$e->getFile().'</code>, line <code>'.$e->getLine().'</code></p><pre>';
191 echo stringTrace($e->getTrace());
192 echo '</pre>';
193 echo '<h2>Parameters:</h2>';
194 echo '<h3>GET</h3>';
195 dumpArray($_GET);
196 echo '<h3>POST</h3>';
197 dumpArray($_POST);
198 echo '<h3>COOKIE</h3>';
199 dumpArray($_COOKIE);
200 echo '</body></html>';
201
202 }
203
204 function printException($e)
205 {
206 if ($e instanceof RackTablesError)
207 $e->dispatch();
208 elseif (get_class($e) == 'PDOException')
209 printPDOException($e);
210 else
211 printGenericException($e);
212 }
213
214 ?>