I want to create a simple PHP status file at the root of a Drupal website. This file will be called by a monitoring system such as Nagios. I want to fully customize the output and add some additional details into the error message (as a plain text string).
I've tried the following code:
define('DRUPAL_ROOT', __DIR__);
require_once realpath(DRUPAL_ROOT . '/includes/bootstrap.inc');
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
restore_exception_handler();
restore_error_handler();
function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "\n";
}
set_exception_handler('exception_handler');
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
echo "PHP Error: [$errno] $errstr<br />\n";
return true;
}
$old_error_handler = set_error_handler("myErrorHandler");
try {
db_query('SELECT COUNT(nid) FROM {node}');
echo 'ok';
}
catch (\PDOException $e) {
echo "Database problem";
}
catch (Exception $e) {
echo "Unexecpted error: " . $e->getMessage();
}
However, when I try to disable the database (by stopping the service), a Drupal custom error appears in HTML format.
You must go to admin/config/development/logging and check the option None to hide errors
Related
I am playing with try - catch block:
<?php
try {
$str = "http://rejstrik-firem.kurzy.cz/73631604";
$domOb = new DOMDocument();
$html = $domOb->loadHTMLFile($str);
$domOb->preserveWhiteSpace = false;
$container = $domOb->getElementById('ormaininfotab');
echo $container; // <========= this is intended error which I want catch
}
catch (Exception $e) {
echo "Exception" . $e->getMessage() . ". File: " . $e->getFile() . ", line: " . $e->getLine();
}
catch (Error $e) {
echo "Error" . $e->getMessage() . ". File: " . $e->getFile() . ", line: " . $e->getLine();
}
?>
My result is this:
Catchable fatal error: Object of class DOMElement could not be
converted to string in /var/www/html/cirkve_ares/test.php on line 8
Why is not this error catched by second catch?
As user2782001 mentioned this is not a bug in the eyes of PHP dev's. They even noted that these type of errors should be referenced as 'recoverable':
we should get rid of any references to "catchable" fatal errors (if they still exist) in favor of "recoverable" fatal errors. Using "catchable" here is confusing as they cannot be caught using catch blocks.
On the ErrorException manual page there is a neat workaround converting those "catchable/recoverable" errors to ErrorException.
<?php
function exception_error_handler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
?>
now you will be able to catch those errors with:
<?php
try {
// Error code
} catch (Error $e) { // this will catch only Errors
echo $e->getMessage();
}
?>
or
try {
// Error code
} catch (Throwable $t) { // this will catch both Errors and Exceptions
echo $t->getMessage();
}
?>
Someone reported this as a bug to PHP's devs, who promptly decided it was not a bug. https://bugs.php.net/bug.php?id=72948&edit=3
This case has been intentionally omitted ...(in practice you can simply convert the recoverable fatal to an exception using an error handler...)
So you still have to use the
set_error_handler()
function, which we were all hoping to leave behind. PHP's devs are so good at never letting your day be too sunny...
There might be some fatal errors which are not even caught by set_error_handler() or \Throwable.
The below implementation will catch the errors which are not even caught by \Throwable as tested in php 7.1. It should only be implemented in your development environment(by just adding it in your development config file) and shouldn't be done in production.
Implementation
register_shutdown_function(function () {
$err = error_get_last();
if (! is_null($err)) {
print 'Error#'.$err['message'].'<br>';
print 'Line#'.$err['line'].'<br>';
print 'File#'.$err['file'].'<br>';
}
});
Example Error
Error# Class Path/To/MyService contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Path/To/MyServiceInterface::add)
Line# 12
File# Path/To/MyService.php
It has been "fixed" as of PHP 7.4 according with Manual PHP, now it throws exception:
Existing recoverable fatal errors in string conversions have been converted to Error exceptions.
I have written a very simple code to catch an exception in PHP but it still shows an error on the page . I am not able to understand why it does not catch the exception .
<?php
try
{
session_start();
echo ($counter);
}
catch(Exception $e)
{
echo "caught exception";
}
?>
Before php 7 you could only catch exceptions, not errors, but since php 7 you have a new interface called \Throwable that is more general than just exceptions and also the Error class was introduced that implements Throwable
http://php.net/manual/en/class.throwable.php
There are currently two types of Throwable objects, that is Exceptions and Errors,
So now you can also catch Errors,
However Fatal errors still break your code
you can try
<?php
try
{
session_start();
echo ($counter);
}
catch(\Exception $e)
{
echo "caught exception";
}
catch(\Error $e)
{
echo "caught error";
}
or you car try
try
{
session_start();
echo ($counter);
}
catch(\Throwable $e)
{
echo "caught exception";
}
I am using zend framework 1.12 for my project. I want to catch all types of fatal errors and send them to an email address for quick fix. I have written the below mentioned code in Bootstrap.php file for this purpose.
protected function _initFatalErrorCatcher()
{
register_shutdown_function(array($this, 'errorlogHandler'));
}
public function errorlogHandler()
{
$e = error_get_last();
if (!is_null($e)) { //fatal error
$msg = 'Fatal error: ' . $e['message'];
$msg .= ' in' . $e['file'];
$msg .= ' on line: ' . $e['line'];
$mail = new Zend_Mail('utf-8');
$mail->setBodyHtml($msg);
$mail->setFrom('zzz#z.com');
$mail->addTo('yyy#y.com');
$mail->setSubject('check this error');
$mail->send();
}
}
Using the above code, i am able to send fatal errors other than database connection related errors and query related errors to email. I followed the instructions from Catch Zend PDO Exception as well, but i believe i am missing something as its not working.
Any help on this will be appreciated.
EDIT:
I am also using Zend_Log to write the error logs in a log-file. But, using this i could not find a way to write the fatal errors. Code for this is given below.
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . "/../data/log-file.log");
$errors = $this->_getParam('error_handler');
$exception = $errors->exception;
$log = new Zend_Log($writer);
$log->debug($exception->getMessage() . "\n" . $exception->getTraceAsString());
Scenario for database connection related issue:
If there is any error in host name, database name or in user name, it shows a Fatal error in browser like below. But its not detected by register_shutdown_function() or Zend_Log().
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB'' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 144 PDOException: SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 129
The post here shows an example. Basically use set_error_handler to tickle php into throwing exceptions when an error is encountered. This example from link:
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
set_error_handler("exception_error_handler");
/* Trigger exception */
strpos();
?>
Hope this helps
//$array contains the values for insert
try {
$this->db->insert('Users', $array );
} catch (Exception $e){
echo $e->getMessage();
}
I have solved it by writing the below mentioned code in Bootstrap.php file.
protected function _initDbConfig()
{
$config = new Zend_Config($this->getOptions());
$params = $config->database->toArray();
try {
$db = Zend_Db::factory('Pdo_Mysql', $params);
$db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
// perhaps the RDBMS is not running
// code to send email goes here
} catch (Zend_Exception $e) {
// perhaps factory() failed to load the specified Adapter class
// code to send email goes here
}
}
In application.ini, i have the following code.
database.host = "localhost"
database.username = "AAAA"
database.password = "*****"
database.dbname = "BBBBB"
Let's say I have the following piece of code.
To test this, I change the server IP to mimic the error messages. The IP below doesn't exist so the Unhandled Exception message is: Cannot connect to 10.199.1.7. Error 113. No route to host
This displays an ugly screen with PHP code. Is it possible to catch this error?
try {
$ssh = new Net_SSH2('10.199.1.7');
if (!$ssh->login('deploy', $key)) {
throw new Exception("Failed login");
}
} catch (Exception $e) {
???
}
Looked through library.
user_error('Connection closed by server', E_USER_NOTICE);
It triggers errors. You can handle those errors using http://php.net/manual/en/function.set-error-handler.php
e.g.
// Your file.php
$ssh = new Net_SSH2('10.199.1.7');
$ssh->login('deploy', $key);
// bootstrap.php
// This will catch all user notice errors!!!
set_error_handler ('errorHandler', E_USER_NOTICE)
function errorHandler($errno, $errstr, $errfile, $errline) {
echo 'Error';
// Whatever you want to do.
}
You can use # in front of you function call. # operator
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.