Catch all error cakephp 2 [duplicate] - cakephp

This question already has answers here:
CakePHP 2.0 - How to make custom error pages?
(7 answers)
Closed 6 years ago.
I using cakephp, when have error, cakephp show error for users example : undefine index, notice in view page...I want when have error cakephp just show one message customize by me in view. Someone know ? Thank very much.

You can make custom error pages,
/app/Config/core.php
Exception render need to set as an AppExceptionRender. Example:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'AppExceptionRenderer',
'log' => true ));
/app/Controller/ErrorsController.php
class ErrorsController extends AppController {
public $name = 'Errors';
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('error404');
}
public function error404() {
//$this->layout = 'default';
} }
/app/Lib/Error/AppExceptionRenderer.php
App::uses('ExceptionRenderer', 'Error');
class AppExceptionRenderer extends ExceptionRenderer {
public function notFound($error) {
$this->controller->redirect(array('controller' => 'errors', 'action' => 'error404'));
} }
/app/View/Errors/error404.ctp
`enter code here`
404 Error - Page Not Found`enter code here`
Insert it where you need: throw new NotFoundException();
after that make sure debug mode is 0,
Its work with production mode only
Configure::write('debug', 0);

Related

Change CakePHP settings for tests

The Code
AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller')
),
'Session'
);
}
PostsController.php
<?php
App::uses('AppController', 'Controller');
class PostsController extends AppController {
public function isAuthorized() {
return $this->Auth->user('role') == 'admin';
}
public function add() {
$this->set('some_var', true);
}
}
PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The Problem
Right now, the "testAddWhileLoggedInAsNonAdminFails" test passes. It should fail. The issue is that redirects do not exit/halt the simulated request.
Partial Solution
I can fix the problem by modifying "AppController.php" and "PostsControllerTest.php" like so:
Modified AppController.php
<?php
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
'Auth' => array(
'authorize' => array('Controller'),
// ***** THE FOLLOWING LINE IS NEW *****
'unauthorizedRedirect' => false
),
'Session'
);
}
Modified PostsControllerTest.php
<?php
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
CakeSession::write('Auth.User', array(
'id' => 2,
'username' => 'joe_bloggs',
'role' => 'user',
'created' => '2013-05-17 10:00:00',
'modified' => '2013-05-17 10:00:00'
));
}
// ***** THE FOLLOWING 3 LINES ARE NEW *****
/**
* #expectedException ForbiddenException
*/
public function testAddWhileLoggedInAsNonAdminFails() {
$this->testAction('/posts/add/', array('method' => 'get'));
}
public function tearDown() {
parent::tearDown();
CakeSession::destroy();
}
}
The problem with this solution is it modifies the behavior of the real website too. I'm looking for a way to set the Auth component's unauthorizedRedirect property to false only when tests are being run. How can I do this?
Changing the behavior of your code to make tests work right is not really a good idea.
The correct answer to this question is that it's not a very good question, and what you really should do is test each function separately.
For the isAuthorized function, you should do:
<?php
class PostsControllerTest extends ControllerTestCase {
public function testIsAuthorized() {
$Posts = $this->generate('Posts');
$user = array('role' => 'admin');
$this->assertTrue($Posts->isAuthorized($user));
$anotherUser = array('role' => 'saboteur');
$this->assertFalse($Posts->isAuthorized($user));
}
public function testAdd() {
$this->testAction('/posts/add/', array('method' => 'get'));
$this->assertTrue($this->vars['some_var']);
}
}
The core concept behind unit testing is breaking down your app into the smallest pieces possible, and testing each in isolation. Once you have your unit tests sorted out, you can work on integration tests that cover more than one function, but many projects never reach that stage, and that's okay. The redirect issue can be interesting to work with, but you can mock out controller::redirect as described in this blog post. It's a bit old but still useful.
Did you check the book? http://book.cakephp.org/2.0/en/development/testing.html#testing-controllers
When testing actions that contain redirect() and other code following
the redirect it is generally a good idea to return when redirecting.
The reason for this, is that redirect() is mocked in testing, and does
not exit like normal. And instead of your code exiting, it will
continue to run code following the redirect.
It exactly describes your problem.
I haven't tested this but try it, the manual says the controller is already mocked when using ControllerTestCase so you should be able to expect it:
$this->controller->expects($this->at(0))
->method('redirect')
->with('/your-expected-input');
Taking a look at the ControllerTestCase class might reveal how the controller is exactly mocked and set up. Alternatively you could just fall back to the regular CakeTestCase and set the controller mocks up by yourself.
Another alternative would be to extend your controller you want to test and override the redirect() method, not calling the parent but setting the first arg to a property like Controller::$redirectUrl. After your action call you can then assertEqual the properties value. But this still requires you to return after the redirect call in your controller. Also this won't work either when using ControllerTestCase because it would mock your overriden method.

