Catch and log fatal errors, warnings, notices in Zend Framework - php

Currently I am using a basic error controller which looks like this:
class ErrorController extends My_MyController
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()
->setRawHeader('HTTP/1.1 404 Not Found');
$this->view->headTitle('HTTP/1.1 404 Not Found');
$this->view->message = 'HTTP/1.1 404 Not Found';
break;
default:
// application error; display error page, but don't change
// status code
// Log the exception:
$exception = $errors->exception;
$log = new Zend_Log(
new Zend_Log_Writer_Stream(
BASE_PATH . '/logs/applicationException.log'
)
);
$log->debug($exception->getMessage() . "\n" .
$exception->getTraceAsString());
$this->view->headTitle('HTTP/1.1 404 Not Found');
$this->view->message = 'Application error';
break;
}
$this->view->exception = $errors->exception;
}
}
This, however, only catches and logs application exceptions. It won't log any warnings, notices and fatal errors.
I would like to log those as well. Is there a recommended way to do it in ErrorController? Or should it be done outside of the ErrorController in index.php (since Zend Framework ErrorHandler will only handle no route, missing application/action exceptions and exceptions in action controllers)?

Notices and warnings aren't (or shouldn't) be application fatal. Fatal errors do what they say on the tin and sometimes can't be caught. But, if you really need to, perhaps look into:
http://php.net/manual/en/function.set-error-handler.php

Related

Zend Exception instead of 404

