Search code examples
pdotry-catch

PHP try catch not catching PDO broken pipe error


I am trying to get a long running PHP script to work with MySQL server. Upon initialization of the script, a new PDO connection to MySQL server will be created. By following instruction from Mr.Bill Karwin in his following post: PDO: How to check if connection is active, for real?

Every time before this PDO connection is used, whether the connection is still active will be checked through the following code:

    private function checkConn()
    {
        try {
            $this->dbConn->query("SELECT 1");  // line pointed to in error message.
        } catch (\Throwable $e) {
            $this->setupConn();
            \Sentry\captureException($e);
        }
    }

But somehow this try-catch block doesn't work. If the PDO connection has not been used for a while and MySQL server closes the connection or if MySQL server is shut down for testing, when the script tries to use PDO connection next time, it will fall onto global error handler set by set_error_handler(), as if try-catch is not there at all.

Error message: PDO::query(): send of 13 bytes failed with errno=32 Broken pipe In file: DbOp.php line:62

I am running this script with PHP7.4.30 on Ubuntu 20.04 LTS, with the following PDO attributes:

\PDO::ATTR_TIMEOUT      => 1,
\PDO::ATTR_ERRMODE      => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_PERSISTENT   => false

What am I missing here?

Please advise, thanks in advance.


Solution

  • It turns out the problem is elsewhere. During base class initialization, a customized error handler is set:

    set_error_handler(array($this, 'errorHandler'));
    

    Without explicitly setting error_levels, all levels of errors, notices and warnings will be routed to errorHandler, therefore bypassing try-catch.

    This therefore can be fixed by either setting a higher error_levels:

    set_error_handler(array($this, 'errorHandler'), E_ALL & ~E_NOTICE & ~E_WARNING);
    

    or temporally turning off error reporting as in Mr. Karwin's original post

        private function checkConn()
        {
            try {
                $old_errlevel = error_reporting(0);// This line is needed if customized error handler is set elsewhere
                $this->dbConn->query("SELECT 1");
            } catch (\Throwable $e) {
                exit(date("Y-m-d H:i:s").", uncaught exception: ".$e->getMessage()."\r\n");
                //$this->setupConn();
                //\Sentry\captureException($e);
            }
            error_reporting($old_errlevel);
        }