統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / Error / ExceptionRenderer.php @ 0b1b8047

履歴 | 表示 | アノテート | ダウンロード (9.644 KB)

1
<?php
2
/**
3
 * Exception Renderer
4
 *
5
 * Provides Exception rendering features. Which allow exceptions to be rendered
6
 * as HTML pages.
7
 *
8
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
9
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
10
 *
11
 * Licensed under The MIT License
12
 * For full copyright and license information, please see the LICENSE.txt
13
 * Redistributions of files must retain the above copyright notice.
14
 *
15
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
16
 * @link          http://cakephp.org CakePHP(tm) Project
17
 * @package       Cake.Error
18
 * @since         CakePHP(tm) v 2.0
19
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
20
 */
21

    
22
App::uses('Sanitize', 'Utility');
23
App::uses('Router', 'Routing');
24
App::uses('CakeResponse', 'Network');
25
App::uses('Controller', 'Controller');
26

    
27
/**
28
 * Exception Renderer.
29
 *
30
 * Captures and handles all unhandled exceptions. Displays helpful framework errors when debug > 1.
31
 * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
32
 * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error.
33
 *
34
 * ### Implementing application specific exception rendering
35
 *
36
 * You can implement application specific exception handling in one of a few ways:
37
 *
38
 * - Create an AppController::appError();
39
 * - Create a subclass of ExceptionRenderer and configure it to be the `Exception.renderer`
40
 *
41
 * #### Using AppController::appError();
42
 *
43
 * This controller method is called instead of the default exception handling. It receives the
44
 * thrown exception as its only argument. You should implement your error handling in that method.
45
 *
46
 * #### Using a subclass of ExceptionRenderer
47
 *
48
 * Using a subclass of ExceptionRenderer gives you full control over how Exceptions are rendered, you
49
 * can configure your class in your core.php, with `Configure::write('Exception.renderer', 'MyClass');`
50
 * You should place any custom exception renderers in `app/Lib/Error`.
51
 *
52
 * @package       Cake.Error
53
 */
54
class ExceptionRenderer {
55

    
56
/**
57
 * Controller instance.
58
 *
59
 * @var Controller
60
 */
61
        public $controller = null;
62

    
63
/**
64
 * template to render for CakeException
65
 *
66
 * @var string
67
 */
68
        public $template = '';
69

    
70
/**
71
 * The method corresponding to the Exception this object is for.
72
 *
73
 * @var string
74
 */
75
        public $method = '';
76

    
77
/**
78
 * The exception being handled.
79
 *
80
 * @var Exception
81
 */
82
        public $error = null;
83

    
84
/**
85
 * Creates the controller to perform rendering on the error response.
86
 * If the error is a CakeException it will be converted to either a 400 or a 500
87
 * code error depending on the code used to construct the error.
88
 *
89
 * @param Exception $exception Exception
90
 */
91
        public function __construct(Exception $exception) {
92
                $this->controller = $this->_getController($exception);
93

    
94
                if (method_exists($this->controller, 'appError')) {
95
                        $this->controller->appError($exception);
96
                        return;
97
                }
98
                $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));
99
                $code = $exception->getCode();
100

    
101
                $methodExists = method_exists($this, $method);
102

    
103
                if ($exception instanceof CakeException && !$methodExists) {
104
                        $method = '_cakeError';
105
                        if (empty($template) || $template === 'internalError') {
106
                                $template = 'error500';
107
                        }
108
                } elseif ($exception instanceof PDOException) {
109
                        $method = 'pdoError';
110
                        $template = 'pdo_error';
111
                        $code = 500;
112
                } elseif (!$methodExists) {
113
                        $method = 'error500';
114
                        if ($code >= 400 && $code < 500) {
115
                                $method = 'error400';
116
                        }
117
                }
118

    
119
                $isNotDebug = !Configure::read('debug');
120
                if ($isNotDebug && $method === '_cakeError') {
121
                        $method = 'error400';
122
                }
123
                if ($isNotDebug && $code == 500) {
124
                        $method = 'error500';
125
                }
126
                $this->template = $template;
127
                $this->method = $method;
128
                $this->error = $exception;
129
        }
130

    
131
/**
132
 * Get the controller instance to handle the exception.
133
 * Override this method in subclasses to customize the controller used.
134
 * This method returns the built in `CakeErrorController` normally, or if an error is repeated
135
 * a bare controller will be used.
136
 *
137
 * @param Exception $exception The exception to get a controller for.
138
 * @return Controller
139
 */
140
        protected function _getController($exception) {
141
                App::uses('AppController', 'Controller');
142
                App::uses('CakeErrorController', 'Controller');
143
                if (!$request = Router::getRequest(true)) {
144
                        $request = new CakeRequest();
145
                }
146
                $response = new CakeResponse();
147

    
148
                if (method_exists($exception, 'responseHeader')) {
149
                        $response->header($exception->responseHeader());
150
                }
151

    
152
                if (class_exists('AppController')) {
153
                        try {
154
                                $controller = new CakeErrorController($request, $response);
155
                                $controller->startupProcess();
156
                                $startup = true;
157
                        } catch (Exception $e) {
158
                                $startup = false;
159
                        }
160
                        // Retry RequestHandler, as another aspect of startupProcess()
161
                        // could have failed. Ignore any exceptions out of startup, as
162
                        // there could be userland input data parsers.
163
                        if ($startup === false &&
164
                                !empty($controller) &&
165
                                $controller->Components->enabled('RequestHandler')
166
                        ) {
167
                                try {
168
                                        $controller->RequestHandler->startup($controller);
169
                                } catch (Exception $e) {
170
                                }
171
                        }
172
                }
173
                if (empty($controller)) {
174
                        $controller = new Controller($request, $response);
175
                        $controller->viewPath = 'Errors';
176
                }
177
                return $controller;
178
        }
