How to log database errors in zend framework - php

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"

Related

PHP invalid string concatenation with object instance, what error is thrown? [duplicate]

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.

Why is PDO throwing an ErrorException rather than a PDOException?

I have Sentry keeping track of uncaught exceptions in my PHP application, and I noticed a peculiar uncaught exception from PDO. The code looks like this:
/**
* #return boolean TRUE if the connection to the database worked; FALSE otherwise.
*/
public function verifyDatabase() {
try{
$this->pdo->query('SELECT 1');
return true;
}
catch (\PDOException $e) {
echo 'Lost connection to database: ' . $e->getMessage() . PHP_EOL;
return false;
}
}
This should catch errors like "MySQL server has gone away", and it indeed works on my development machine. However, Sentry recently recorded this error:
ErrorException
PDO::query(): MySQL server has gone away
According to sentry, this was thrown by the $this->pdo->query('SELECT 1'); statement above. Errors like this should have been caught by the try/catch. Why is PDO throwing an ErrorException rather than a PDOException?
I can't reproduce an ErrorException.
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', ..., ...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
sleep(20); // during this sleep, I stop my MySQL Server instance.
$result = $pdo->query("SELECT 1");
Output:
Warning: PDO::query(): MySQL server has gone away
Warning: PDO::query(): Error reading result set's header
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Stack trace:
#0 /Users/bkarwin/Documents/SO/pdo.php(8): PDO->query('SELECT 1')
This shows it throws a PDOException when the server has gone away, not an ErrorException.
Tested with MySQL 5.6.37 and PHP 7.1.23.
I wonder if the code you show in your question is actually the code that is deployed and throwing the exception to Sentry. Perhaps you have some code like:
catch (\PDOException $e) {
throw ErrorException($e->getMessage());
}
Either in your verifyDatabase() function, or else in the code that calls verifyDatabase(). In other words, what does your app do when verifyDatabase() returns false?
Okay, I think I've figured it out. It appears that this is related to a bug in which the PDO MySQL driver emits warnings even when they are supposed to be disabled (See also: this answer). I believe Sentry is then catching these as errors.
I was finally able to replicate, and solve this, by modifying Bill Karwin's test script:
// Initialize the Sentry reporting client
$ravenClient = new \Raven_Client(SENTRY_KEY);
$ravenClient->install();
echo 'Connecting...' . PHP_EOL;
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo 'Connected. Waiting...' . PHP_EOL;
sleep(20); // during this sleep, I stop my MySQL Server instance.
echo 'Querying...' . PHP_EOL;
try {
$result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}
This will print the following:
Connecting...
Connected. Waiting...
Querying...
PHP Warning: PDO::query(): MySQL server has gone away in /home/xxx/src/test.php on line 37
PHP Stack trace:
PHP 1. {main}() /home/xxx/src/test.php:0
PHP 2. PDO->query() /home/xxx/src/test.php:37
Caught PDOException SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Done.
And Sentry will record an ErrorException on the query() line.
I was able to solve this issue by implementing the "solution" posted by jferrer on the PHP bug report.
// Convert NOTICE, WARNING, ... in Exceptions
$convertErrorToException = function ($level, $message, $file, $line){
throw new ErrorException($message, 0, $level, $file, $line);
};
// The $previousErrorHandler will contain Sentry's handler
$previousErrorHandler = set_error_handler($convertErrorToException);
try {
$result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}
catch(\ErrorException $e) {
echo 'Caught ErrorException ' . $e->getMessage() . PHP_EOL;
}
// Restore Sentry as the default handler
set_error_handler($previousErrorHandler);
This results in just the ErrorException being thrown and caught:
Connecting...
Connected. Waiting...
Querying...
Caught ErrorException PDO::query(): MySQL server has gone away
Done.

how exceptions(try, catch) work in php?

I don't exactly know how exceptions work. as I assume, they should avoid php errors and display "my error message". for example, i want to open file
class File{
public $file;
public function __construct($file)
{
try{
$this->file = fopen($file,'r');
}
catch(Exception $e){
echo "some error" . $e->getMessage();
}
}
}
$file = new File('/var/www/html/OOP/texts.txt');
it works. now I intentionally change the file name texts.txt to tex.txt just to see an error message from my catch block, but instead, php gives an error Warning: fopen(/var/www/html/OOP/texts.txt): failed to open stream: No such file or directory in /var/www/html/OOP/file.php on line 169 . so it's php error, it doesn't display error message from catch block. What am I doing wrong? how exactly try/catch works?
From the PHP manual
If the open fails, an error of level E_WARNING is generated. You may
use # to suppress this warning.
fopen returns FALSE on error so you could test for that and throw an exception which would be caught. Some native PHP functions will generate exceptions, others raise errors.
class File{
public $file;
public function __construct($file){
try{
$this->file = #fopen($file,'r');
if( !$this->file ) throw new Exception('File could not be found',404);
} catch( Exception $e ){
echo "some error" . $e->getMessage();
}
}
}