My ZF app is throwing an exception instead of returning a 404 for urls which are really 404s. E.g. testlotto.ie/rrr should be a 404, yet I get the following:
Fatal error: Uncaught exception 'Zend_Controller_Dispatcher_Exception' with message 'Invalid controller specified (rrr)' in /mnt/pc/sites/git-lotto/lotto/irish-lotto/library/Zend/Controller/Dispatcher/Standard.php:
I have tried the suggestions on Fatal error: Uncaught exception 'Zend_Controller_Dispatcher_Exception' while creating object of model class and Uncaught exception 'Zend_Controller_Dispatcher_Exception' (as well as many others), and nothing is working.
I do have my ErrorController controller in the ../application/controllers dir, which is what is returned when I die() out getControllerDirectory() from Controller/Dispatcher/Standard.php - so no problem of controller not being found.
My ErrorController is as follows (not that it matters as it is not getting called):
<?php
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->getResponse()->setHttpResponseCode(404);
$this->getResponse()->setRawHeader('HTTP/1.1 404 Not Found');
$content =<<<EOH
<h1>Unlucky!</h1>
<p>The url you entered is not a valid one - please check what you typed, the mistake is most likely there.</p>
EOH;
break;
default:
// application error
$content =<<<EOH
<h1>Error!</h1>
<p>An unexpected error occurred. Please try again later.</p>
EOH;
break;
}
// Clear previous content
$this->getResponse()->clearBody();
$this->view->content = $content;
}
}
The start of the despatch method is:
public function dispatch(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response)
{
$this->setResponse($response);
/**
* Get controller class
*/
if (!$this->isDispatchable($request)) {
$controller = $request->getControllerName();
//die($controller);
//$this->setParam('useDefaultControllerAlways',true);
//ErrorController::errorAction();
if (!$this->getParam('useDefaultControllerAlways') && !empty($controller)) {
require_once 'Zend/Controller/Dispatcher/Exception.php';
throw new Zend_Controller_Dispatcher_Exception('Invalid controller specified (' . $request->getControllerName() . ')');
}
$className = $this->getDefaultControllerClass($request);
As you can see, I tried as suggested setting setParam('useDefaultControllerAlways',true);, but all this achieves is the homepage of the website is rendered (default controller of course) with a 200 response code - I need a 404.
Also, $controller dies out as the "rrr" in this example.
My question is what do I need to do to get 404's returned? This used to work on my site - I just swapped servers and now it is not working.
The only solution I could find to this which worked (i.e. the site now correctly returns a 404 for urls which do not exist), was taken from http://www.christopherhogan.com/2012/06/13/zend-framework-404-page/
Specifically, adding the below to my bootstrap file does the trick:
try {
$frontController->dispatch(); //instead of just $frontController->dispatch();
} catch (Zend_Exception $e) {
// this is where the 404 goes
header( 'HTTP/1.0 404 Not Found' );
echo "<div style='float:center; width:1000px; margin:0 auto;'><img src='http://www.foundco.com/images/404.jpg' alt='Everything is gonna be fine, please do not panic!' /></div>";
}
However, it's not perfect, as my ErrorController does not get called into action. If anyone could suggest how to trigger my ErrorController from the bootstrap file, would be much appreciated.

How to catch DB exceptions in Zend's (1.11) ErrorController?

I'm currently working on a project based on Zend 1.11 where I'm required to catch database related exceptions and display a notification should one occur. Needless to say I'm completely new to Zend Framework...
Judging by what I see in the default action defined within the ErrorController, I don't have a clue on how to achieve this:
class ErrorController extends Zend_Controller_Action
{
private $logPriority_;
public function errorAction()
{
$errors = $this->_getParam('error_handler');
if (!$errors || !$errors instanceof ArrayObject)
$this->_forward('notfound','error');
switch ($errors->type) {
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
// 404 error -- controller or action not found
$this->_forward('notfound','error');
break;
default:
// application error
break;
}
$this->getResponse()->setHttpResponseCode(500);
$this->view->message = '500 Internal Server Error';
$this->logErrors(Zend_Log::CRIT);
}
// ...
Where and how should I approach this issue?
You can check if Zend_Db_Exception is thrown:
if($errors->exception and $errors->exception instanceof Zend_Db_Exception) {
// do something
}

I'm trying to handle an Exception in PHP but stack error is still showing rather than being handled by catch

I'm calling a method that I know could cause an error and I'm trying to handle the error by wrapping the code in a try/catch statement...
class TestController extends Zend_Controller_Action
{
public function init()
{
// Anything here happens BEFORE the View has rendered
}
public function indexAction()
{
// Anything `echo`ed here is added to the end of the View
$model = new Application_Model_Testing('Mark', 31);
$this->view->sentence = $model->test();
$this->loadDataWhichCouldCauseError();
$this->loadView($model); // this method 'forwards' the Action onto another Controller
}
private function loadDataWhichCouldCauseError()
{
try {
$test = new Application_Model_NonExistent();
} catch (Exception $e) {
echo 'Handle the error';
}
}
private function loadView($model)
{
// Let's pretend we have loads of Models that require different Views
switch (get_class($model)) {
case 'Application_Model_Testing':
// Controller's have a `_forward` method to pass the Action onto another Controller
// The following line forwards to an `indexAction` within the `BlahController`
// It also passes some data onto the `BlahController`
$this->_forward('index', 'blah', null, array('data' => 'some data'));
break;
}
}
}
...but the problem I have is that the error isn't being handled. When viewing the application I get the following error...
( ! ) Fatal error: Class 'Application_Model_NonExistent' not found in /Library/WebServer/Documents/ZendTest/application/controllers/TestController.php on line 23
Can any one explain why this is happening and how I can get it to work?
Thanks
use
if (class_exists('Application_Model_NonExistent')) {
$test = new Application_Model_NonExistent;
} else {
echo 'class not found.';
}
like #prodigitalson said you can't catch that fatal error.
An error and an exception are not the same thing. Exceptions are thrown and meant to be caught, where errors are generally unrecoverable and triggered with http://www.php.net/manual/en/function.trigger-error.php
PHP: exceptions vs errors?
Can I try/catch a warning?
If you need to do some cleanup because of an error, you can use http://www.php.net/manual/en/function.set-error-handler.php
Thats not an exception, thats a FATAL error meaning you cant catch it like that. By definition a FATAL should not be recoverable.
Exception and Error are different things. There is an Exception class, which you are using and that $e is it's object.
You want to handle errors, check error handling in php-zend framework. But here, this is a Fatal error, you must rectify it, can not be handled.

PHP Exception handler kills script

basically i have a custom exception handler. When i handle an exception, i just want it to echo the message and continue the script. But after my method handles the exception, the script doesnt continue.
Is this a behaviour of php or is my exception handler doing something wrong?
This is a behavior of php. This differs from set_error_handler() in that, according to the manual on set_exception_handler(), Execution will stop after the exception_handler is called. Therefore, ensure you catch all exceptions, letting only those you want to kill your script through.
This is actually why set_error_handler() doesn't pair well with exceptions and set_exception_handler() when converting all errors to exceptions... unless you actually mean your application to be so strictly coded that any notice or warning halts the script. But at least it gives you a trace on that call involving an unset array key.
With a custom exception handler, you'll want to catch the exception in a try/catch block and do whatever handling you want in there.
The following is the example from The CodeUnit of Craig
try {
$error = 'Throw this error';
throw new Exception($error);
echo 'Never get here';
}
catch (Exception $e)
{
echo 'Exception caught: ', $e->getMessage(), "\n";
}
If you want to catch and print any unhandled exception, you can set a top level exception handler like this example from w3schools(near the bottom of the page)
<?php
function myException($exception){
echo "<b>Exception:</b> " , $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>
should print: "Exception: Uncaught Exception occurred"
Look at the following code. it worked for me:
define(BR, "<br/>");
try {
echo "throwing exception" . BR;
throw new Exception("This is exception");
}
catch(Exception $ex) {
echo "caught exception: " . BR . $ex->getMessage() . BR;
}
echo "Keep on going!. ..." . BR;
it prints the following:
throwing exception
caught exception:
This is exception
Keep on going!. ...
What do you say ?
Can you show the code of your code handler ?
You could do this :
function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
if (error_reporting() & $errno) {
// only process when included in error_reporting
return processError($errno, $errstring);
}
return true;
}
function handleException($exception){
// Here, you do whatever you want with the generated
// exceptions. You can store them in a file or database,
// output them in a debug section of your page or do
// pretty much anything else with it, as if it's a
// normal variable
}
function processError($code, $message){
switch ($code) {
case E_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
// Throw exception and stop execution of script
throw new Exception($message, $code);
default:
// Execute exception handler and continue execution afterwards
return handleException(new Exception($message, $code));
}
}
// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');
// ---------------------------------- //
// Generate warning
processError(E_USER_WARNING, 'This went wrong, but we can continue');
// Generate fatal error :
processError(E_USER_ERROR, 'This went horrible wrong');
Alternate approach :
function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
if (error_reporting() & $errno) {
// only process when included in error_reporting
return handleException(new \Exception($errstring, $errno));
}
return true;
}
function handleException($exception){
// Here, you do whatever you want with the generated
// exceptions. You can store them in a file or database,
// output them in a debug section of your page or do
// pretty much anything else with it, as if it's a
// normal variable
switch ($code) {
case E_ERROR:
case E_CORE_ERROR:
case E_USER_ERROR:
// Make sure script exits here
exit(1);
default:
// Let script continue
return true;
}
}
// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');
// ---------------------------------- //
// Generate warning
trigger_error('This went wrong, but we can continue', E_USER_WARNING);
// Generate fatal error :
trigger_error('This went horrible wrong', E_USER_ERROR);
An advantage of the latter strategy, is that you get the $errcontext parameter if you do $exception->getTrace() within the function handleException.
This is very useful for certain debugging purposes. Unfortunately, this works only if you use trigger_error directly from your context, which means you can't use a wrapper function/method to alias the trigger_error function (so you can't do something like function debug($code, $message) { return trigger_error($message, $code); } if you want the context data in your trace).
EDIT
I've found one dirty workaround for the trigger_error problem.
Consider the following code :
define("__DEBUG__", "Use of undefined constant DEBUG - assumed 'DEBUG'");
public static function handleError($code, $message, $file, $line, $context = false) {
if ($message == __DEBUG__) {
return static::exception(new \Exception(__DEBUG__, E_USER_WARNING));
} else {
if (error_reporting() & $code) {
return static::exception(new \Exception($message, $code));
}
return true;
}
}
public static function handleException($e) {
global $debug;
$code = $e->getCode();
$trace = $e->getTrace();
if ($e->getMessage() == __DEBUG__) {
// DEBUG
array_push($debug, array(
'__TIME__' => microtime(),
'__CONTEXT__' => array(
'file' => $trace[0]['file'],
'line' => $trace[0]['line'],
'function' => $trace[1]['function'],
'class' => $trace[1]['class'],
'type' => $trace[1]['type'],
'args' => $trace[0]['args'][4]
)
));
} else {
// NORMAL ERROR HANDLING
}
return true;
}
With this code, you can use the statement DEBUG; to generate a list of all available variables and a stack trace for any specific context. This list is stored in the global variable $debug. You can add it to a log file, add it to a database or print it out.
This is a VERY, VERY dirty hack, though, so use it at your own discretion. However, it can make debugging a lot easier and allows you to create a clean UI for your debug code.

php: autoload exception handling

I'm extending my previous question (Handling exceptions within exception handle) to address my bad coding practice.
I'm trying to delegate autoload errors to a exception handler.
<?php
function __autoload($class_name) {
$file = $class_name.'.php';
try {
if (file_exists($file)) {
include $file;
}else{
throw new loadException("File $file is missing");
}
if(!class_exists($class_name,false)){
throw new loadException("Class $class_name missing in $file");
}
}catch(loadException $e){
header("HTTP/1.0 500 Internal Server Error");
$e->loadErrorPage('500');
exit;
}
return true;
}
class loadException extends Exception {
public function __toString()
{
return get_class($this) . " in {$this->file}({$this->line})".PHP_EOL
."'{$this->message}'".PHP_EOL
. "{$this->getTraceAsString()}";
}
public function loadErrorPage($code){
try {
$page = new pageClass();
echo $page->showPage($code);
}catch(Exception $e){
echo 'fatal error: ', $code;
}
}
}
$test = new testClass();
?>
the above script is supposed to load a 404 page if the testClass.php file is missing, and it works fine, UNLESS the pageClass.php file is missing as well, in which case I see a
"Fatal error: Class 'pageClass' not found in D:\xampp\htdocs\Test\PHP\errorhandle\index.php on line 29" instead of the "fatal error: 500" message
I do not want to add a try/catch block to each and every class autoload (object creation), so i tried this.
What is the proper way of handling this?
Have you tried checking for pageClass early on in the process, since it seems to be necessary even to get the error page out? If it doesn't exist, and if you don't want to write the 404 page w/o any objects (e.g. just HTML), bombing out of execution where that class doesn't exist would seem to be a good path.
Hope that helps.
Thanks,
Joe

Categories