CakePHP: Redirect not workin

I have the following code in my controller:
class ContactsController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public function index() {
$this->set('contacts', $this->Contact->find('all'));
}
public function view($id) {
if (!$id) {
throw new NotFoundException(__('Invalid contact'));
}
$contact = $this->Contact->findById($id);
if (!$contact) {
throw new NotFoundException(__('Invalid contact'));
}
$this->set('contact', $contact);
}
public function add() {
if ($this->request->is('post')) {
$this->Contact->create();
if ($this->Contact->save($this->request->data)) {
$this->Session->setFlash('Your contact has been saved.');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('Unable to add your contact.');
}
}
}
}
In the add() method I have the line $this->redirect(array('action' => 'index')); I'm expecting this line to redirect back to my index page within my view. But all I get is a blank white page.
Any help appreciated.
Regards,
Stephen
Your code looks valid; I don't see an error in the code you provided.
There are a few possible causes;
there is an error somewhere else in your code, but cake php doesn't output the error message because debugging is disabled. Enable debugging by setting debugging to 1 or 2 inside app/Config/core.php - Configure::write('debug', 2);
you application is outputting something to the browser before the redirect header was sent. This may be caused by (non visible) white-space before or after any <?php or ?> . This will cause a 'headers already sent' warning, and the browser will not redirect. There are many questions regarding this situation here on StackOverflow, for example: How to fix "Headers already sent" error in PHP
Note;
in CakePHP its good practice to put a return before any redirect statement; this will allow better Unit-testing of your application, i.e.:
return $this->redirect(array('action' => 'index'));
update; additional 'pointers' on locating the cause
Tracking these kind of problems may be troublesome, I'll add some pointers
If you're using the Auth or Security component, it's possible that one of these cause the 'problem' (e.g. authorization failed or the posted data is marked 'invalid', which will be handled by a 'blackHole()' callback. Disable both in your AppController to check if the problem is still present
If the problem is still present, it is possible that headers are being sent (as mentioned before), but no warning/error is presented. To find if and where those headers are sent, add this code to your 'add' action;
Debugging code:
if (headers_sent($file, $line)) {
exit("headers were already sent in file: {$file}, line number: {$line}");
}
After lots of research finally I got a solution for that.
Please use
ob_start(); in AppController.php
ob_start();
class AppController extends Controller {
function beforeFilter() {
parent::beforeFilter();
}
}

CakePHP AMFPHP 2.1 vendor integration

I have problem with cakephp and amfphp 2.1 integration.
I made following controller:
class AmfController extends AppController
{
public function index(){
App::import('Vendor','Amfphp/index');
$this->autoRender = false;
}
public function backOffice(){
App::import('Vendor', 'backOffice', array('file' => 'BackOffice' . DS . 'ServiceBrowser.php'));
$this->autoRender = false;
}
}
Method index is working perfectly, cakephp is outputting amf entry point, but method backOffice is outputting following error:
Service call failed
object(CakeRequest) {
params => array(
[maximum depth reached]
)
data => array([maximum depth reached])
query => array([maximum depth reached])
url => 'amf/backOffice'
base => ''
webroot => '/'
here => '/amf/backOffice'
}
object(CakeResponse) {
}
Please help me, folder Amfphp and BackOffice are located in app/Vendor folder.
Here's a thought that might help: the service browser calls the entry point to get information about the various services, so that might create some bizarre side effects in CakePHP resulting in an infinite loop.
Other than that I don't know... If you still have trouble post back and I will give it a try.

Set page title when handling errors in CakePHP

I need to change page title from default "Error" when handling errors like 404. So I need to put my the title in the variable $title_for_layout for my Layout. I tried to create custom error handling function by changing configuration in app/Config/core.php and setting the page title as in controllers
Configure::write('Error.handler', function($code, $description, $file = null, $line = null, $context = null) {
$this->set('title_for_layout', 'Vyskytla sa chyba');
});
As I expected, I got a PHP error (line 59 is the second line in the code sample)
Fatal error: Using $this when not in object context in /var/www/web/app/Config/core.php on line 59
So how I can set the title for my default.ctp layout?
Thanks.
In CakePHP 2.0, you can try the following code to achieve the same you needed.
Try this:
/app/Config/core.php
Exception render need to set as an AppExceptionRender. Example:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'AppExceptionRenderer',
'log' => true
));
/app/Controller/ErrorsController.php
class ErrorsController extends AppController {
public $name = 'Errors';
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('error404');
}
public function error404() {
//$this->layout = 'default';
$this->set('title_for_layout', 'Vyskytla sa chyba');
}
}
/app/Lib/Error/AppExceptionRenderer.php
App::uses('ExceptionRenderer', 'Error');
class AppExceptionRenderer extends ExceptionRenderer {
public function notFound($error) {
$this->controller->redirect(array('controller' => 'errors', 'action' => 'error404'));
}
}
/app/View/Errors/error404.ctp
<div class="inner404">
<h2>404 Error - Page Not Found</h2>
</div>
Insert it where you need: throw new NotFoundException();
Ref: CakePHP 2.0 - How to make custom error pages?
For < CakePHP 2.x:
If you create a custome error page view in app/views/errors then in a
php section on that error view page you can use:
$this->setLayout("Title for the error page here");
Then when you see the error page, it will have your title. Again, that
is if you set a custom error page.
Here is another way to do the same you needed.
// Create an error.php file in your /app folder with the following code:
<?php
class AppError extends ErrorHandler {
function error404($params) {
$this->controller->layout = "error";
$this->set('title_for_layout', 'Vyskytla sa chyba');
parent::error404($params);
}
}
?>

