php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #70689 Exception handler does not work as expected.
Submitted: 2015-10-11 03:34 UTC Modified: 2015-10-12 09:17 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: yajenyak at yandex dot ru Assigned:
Status: Closed Package: Scripting Engine problem
PHP Version: 7.0.0RC4 OS: *
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: yajenyak at yandex dot ru
New email:
PHP Version: OS:

 

 [2015-10-11 03:34 UTC] yajenyak at yandex dot ru
Description:
------------
When the ErrorException is thrown in the error handler registered with set_error_handler() the exception handler is not called but the next line in the code is executed which not should happend because the exception handler must be called.

I can't provide exact chunk of the code because the error is not reproducable without context, but the code that I will provide causes the bug happen in the context.

This bug may be related with the https://bugs.php.net/bug.php?id=70210 but I don't have enough knowledge of the PHP internals for now to make conclusions.

Test script:
---------------
// This error code doesn't reproduce the error without context code, but it shows when the bug happens:

class MyHandler {
    public function handleException(\Throwable $e) {
        die('This line WILL NOT be called!');
    }

    public function handleError($errno, $errstr, $errfile, $errline, $context) {
        if ($errno && error_reporting()) {
            // This line should cause the MyHandler::handleException() be called
            // but the the die() in the $myCallback is executed instead,
            // it should not happen!
            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        }
    }

    public function handleError($errno, $errstr, $errfile, $errline, $context) {
        die('called');
    }
}
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
$myHandler = new MyHandler;
set_error_handler([$myHandler, 'handleError']);
set_exception_handler([$myHandler, 'handleException']);
register_shutdown_function([$myHandler, 'shutdown']);

// ------------------------
// In some method of some class:
class MySomeClass {
// ...
    public function doSomething() {
        // ...
        $myCallback = function ($name) {
            die('This line WILL be executed!');
        };
        foreach ($collection as $item) {
            // ...
            try {
                // Trigger error - E_WARNING
                $res = $myCallback();
            } catch (\Throwable $e) {
                die('This line WILL NOT be executed');
            }
            die('This line WILL NOT be executed');
            // ...
         // ...
}

Expected result:
----------------
Calling of the MyHandler::handleException()

Actual result:
--------------
The die() function is called here:      
$myCallback = function ($name) {
    die('This line WILL be executed!');
};

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-10-11 06:05 UTC] yajenyak at yandex dot ru
-Operating System: Arch Linux 4.2.2 +Operating System: Arch Linux 4.2.2, 64-bit
 [2015-10-11 06:05 UTC] yajenyak at yandex dot ru
Updated OS information.
 [2015-10-11 12:22 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2015-10-11 12:22 UTC] requinix@php.net
Really need that repro script because as far as I can tell from that code, it should not repro your problem. Which is what you said is the case. But it looks like what you've given should be close?

By the way, there's a bug in your code:

  if ($errno && error_reporting()) {

That's a boolean &&. What the expression does is check that $errno != 0 (the expression was not @-silenced) and that error_reporting() != 0. What you should be doing is checking that the error_reporting() bitmask has the $errno bit set - using the bitwise &. An easy mistake/typo to make.

  if ($errno & error_reporting()) {
 [2015-10-12 00:13 UTC] yajenyak at yandex dot ru
-Status: Feedback +Status: Open
 [2015-10-12 00:13 UTC] yajenyak at yandex dot ru
Thank you for you comment. I able to reproduce the bug, here is a code:
<?php
class MyHandler {
    public function handleException(\Throwable $e) {
        die('This line WILL NOT be called!');
    }

    public function handleError($errno, $errstr, $errfile, $errline, $context) {
        if ($errno & error_reporting()) {
            // This line should cause the MyHandler::handleException() be called
            // but the the die() in the $myCallback is executed instead,
            // it should not happen!
            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        }
    }

    public function shutdown() {
        die("\n" . __METHOD__ . ' called');
    }
}
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
$myHandler = new MyHandler;
set_error_handler([$myHandler, 'handleError']);
set_exception_handler([$myHandler, 'handleException']);
register_shutdown_function([$myHandler, 'shutdown']);

// ------------------------
// In some method of some class:
class MySomeClass {
    public function test() {
        $method = 'doSomething';
        $res = $this->$method();
    }

    public function doSomething() {
        // ...
        $myCallback = function ($name) {
            die('This line WILL be executed!');
        };
        foreach (['a', 'b', 'c'] as $item) {
            // ...
            try {
                // Trigger error - E_WARNING
                $res = $myCallback();
            } catch (\Throwable $e) {
                die('This line WILL NOT be executed');
            }
            die('This line WILL NOT be executed');
        }
    }
}

$instance = new MySomeClass();
$instance->doSomething();

-------------------------------------------------------------
Part of the output is:
This line WILL be executed!PHP Fatal error:  Uncaught ErrorException: Missing argument 1 for MySomeClass::{closure}(), called in ... on line 44 and defined...
...
 [2015-10-12 00:54 UTC] requinix@php.net
-Status: Open +Status: Verified -Operating System: Arch Linux 4.2.2, 64-bit +Operating System: *
 [2015-10-12 00:54 UTC] requinix@php.net
https://3v4l.org/haASA

So the problem is that the function gets executed even if there is an exception thrown during the call.
 [2015-10-12 09:17 UTC] nikic@php.net
The main problem here is that the return value of http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_execute.c#896 was inverted. Furthermore http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_vm_def.h#4717 does an unconditional HANDLE_EXCEPTION() in the failure case, even though the warning does not necessarily result in an exception. Should probably issue ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION() in the failure case.
 [2015-10-13 09:44 UTC] laruence@php.net
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=a8ae88162fe2ee2f9ca9846ff62c1d4d0d141a6a
Log: Fixed bug #70689 (Exception handler does not work as expected)
 [2015-10-13 09:44 UTC] laruence@php.net
-Status: Verified +Status: Closed
 [2015-10-25 12:42 UTC] ab@php.net
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c5d5aacc79cef552d80b637c266d66692b8ac46d
Log: Fixed bug #70689 (Exception handler does not work as expected)
 [2016-07-20 11:36 UTC] davey@php.net
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=a8ae88162fe2ee2f9ca9846ff62c1d4d0d141a6a
Log: Fixed bug #70689 (Exception handler does not work as expected)
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Oct 09 06:01:26 2024 UTC