Error even when using a Try/Catch block in PHP

I have a Laravel/PHP application with the following code:
try {
//gets the day of the last record for logged in user
$lastRecord = $user->records()->orderBy('date', 'DESC')->first()->date;
//convert to date
$lastTime = \Carbon\Carbon::createFromFormat('Y-m-d', $lastRecord);
}
catch(Exception $e) {
$lastTime = \Carbon\Carbon::now($user->timezone)->addDays(-1);
}
but I still get the error:
ErrorException in Habit.php line 104:
Trying to get property of non-object
Now I'm confused.. The main idea was to fail sometimes, and then continue to the catch block. How come it's still raising an error?
Just a guess, but try this instead
try {
//gets the day of the last record for logged in user
$lastRecord = $user->records()->orderBy('date', 'DESC')->first()->date;
//convert to date
$lastTime = \Carbon\Carbon::createFromFormat('Y-m-d', $lastRecord);
}
catch(\Exception $e) {
$lastTime = \Carbon\Carbon::now($user->timezone)->addDays(-1);
}
That is, add a leading namespace separator in front of Exception. My guess is you're using this code in a namespaced file. When you do something like this
namespace App\Some\Somenamespace;
...
catch(Exception $e) {
...
PHP assumes you want to catch an exception with the name App\Some\Somenamespace\Exception. Since that's not the exception thrown, and there's no second catch for the global PHP \Exception, PHP complains about an uncaught exception. You'll want to explicitly refer to the exception as global
catch(\Exception $e) {
or import it into the current namespace
namespace App\Some\Somenamespace\;
use Exception;
...
catch(Exception $e) {
...
FWIW, I still do this all the time. Old habits are hard to break.
The exception says that you are accessing a property of a non-object. This means that either $user itself or one of the properies in chain does not exist. It may be possible to avoid this by checking return values.
But if you still want to catch the fatal error, you may install a global shutdown handler, like this:
register_shutdown_function( "shutdown_handler" );
function fatal_handler() {
$errfile = "unknown file";
$errstr = "shutdown";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if( $error !== NULL) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
error_mail(format_error( $errno, $errstr, $errfile, $errline));
}
}
taken from this SO post and this SO post.

Mysqli php i want a custom error message when unknown server host

I am writing a script to install the database of an application in php. It working fine but when im trying to install a database that doesnt exist i want only my own error message but i keep getting the default Warning : Warning: mysqli::mysqli() [mysqli.mysqli]: (HY000/2005): Unknown MySQL server host 'kasdasd'.
So I know that the host is wrong and I want it to be so, with only my own errormessage. How do I get rid of this message?
My connectclass with parameter DBConfig $config:
$this->mysqli = new mysqli($config->m_host,
$config->m_user,
$config->m_passw,
$config->m_db);
if ($this->mysqli->connect_error) {
return false;
}
$this->mysqli->set_charset("utf8");
return true;
an easy solution would be to see if you can open the hostname using fsockopen and suppressing the errors:
$port = 80;
if($fp = #fsockopen($config->m_host,$port)){
$db = new mysqli($config->m_host,$config->m_user,$config->m_passw,$config->m_db);
}else{
echo 'hostname not recognized';
}
#fclose($fp);
Edited:
You can use
if ($mysqli->connect_error) {
/** handle your error here **/
// Throw a custom exception if you like! (see below)
// or just echo "There was an error";
}
You can further use mysqli_connect_errno() to find out wht happened and handle it accordingly.
Edit: mysqli doesn't throw erros so below is incorrect.
Wrap your code in a try-catch, then you can throw whatever kind of error you like:
try {
/** your code here **/
} catch (Exception $e) {
/** your handling here **/
// i.e.
throw new BadHostNameException($config->m_host);
// or
echo "Could not connect!":
}
Note: you should replace Exception $e with the specific kind of exception a bad host throws so catch(MysqlBadHostnameException $e) (the type of error will be in your error log from your previous attempts or you can do get_class($e) in my example above.
// Isusing a custom exception: Add this outside of your class..
class BadHostNameException extends Exception {}

Categories