Cakephp - how to make error pages have its own layouts?

I wanna have a different layout for the page not found 404 page. How can i set a different layout for that page?
Savant from the IRC helped me out and he suggest in using beforeRender(){} in the app_controller
// Before Render
function beforeRender() {
if($this->name == 'CakeError') {
//$this->layout = 'error';
}
}
CakeError is a catchAll for errors :D
In CakePHP 2.2.2 I changed the ExceptionRenderer in core.php with my own, like this:
app/Config/core.php:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'MyExceptionRenderer', // this is ExceptionRenderer by default
'log' => true
));
app/Lib/Error/MyExceptionRenderer.php:
App::uses('ExceptionRenderer', 'Error');
class MyExceptionRenderer extends ExceptionRenderer {
protected function _outputMessage($template) {
$this->controller->layout = 'error';
parent::_outputMessage($template);
}
}
Just you need to make layout changes in your error400.ctp file under /app/View/Errors/error400.ctp
Open that file and set layout by
<?php $this->layout=''; //set your layout here ?>
better to create an error.php file in your app folder
class AppError extends ErrorHandler {
function error404($params) {
$this->controller->layout = 'error';
parent::error404($params);
}
}
so you can avoid the if-testing at EVERY page render that savants' solution introduces
My solution for CakePHP 2.3
Change the ExceptionRenderer in core.php to use your own renderer.
app/Config/core.php:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'MyExceptionRenderer',
'log' => true
));
app/Lib/Error/MyExceptionRenderer.php:
App::uses('ExceptionRenderer', 'Error');
class MyExceptionRenderer extends ExceptionRenderer
{
/**
* Overrided, to always use a bare controller.
*
* #param Exception $exception The exception to get a controller for.
* #return Controller
*/
protected function _getController($exception) {
if (!$request = Router::getRequest(true)) {
$request = new CakeRequest();
}
$response = new CakeResponse(array('charset' => Configure::read('App.encoding')));
$controller = new Controller($request, $response);
$controller->viewPath = 'Errors';
$controller->layout = 'error';
return $controller;
}
}
The advantage to this approach is that it ensures any exceptions thrown from AppController don't cause an endless loop when rendering the exception. Forces a basic rendering of the exception message every time.
This simplest way I know of is to create this function in your AppController:
function appError($method, $messages)
{
}
You can then do whatever you want with the error, display it however you like, or not display it at all, send an email etc.. (I'm not sure if this method if still valid.)
There is also an option of creating app_error.php in your app root, with class AppError extends ErrorHandler in it, which enables you to override all kinds of errors. But I haven't done this yet, so I can't tell you more about it.
See cake/libs/error.php and cake/libs/object.php and of course The Book for more info.
Edit: Forgot to mention, once you caught the error, there's nothing preventing you to - for example - store the error in session, redirect to your "error handling controller", and then display it in your controller however you want.

Resources