179

    
180
/**
181
 * Renders the response for the exception.
182
 *
183
 * @return void
184
 */
185
        public function render() {
186
                if ($this->method) {
187
                        call_user_func_array(array($this, $this->method), array($this->error));
188
                }
189
        }
190

    
191
/**
192
 * Generic handler for the internal framework errors CakePHP can generate.
193
 *
194
 * @param CakeException $error The exception to render.
195
 * @return void
196
 */
197
        protected function _cakeError(CakeException $error) {
198
                $url = $this->controller->request->here();
199
                $code = ($error->getCode() >= 400 && $error->getCode() < 506) ? $error->getCode() : 500;
200
                $this->controller->response->statusCode($code);
201
                $this->controller->set(array(
202
                        'code' => $code,
203
                        'name' => h($error->getMessage()),
204
                        'message' => h($error->getMessage()),
205
                        'url' => h($url),
206
                        'error' => $error,
207
                        '_serialize' => array('code', 'name', 'message', 'url')
208
                ));
209
                $this->controller->set($error->getAttributes());
210
                $this->_outputMessage($this->template);
211
        }
212

    
213
/**
214
 * Convenience method to display a 400 series page.
215
 *
216
 * @param Exception $error The exception to render.
217
 * @return void
218
 */
219
        public function error400($error) {
220
                $message = $error->getMessage();
221
                if (!Configure::read('debug') && $error instanceof CakeException) {
222
                        $message = __d('cake', 'Not Found');
223
                }
224
                $url = $this->controller->request->here();
225
                $this->controller->response->statusCode($error->getCode());
226
                $this->controller->set(array(
227
                        'name' => h($message),
228
                        'message' => h($message),
229
                        'url' => h($url),
230
                        'error' => $error,
231
                        '_serialize' => array('name', 'message', 'url')
232
                ));
233
                $this->_outputMessage('error400');
234
        }
235

    
236
/**
237
 * Convenience method to display a 500 page.
238
 *
239
 * @param Exception $error The exception to render.
240
 * @return void
241
 */
242
        public function error500($error) {
243
                $message = $error->getMessage();
244
                if (!Configure::read('debug')) {
245
                        $message = __d('cake', 'An Internal Error Has Occurred.');
246
                }
247
                $url = $this->controller->request->here();
248
                $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500;
249
                $this->controller->response->statusCode($code);
250
                $this->controller->set(array(
251
                        'name' => h($message),
252
                        'message' => h($message),
253
                        'url' => h($url),
254
                        'error' => $error,
255
                        '_serialize' => array('name', 'message', 'url')
256
                ));
257
                $this->_outputMessage('error500');
258
        }
259

    
260
/**
261
 * Convenience method to display a PDOException.
262
 *
263
 * @param PDOException $error The exception to render.
264
 * @return void
265
 */
266
        public function pdoError(PDOException $error) {
267
                $url = $this->controller->request->here();
268
                $code = 500;
269
                $this->controller->response->statusCode($code);
270
                $this->controller->set(array(
271
                        'code' => $code,
272
                        'name' => h($error->getMessage()),
273
                        'message' => h($error->getMessage()),
274
                        'url' => h($url),
275
                        'error' => $error,
276
                        '_serialize' => array('code', 'name', 'message', 'url', 'error')
277
                ));
278
                $this->_outputMessage($this->template);
279
        }
280

    
281
/**
282
 * Generate the response using the controller object.
283
 *
284
 * @param string $template The template to render.
285
 * @return void
286
 */
287
        protected function _outputMessage($template) {
288
                try {
289
                        $this->controller->render($template);
290
                        $this->controller->afterFilter();
291
                        $this->controller->response->send();
292
                } catch (MissingViewException $e) {
293
                        $attributes = $e->getAttributes();
294
                        if (isset($attributes['file']) && strpos($attributes['file'], 'error500') !== false) {
295
                                $this->_outputMessageSafe('error500');
296
                        } else {
297
                                $this->_outputMessage('error500');
298
                        }
299
                } catch (MissingPluginException $e) {
300
                        $attributes = $e->getAttributes();
301
                        if (isset($attributes['plugin']) && $attributes['plugin'] === $this->controller->plugin) {
302
                                $this->controller->plugin = null;
303
                        }
304
                        $this->_outputMessageSafe('error500');
305
                } catch (Exception $e) {
306
                        $this->_outputMessageSafe('error500');
307
                }
308
        }
309

    
310
/**
311
 * A safer way to render error messages, replaces all helpers, with basics
312
 * and doesn't call component methods.
313
 *
314
 * @param string $template The template to render
315
 * @return void
316
 */
317
        protected function _outputMessageSafe($template) {
318
                $this->controller->layoutPath = null;
319
                $this->controller->subDir = null;
320
                $this->controller->viewPath = 'Errors';
321
                $this->controller->layout = 'error';
322
                $this->controller->helpers = array('Form', 'Html', 'Session');
323

    
324
                $view = new View($this->controller);
325
                $this->controller->response->body($view->render($template, 'error'));
326
                $this->controller->response->type('html');
327
                $this->controller->response->send();
328
        }
329

    
330
}