diff --git a/cake/libs/error_handler.php b/cake/libs/error_handler.php
index 73deafc9c..33146a289 100644
--- a/cake/libs/error_handler.php
+++ b/cake/libs/error_handler.php
@@ -54,119 +54,17 @@
*/
class ErrorHandler {
-/**
- * Controller instance.
- *
- * @var Controller
- * @access public
- */
- public $controller = null;
-
-/**
- * template to render for CakeException
- *
- * @var string
- */
- public $template = '';
-
-/**
- * The method corresponding to the Exception this object is for.
- *
- * @var string
- */
- public $method = '';
-
-/**
- * The exception being handled.
- *
- * @var Exception
- */
- public $error = null;
-
-/**
- * Creates the controller to perform rendering on the error response.
- * If the error is a CakeException it will be converted to either a 400 or a 500
- * code error depending on the code used to construct the error.
- *
- * @param string $method Method producing the error
- * @param array $messages Error messages
- */
- function __construct(Exception $exception) {
- App::import('Core', 'Sanitize');
-
- $this->controller = $this->_getController($exception);
-
- if (method_exists($this->controller, 'apperror')) {
- return $this->controller->appError($exception);
- }
- $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));
- $code = $exception->getCode();
-
- $methodExists = method_exists($this, $method);
-
- if ($exception instanceof CakeException && !$methodExists) {
- $method = '_cakeError';
- if ($template == 'internalError') {
- $template = 'error500';
- }
- } elseif (!$methodExists) {
- $method = 'error500';
- if ($code >= 400) {
- $method = 'error400';
- }
- }
-
- if (Configure::read('debug') == 0) {
- $parentClass = get_parent_class($this);
- if ($parentClass != 'ErrorHandler') {
- $method = 'error400';
- }
- $parentMethods = (array)get_class_methods($parentClass);
- if (in_array($method, $parentMethods)) {
- $method = 'error400';
- }
- if ($code == 500) {
- $method = 'error500';
- }
- }
- $this->template = $template;
- $this->method = $method;
- $this->error = $exception;
- }
-
-/**
- * Get the controller instance to handle the exception.
- * Override this method in subclasses to customize the controller used.
- * This method returns the built in `CakeErrorController` normally, or if an error is repeated
- * a bare controller will be used.
- *
- * @param Exception $exception The exception to get a controller for.
- * @return Controller
- */
- protected function _getController($exception) {
- static $__previousError = null;
- App::import('Controller', 'CakeError');
-
- if ($__previousError != $exception) {
- $__previousError = $exception;
- $controller = new CakeErrorController();
- } else {
- $controller = new Controller();
- $controller->viewPath = 'errors';
- }
- return $controller;
- }
-
/**
* Set as the default exception handler by the CakePHP bootstrap process.
*
* This will either use an AppError class if your application has one,
- * or use the default ErrorHandler.
+ * or use the default ExceptionRenderer.
*
* @return void
* @see http://php.net/manual/en/function.set-exception-handler.php
*/
public static function handleException(Exception $exception) {
+ App::import('Core', 'ExceptionRenderer');
if (file_exists(APP . 'app_error.php') || class_exists('AppError')) {
if (!class_exists('AppError')) {
require(APP . 'app_error.php');
@@ -174,7 +72,7 @@ class ErrorHandler {
$AppError = new AppError($exception);
return $AppError->render();
}
- $error = new ErrorHandler($exception);
+ $error = new ExceptionRenderer($exception);
$error->render();
}
@@ -271,81 +169,4 @@ class ErrorHandler {
}
return array($error, $log);
}
-
-/**
- * Renders the response for the exception.
- *
- * @return void
- */
- public function render() {
- call_user_func_array(array($this, $this->method), array($this->error));
- }
-
-/**
- * Generic handler for the internal framework errors CakePHP can generate.
- *
- * @param CakeExeption $error
- * @return void
- */
- protected function _cakeError(CakeException $error) {
- $url = Router::normalize($this->controller->request->here);
- $code = $error->getCode();
- $this->controller->response->statusCode($code);
- $this->controller->set(array(
- 'code' => $code,
- 'url' => h($url),
- 'name' => $error->getMessage(),
- 'error' => $error,
- ));
- $this->controller->set($error->getAttributes());
- $this->_outputMessage($this->template);
- }
-
-/**
- * Convenience method to display a 400 series page.
- *
- * @param array $params Parameters for controller
- */
- public function error400($error) {
- $message = $error->getMessage();
- if (Configure::read('debug') == 0 && $error instanceof CakeException) {
- $message = __('Not Found');
- }
- $url = Router::normalize($this->controller->request->here);
- $this->controller->response->statusCode($error->getCode());
- $this->controller->set(array(
- 'name' => $message,
- 'url' => h($url),
- 'error' => $error,
- ));
- $this->_outputMessage('error400');
- }
-
-/**
- * Convenience method to display a 500 page.
- *
- * @param array $params Parameters for controller
- */
- public function error500($error) {
- $url = Router::normalize($this->controller->request->here);
- $code = ($error->getCode() > 500) ? $error->getCode() : 500;
- $this->controller->response->statusCode($code);
- $this->controller->set(array(
- 'name' => __('An Internal Error Has Occurred'),
- 'message' => h($url),
- 'error' => $error,
- ));
- $this->_outputMessage('error500');
- }
-
-/**
- * Generate the response using the controller object.
- *
- * @param string $template The template to render.
- */
- protected function _outputMessage($template) {
- $this->controller->render($template);
- $this->controller->afterFilter();
- $this->controller->response->send();
- }
}
diff --git a/cake/libs/exception_renderer.php b/cake/libs/exception_renderer.php
new file mode 100644
index 000000000..cb795d212
--- /dev/null
+++ b/cake/libs/exception_renderer.php
@@ -0,0 +1,233 @@
+ 1.
+ * When debug < 1 a CakeException will render 404 or 500 errors. If an uncaught exception is thrown
+ * and it is a type that ExceptionHandler does not know about it will be treated as a 500 error.
+ *
+ * ### Implementing application specific exception handling
+ *
+ * You can implement application specific exception handling in one of a few ways:
+ *
+ * - Create a AppController::appError();
+ * - Create an AppError class.
+ *
+ * #### Using AppController::appError();
+ *
+ * This controller method is called instead of the default exception handling. It receives the
+ * thrown exception as its only argument. You should implement your error handling in that method.
+ *
+ * #### Using an AppError class
+ *
+ * This approach gives more flexibility and power in how you handle exceptions. You can create
+ * `app/libs/app_error.php` and create a class called `AppError`. The core ErrorHandler class
+ * will attempt to construct this class and let it handle the exception. This provides a more
+ * flexible way to handle exceptions in your application.
+ *
+ * @package cake
+ * @subpackage cake.cake.libs
+ */
+class ExceptionRenderer {
+
+/**
+ * Controller instance.
+ *
+ * @var Controller
+ * @access public
+ */
+ public $controller = null;
+
+/**
+ * template to render for CakeException
+ *
+ * @var string
+ */
+ public $template = '';
+
+/**
+ * The method corresponding to the Exception this object is for.
+ *
+ * @var string
+ */
+ public $method = '';
+
+/**
+ * The exception being handled.
+ *
+ * @var Exception
+ */
+ public $error = null;
+
+/**
+ * Creates the controller to perform rendering on the error response.
+ * If the error is a CakeException it will be converted to either a 400 or a 500
+ * code error depending on the code used to construct the error.
+ *
+ * @param string $method Method producing the error
+ * @param array $messages Error messages
+ */
+ function __construct(Exception $exception) {
+ App::import('Core', 'Sanitize');
+
+ $this->controller = $this->_getController($exception);
+
+ if (method_exists($this->controller, 'apperror')) {
+ return $this->controller->appError($exception);
+ }
+ $method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));
+ $code = $exception->getCode();
+
+ $methodExists = method_exists($this, $method);
+
+ if ($exception instanceof CakeException && !$methodExists) {
+ $method = '_cakeError';
+ if ($template == 'internalError') {
+ $template = 'error500';
+ }
+ } elseif (!$methodExists) {
+ $method = 'error500';
+ if ($code >= 400) {
+ $method = 'error400';
+ }
+ }
+
+ if (Configure::read('debug') == 0) {
+ $parentClass = get_parent_class($this);
+ if ($parentClass != __CLASS__) {
+ $method = 'error400';
+ }
+ $parentMethods = (array)get_class_methods($parentClass);
+ if (in_array($method, $parentMethods)) {
+ $method = 'error400';
+ }
+ if ($code == 500) {
+ $method = 'error500';
+ }
+ }
+ $this->template = $template;
+ $this->method = $method;
+ $this->error = $exception;
+ }
+
+/**
+ * Get the controller instance to handle the exception.
+ * Override this method in subclasses to customize the controller used.
+ * This method returns the built in `CakeErrorController` normally, or if an error is repeated
+ * a bare controller will be used.
+ *
+ * @param Exception $exception The exception to get a controller for.
+ * @return Controller
+ */
+ protected function _getController($exception) {
+ static $__previousError = null;
+ App::import('Controller', 'CakeError');
+
+ if ($__previousError != $exception) {
+ $__previousError = $exception;
+ $controller = new CakeErrorController();
+ } else {
+ $controller = new Controller();
+ $controller->viewPath = 'errors';
+ }
+ return $controller;
+ }
+
+/**
+ * Renders the response for the exception.
+ *
+ * @return void
+ */
+ public function render() {
+ call_user_func_array(array($this, $this->method), array($this->error));
+ }
+
+/**
+ * Generic handler for the internal framework errors CakePHP can generate.
+ *
+ * @param CakeExeption $error
+ * @return void
+ */
+ protected function _cakeError(CakeException $error) {
+ $url = Router::normalize($this->controller->request->here);
+ $code = $error->getCode();
+ $this->controller->response->statusCode($code);
+ $this->controller->set(array(
+ 'code' => $code,
+ 'url' => h($url),
+ 'name' => $error->getMessage(),
+ 'error' => $error,
+ ));
+ $this->controller->set($error->getAttributes());
+ $this->_outputMessage($this->template);
+ }
+
+/**
+ * Convenience method to display a 400 series page.
+ *
+ * @param array $params Parameters for controller
+ */
+ public function error400($error) {
+ $message = $error->getMessage();
+ if (Configure::read('debug') == 0 && $error instanceof CakeException) {
+ $message = __('Not Found');
+ }
+ $url = Router::normalize($this->controller->request->here);
+ $this->controller->response->statusCode($error->getCode());
+ $this->controller->set(array(
+ 'name' => $message,
+ 'url' => h($url),
+ 'error' => $error,
+ ));
+ $this->_outputMessage('error400');
+ }
+
+/**
+ * Convenience method to display a 500 page.
+ *
+ * @param array $params Parameters for controller
+ */
+ public function error500($error) {
+ $url = Router::normalize($this->controller->request->here);
+ $code = ($error->getCode() > 500) ? $error->getCode() : 500;
+ $this->controller->response->statusCode($code);
+ $this->controller->set(array(
+ 'name' => __('An Internal Error Has Occurred'),
+ 'message' => h($url),
+ 'error' => $error,
+ ));
+ $this->_outputMessage('error500');
+ }
+
+/**
+ * Generate the response using the controller object.
+ *
+ * @param string $template The template to render.
+ */
+ protected function _outputMessage($template) {
+ $this->controller->render($template);
+ $this->controller->afterFilter();
+ $this->controller->response->send();
+ }
+}
\ No newline at end of file
diff --git a/cake/tests/cases/libs/error_handler.test.php b/cake/tests/cases/libs/error_handler.test.php
index e09e4fc85..490ce1a87 100644
--- a/cake/tests/cases/libs/error_handler.test.php
+++ b/cake/tests/cases/libs/error_handler.test.php
@@ -188,16 +188,6 @@ class ErrorHandlerTest extends CakeTestCase {
}
}
-/**
- * Mocks out the response on the errorhandler object so headers aren't modified.
- *
- * @return void
- */
- protected function _mockResponse($error) {
- $error->controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
- return $error;
- }
-
/**
* test error handling when debug is on, an error should be printed from Debugger.
*
@@ -288,376 +278,4 @@ class ErrorHandlerTest extends CakeTestCase {
$this->assertPattern('/Kaboom!/', $result, 'message missing.');
}
-/**
- * test that methods declared in an ErrorHandler subclass are not converted
- * into error400 when debug > 0
- *
- * @return void
- */
- function testSubclassMethodsNotBeingConvertedToError() {
- Configure::write('debug', 2);
-
- $exception = new MissingWidgetThingException('Widget not found');
- $ErrorHandler = $this->_mockResponse(new MyCustomErrorHandler($exception));
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertEqual($result, 'widget thing is missing');
- }
-
-/**
- * test that subclass methods are not converted when debug = 0
- *
- * @return void
- */
- function testSubclassMethodsNotBeingConvertedDebug0() {
- Configure::write('debug', 0);
- $exception = new MissingWidgetThingException('Widget not found');
- $ErrorHandler = $this->_mockResponse(new MyCustomErrorHandler($exception));
-
- $this->assertEqual('missingWidgetThing', $ErrorHandler->method);
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertEqual($result, 'widget thing is missing', 'Method declared in subclass converted to error400');
- }
-
-/**
- * test that ErrorHandler subclasses properly convert framework errors.
- *
- * @return void
- */
- function testSubclassConvertingFrameworkErrors() {
- Configure::write('debug', 0);
-
- $exception = new MissingControllerException('PostsController');
- $ErrorHandler = $this->_mockResponse(new MyCustomErrorHandler($exception));
-
- $this->assertEqual('error400', $ErrorHandler->method);
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertPattern('/Not Found/', $result, 'Method declared in error handler not converted to error400. %s');
- }
-
-/**
- * test things in the constructor.
- *
- * @return void
- */
- function testConstruction() {
- $exception = new NotFoundException('Page not found');
- $ErrorHandler = new ErrorHandler($exception);
-
- $this->assertType('CakeErrorController', $ErrorHandler->controller);
- $this->assertEquals('error400', $ErrorHandler->method);
- $this->assertEquals($exception, $ErrorHandler->error);
- }
-
-/**
- * test that method gets coerced when debug = 0
- *
- * @return void
- */
- function testErrorMethodCoercion() {
- Configure::write('debug', 0);
- $exception = new MissingActionException('Page not found');
- $ErrorHandler = new ErrorHandler($exception);
-
- $this->assertType('CakeErrorController', $ErrorHandler->controller);
- $this->assertEquals('error400', $ErrorHandler->method);
- $this->assertEquals($exception, $ErrorHandler->error);
- }
-
-/**
- * test that unknown exception types with valid status codes are treated correctly.
- *
- * @return void
- */
- function testUnknownExceptionTypeWithExceptionThatHasA400Code() {
- $exception = new MissingWidgetThingException('coding fail.');
- $ErrorHandler = new ErrorHandler($exception);
- $ErrorHandler->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
- $ErrorHandler->controller->response->expects($this->once())->method('statusCode')->with(404);
-
- ob_start();
- $ErrorHandler->render();
- $results = ob_get_clean();
-
- $this->assertFalse(method_exists($ErrorHandler, 'missingWidgetThing'), 'no method should exist.');
- $this->assertEquals('error400', $ErrorHandler->method, 'incorrect method coercion.');
- }
-
-/**
- * test that unknown exception types with valid status codes are treated correctly.
- *
- * @return void
- */
- function testUnknownExceptionTypeWithNoCodeIsA500() {
- $exception = new OutOfBoundsException('foul ball.');
- $ErrorHandler = new ErrorHandler($exception);
- $ErrorHandler->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
- $ErrorHandler->controller->response->expects($this->once())->method('statusCode')->with(500);
-
- ob_start();
- $ErrorHandler->render();
- $results = ob_get_clean();
-
- $this->assertEquals('error500', $ErrorHandler->method, 'incorrect method coercion.');
- }
-
-/**
- * testerror400 method
- *
- * @access public
- * @return void
- */
- function testError400() {
- Router::reload();
-
- $request = new CakeRequest('posts/view/1000', false);
- Router::setRequestInfo($request);
-
- $exception = new NotFoundException('Custom message');
- $ErrorHandler = new ErrorHandler($exception);
- $ErrorHandler->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
- $ErrorHandler->controller->response->expects($this->once())->method('statusCode')->with(404);
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertPattern('/
Custom message<\/h2>/', $result);
- $this->assertPattern("/'\/posts\/view\/1000'<\/strong>/", $result);
- }
-
-/**
- * test that error400 only modifies the messages on CakeExceptions.
- *
- * @return void
- */
- function testerror400OnlyChangingCakeException() {
- Configure::write('debug', 0);
-
- $exception = new NotFoundException('Custom message');
- $ErrorHandler = $this->_mockResponse(new ErrorHandler($exception));
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
- $this->assertContains('Custom message', $result);
-
- $exception = new MissingActionException(array('controller' => 'PostsController', 'action' => 'index'));
- $ErrorHandler = $this->_mockResponse(new ErrorHandler($exception));
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
- $this->assertContains('Not Found', $result);
- }
-/**
- * test that error400 doesn't expose XSS
- *
- * @return void
- */
- function testError400NoInjection() {
- Router::reload();
-
- $request = new CakeRequest('pages/pink', false);
- Router::setRequestInfo($request);
-
- $exception = new NotFoundException('Custom message');
- $ErrorHandler = $this->_mockResponse(new ErrorHandler($exception));
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertNoPattern('##', $result);
- }
-
-/**
- * testError500 method
- *
- * @access public
- * @return void
- */
- function testError500Message() {
- $exception = new InternalErrorException('An Internal Error Has Occurred');
- $ErrorHandler = new ErrorHandler($exception);
- $ErrorHandler->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
- $ErrorHandler->controller->response->expects($this->once())->method('statusCode')->with(500);
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertPattern('/An Internal Error Has Occurred<\/h2>/', $result);
- }
-
-/**
- * testMissingController method
- *
- * @access public
- * @return void
- */
- function testMissingController() {
- $exception = new MissingControllerException(array('controller' => 'PostsController'));
- $ErrorHandler = $this->_mockResponse(new ErrorHandler($exception));
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- $this->assertPattern('/Missing Controller<\/h2>/', $result);
- $this->assertPattern('/PostsController<\/em>/', $result);
- }
-
-/**
- * Returns an array of tests to run for the various CakeException classes.
- *
- * @return void
- */
- public static function testProvider() {
- return array(
- array(
- new MissingActionException(array('controller' => 'PostsController', 'action' => 'index')),
- array(
- '/Missing Method in PostsController<\/h2>/',
- '/PostsController::<\/em>index\(\)<\/em>/'
- ),
- 404
- ),
- array(
- new PrivateActionException(array('controller' => 'PostsController' , 'action' => '_secretSauce')),
- array(
- '/Private Method in PostsController<\/h2>/',
- '/PostsController::<\/em>_secretSauce\(\)<\/em>/'
- ),
- 404
- ),
- array(
- new MissingTableException(array('table' => 'articles', 'class' => 'Article')),
- array(
- '/Missing Database Table<\/h2>/',
- '/table articles<\/em> for model Article<\/em>/'
- ),
- 500
- ),
- array(
- new MissingDatabaseException(array('connection' => 'default')),
- array(
- '/Missing Database Connection<\/h2>/',
- '/Confirm you have created the file/'
- ),
- 500
- ),
- array(
- new MissingViewException(array('file' => '/posts/about.ctp')),
- array(
- "/posts\/about.ctp/"
- ),
- 500
- ),
- array(
- new MissingLayoutException(array('file' => 'layouts/my_layout.ctp')),
- array(
- "/Missing Layout/",
- "/layouts\/my_layout.ctp/"
- ),
- 500
- ),
- array(
- new MissingConnectionException(array('class' => 'Article')),
- array(
- '/Missing Database Connection<\/h2>/',
- '/Article requires a database connection/'
- ),
- 500
- ),
- array(
- new MissingHelperFileException(array('file' => 'my_custom.php', 'class' => 'MyCustomHelper')),
- array(
- '/Missing Helper File<\/h2>/',
- '/Create the class below in file:/',
- '/(\/|\\\)my_custom.php/'
- ),
- 500
- ),
- array(
- new MissingHelperClassException(array('file' => 'my_custom.php', 'class' => 'MyCustomHelper')),
- array(
- '/Missing Helper Class<\/h2>/',
- '/The helper class MyCustomHelper<\/em> can not be found or does not exist./',
- '/(\/|\\\)my_custom.php/',
- ),
- 500
- ),
- array(
- new MissingBehaviorFileException(array('file' => 'my_custom.php', 'class' => 'MyCustomBehavior')),
- array(
- '/Missing Behavior File<\/h2>/',
- '/Create the class below in file:/',
- '/(\/|\\\)my_custom.php/',
- ),
- 500
- ),
- array(
- new MissingBehaviorClassException(array('file' => 'my_custom.php', 'class' => 'MyCustomBehavior')),
- array(
- '/The behavior class MyCustomBehavior<\/em> can not be found or does not exist./',
- '/(\/|\\\)my_custom.php/'
- ),
- 500
- ),
- array(
- new MissingComponentFileException(array('file' => 'sidebox.php', 'class' => 'SideboxComponent')),
- array(
- '/Missing Component File<\/h2>/',
- '/Create the class SideboxComponent<\/em> in file:/',
- '/(\/|\\\)sidebox.php/'
- ),
- 500
- ),
- array(
- new MissingComponentClassException(array('file' => 'sidebox.php', 'class' => 'SideboxComponent')),
- array(
- '/Missing Component Class<\/h2>/',
- '/Create the class SideboxComponent<\/em> in file:/',
- '/(\/|\\\)sidebox.php/'
- ),
- 500
- )
-
- );
- }
-
-/**
- * Test the various CakeException sub classes
- *
- * @dataProvider testProvider
- * @return void
- */
- function testCakeExceptionHandling($exception, $patterns, $code) {
- $ErrorHandler = new ErrorHandler($exception);
- $ErrorHandler->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
- $ErrorHandler->controller->response->expects($this->once())
- ->method('statusCode')
- ->with($code);
-
- ob_start();
- $ErrorHandler->render();
- $result = ob_get_clean();
-
- foreach ($patterns as $pattern) {
- $this->assertPattern($pattern, $result);
- }
- }
}
diff --git a/cake/tests/cases/libs/exception_renderer.test.php b/cake/tests/cases/libs/exception_renderer.test.php
new file mode 100644
index 000000000..c99c6b118
--- /dev/null
+++ b/cake/tests/cases/libs/exception_renderer.test.php
@@ -0,0 +1,573 @@
+
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ * @since CakePHP(tm) v 2.0
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::import('Core', array('ExceptionRenderer', 'Controller', 'Component'));
+
+/**
+ * Short description for class.
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ */
+class AuthBlueberryUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AuthBlueberryUser'
+ * @access public
+ */
+ public $name = 'AuthBlueberryUser';
+
+/**
+ * useTable property
+ *
+ * @var string
+ * @access public
+ */
+ public $useTable = false;
+}
+
+/**
+ * BlueberryComponent class
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ */
+class BlueberryComponent extends Component {
+
+/**
+ * testName property
+ *
+ * @access public
+ * @return void
+ */
+ public $testName = null;
+
+/**
+ * initialize method
+ *
+ * @access public
+ * @return void
+ */
+ function initialize(&$controller) {
+ $this->testName = 'BlueberryComponent';
+ }
+}
+
+/**
+ * TestErrorController class
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ */
+class TestErrorController extends Controller {
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+ public $uses = array();
+
+/**
+ * components property
+ *
+ * @access public
+ * @return void
+ */
+ public $components = array('Blueberry');
+
+/**
+ * beforeRender method
+ *
+ * @access public
+ * @return void
+ */
+ function beforeRender() {
+ echo $this->Blueberry->testName;
+ }
+
+/**
+ * index method
+ *
+ * @access public
+ * @return void
+ */
+ function index() {
+ $this->autoRender = false;
+ return 'what up';
+ }
+}
+
+/**
+ * MyCustomExceptionRenderer class
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ */
+class MyCustomExceptionRenderer extends ExceptionRenderer {
+
+/**
+ * custom error message type.
+ *
+ * @return void
+ */
+ function missingWidgetThing() {
+ echo 'widget thing is missing';
+ }
+}
+/**
+ * Exception class for testing app error handlers and custom errors.
+ *
+ * @package cake.test.cases.libs
+ */
+class MissingWidgetThingException extends NotFoundException { }
+
+
+/**
+ * ExceptionRendererTest class
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs
+ */
+class ExceptionRendererTest extends CakeTestCase {
+
+ var $_restoreError = false;
+/**
+ * setup create a request object to get out of router later.
+ *
+ * @return void
+ */
+ function setUp() {
+ App::build(array(
+ 'views' => array(
+ TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS,
+ TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS
+ )
+ ), true);
+ Router::reload();
+
+ $request = new CakeRequest(null, false);
+ $request->base = '';
+ Router::setRequestInfo($request);
+ $this->_debug = Configure::read('debug');
+ $this->_error = Configure::read('Error');
+ Configure::write('debug', 2);
+ }
+
+/**
+ * teardown
+ *
+ * @return void
+ */
+ function teardown() {
+ Configure::write('debug', $this->_debug);
+ Configure::write('Error', $this->_error);
+ App::build();
+ if ($this->_restoreError) {
+ restore_error_handler();
+ }
+ }
+
+/**
+ * Mocks out the response on the ExceptionRenderer object so headers aren't modified.
+ *
+ * @return void
+ */
+ protected function _mockResponse($error) {
+ $error->controller->response = $this->getMock('CakeResponse', array('_sendHeader'));
+ return $error;
+ }
+
+/**
+ * test that methods declared in an ExceptionRenderer subclass are not converted
+ * into error400 when debug > 0
+ *
+ * @return void
+ */
+ function testSubclassMethodsNotBeingConvertedToError() {
+ Configure::write('debug', 2);
+
+ $exception = new MissingWidgetThingException('Widget not found');
+ $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertEqual($result, 'widget thing is missing');
+ }
+
+/**
+ * test that subclass methods are not converted when debug = 0
+ *
+ * @return void
+ */
+ function testSubclassMethodsNotBeingConvertedDebug0() {
+ Configure::write('debug', 0);
+ $exception = new MissingWidgetThingException('Widget not found');
+ $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
+
+ $this->assertEqual('missingWidgetThing', $ExceptionRenderer->method);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertEqual($result, 'widget thing is missing', 'Method declared in subclass converted to error400');
+ }
+
+/**
+ * test that ExceptionRenderer subclasses properly convert framework errors.
+ *
+ * @return void
+ */
+ function testSubclassConvertingFrameworkErrors() {
+ Configure::write('debug', 0);
+
+ $exception = new MissingControllerException('PostsController');
+ $ExceptionRenderer = $this->_mockResponse(new MyCustomExceptionRenderer($exception));
+
+ $this->assertEqual('error400', $ExceptionRenderer->method);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertPattern('/Not Found/', $result, 'Method declared in error handler not converted to error400. %s');
+ }
+
+/**
+ * test things in the constructor.
+ *
+ * @return void
+ */
+ function testConstruction() {
+ $exception = new NotFoundException('Page not found');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+
+ $this->assertType('CakeErrorController', $ExceptionRenderer->controller);
+ $this->assertEquals('error400', $ExceptionRenderer->method);
+ $this->assertEquals($exception, $ExceptionRenderer->error);
+ }
+
+/**
+ * test that method gets coerced when debug = 0
+ *
+ * @return void
+ */
+ function testErrorMethodCoercion() {
+ Configure::write('debug', 0);
+ $exception = new MissingActionException('Page not found');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+
+ $this->assertType('CakeErrorController', $ExceptionRenderer->controller);
+ $this->assertEquals('error400', $ExceptionRenderer->method);
+ $this->assertEquals($exception, $ExceptionRenderer->error);
+ }
+
+/**
+ * test that unknown exception types with valid status codes are treated correctly.
+ *
+ * @return void
+ */
+ function testUnknownExceptionTypeWithExceptionThatHasA400Code() {
+ $exception = new MissingWidgetThingException('coding fail.');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+ $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
+ $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $results = ob_get_clean();
+
+ $this->assertFalse(method_exists($ExceptionRenderer, 'missingWidgetThing'), 'no method should exist.');
+ $this->assertEquals('error400', $ExceptionRenderer->method, 'incorrect method coercion.');
+ }
+
+/**
+ * test that unknown exception types with valid status codes are treated correctly.
+ *
+ * @return void
+ */
+ function testUnknownExceptionTypeWithNoCodeIsA500() {
+ $exception = new OutOfBoundsException('foul ball.');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+ $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
+ $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $results = ob_get_clean();
+
+ $this->assertEquals('error500', $ExceptionRenderer->method, 'incorrect method coercion.');
+ }
+
+/**
+ * testerror400 method
+ *
+ * @access public
+ * @return void
+ */
+ function testError400() {
+ Router::reload();
+
+ $request = new CakeRequest('posts/view/1000', false);
+ Router::setRequestInfo($request);
+
+ $exception = new NotFoundException('Custom message');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+ $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
+ $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(404);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertPattern('/Custom message<\/h2>/', $result);
+ $this->assertPattern("/'\/posts\/view\/1000'<\/strong>/", $result);
+ }
+
+/**
+ * test that error400 only modifies the messages on CakeExceptions.
+ *
+ * @return void
+ */
+ function testerror400OnlyChangingCakeException() {
+ Configure::write('debug', 0);
+
+ $exception = new NotFoundException('Custom message');
+ $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+ $this->assertContains('Custom message', $result);
+
+ $exception = new MissingActionException(array('controller' => 'PostsController', 'action' => 'index'));
+ $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+ $this->assertContains('Not Found', $result);
+ }
+/**
+ * test that error400 doesn't expose XSS
+ *
+ * @return void
+ */
+ function testError400NoInjection() {
+ Router::reload();
+
+ $request = new CakeRequest('pages/pink', false);
+ Router::setRequestInfo($request);
+
+ $exception = new NotFoundException('Custom message');
+ $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertNoPattern('##', $result);
+ }
+
+/**
+ * testError500 method
+ *
+ * @access public
+ * @return void
+ */
+ function testError500Message() {
+ $exception = new InternalErrorException('An Internal Error Has Occurred');
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+ $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
+ $ExceptionRenderer->controller->response->expects($this->once())->method('statusCode')->with(500);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertPattern('/An Internal Error Has Occurred<\/h2>/', $result);
+ }
+
+/**
+ * testMissingController method
+ *
+ * @access public
+ * @return void
+ */
+ function testMissingController() {
+ $exception = new MissingControllerException(array('controller' => 'PostsController'));
+ $ExceptionRenderer = $this->_mockResponse(new ExceptionRenderer($exception));
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ $this->assertPattern('/Missing Controller<\/h2>/', $result);
+ $this->assertPattern('/PostsController<\/em>/', $result);
+ }
+
+/**
+ * Returns an array of tests to run for the various CakeException classes.
+ *
+ * @return void
+ */
+ public static function testProvider() {
+ return array(
+ array(
+ new MissingActionException(array('controller' => 'PostsController', 'action' => 'index')),
+ array(
+ '/Missing Method in PostsController<\/h2>/',
+ '/PostsController::<\/em>index\(\)<\/em>/'
+ ),
+ 404
+ ),
+ array(
+ new PrivateActionException(array('controller' => 'PostsController' , 'action' => '_secretSauce')),
+ array(
+ '/Private Method in PostsController<\/h2>/',
+ '/PostsController::<\/em>_secretSauce\(\)<\/em>/'
+ ),
+ 404
+ ),
+ array(
+ new MissingTableException(array('table' => 'articles', 'class' => 'Article')),
+ array(
+ '/Missing Database Table<\/h2>/',
+ '/table articles<\/em> for model Article<\/em>/'
+ ),
+ 500
+ ),
+ array(
+ new MissingDatabaseException(array('connection' => 'default')),
+ array(
+ '/Missing Database Connection<\/h2>/',
+ '/Confirm you have created the file/'
+ ),
+ 500
+ ),
+ array(
+ new MissingViewException(array('file' => '/posts/about.ctp')),
+ array(
+ "/posts\/about.ctp/"
+ ),
+ 500
+ ),
+ array(
+ new MissingLayoutException(array('file' => 'layouts/my_layout.ctp')),
+ array(
+ "/Missing Layout/",
+ "/layouts\/my_layout.ctp/"
+ ),
+ 500
+ ),
+ array(
+ new MissingConnectionException(array('class' => 'Article')),
+ array(
+ '/Missing Database Connection<\/h2>/',
+ '/Article requires a database connection/'
+ ),
+ 500
+ ),
+ array(
+ new MissingHelperFileException(array('file' => 'my_custom.php', 'class' => 'MyCustomHelper')),
+ array(
+ '/Missing Helper File<\/h2>/',
+ '/Create the class below in file:/',
+ '/(\/|\\\)my_custom.php/'
+ ),
+ 500
+ ),
+ array(
+ new MissingHelperClassException(array('file' => 'my_custom.php', 'class' => 'MyCustomHelper')),
+ array(
+ '/Missing Helper Class<\/h2>/',
+ '/The helper class MyCustomHelper<\/em> can not be found or does not exist./',
+ '/(\/|\\\)my_custom.php/',
+ ),
+ 500
+ ),
+ array(
+ new MissingBehaviorFileException(array('file' => 'my_custom.php', 'class' => 'MyCustomBehavior')),
+ array(
+ '/Missing Behavior File<\/h2>/',
+ '/Create the class below in file:/',
+ '/(\/|\\\)my_custom.php/',
+ ),
+ 500
+ ),
+ array(
+ new MissingBehaviorClassException(array('file' => 'my_custom.php', 'class' => 'MyCustomBehavior')),
+ array(
+ '/The behavior class MyCustomBehavior<\/em> can not be found or does not exist./',
+ '/(\/|\\\)my_custom.php/'
+ ),
+ 500
+ ),
+ array(
+ new MissingComponentFileException(array('file' => 'sidebox.php', 'class' => 'SideboxComponent')),
+ array(
+ '/Missing Component File<\/h2>/',
+ '/Create the class SideboxComponent<\/em> in file:/',
+ '/(\/|\\\)sidebox.php/'
+ ),
+ 500
+ ),
+ array(
+ new MissingComponentClassException(array('file' => 'sidebox.php', 'class' => 'SideboxComponent')),
+ array(
+ '/Missing Component Class<\/h2>/',
+ '/Create the class SideboxComponent<\/em> in file:/',
+ '/(\/|\\\)sidebox.php/'
+ ),
+ 500
+ )
+
+ );
+ }
+
+/**
+ * Test the various CakeException sub classes
+ *
+ * @dataProvider testProvider
+ * @return void
+ */
+ function testCakeExceptionHandling($exception, $patterns, $code) {
+ $ExceptionRenderer = new ExceptionRenderer($exception);
+ $ExceptionRenderer->controller->response = $this->getMock('CakeResponse', array('statusCode', '_sendHeader'));
+ $ExceptionRenderer->controller->response->expects($this->once())
+ ->method('statusCode')
+ ->with($code);
+
+ ob_start();
+ $ExceptionRenderer->render();
+ $result = ob_get_clean();
+
+ foreach ($patterns as $pattern) {
+ $this->assertPattern($pattern, $result);
+ }
+ }
+}