Can I catch exit() and die() messages?
Asked Answered
T

8

34

I'd like to be able to catch die() and exit() messages. Is this possible? I'm hoping for something similar to set_error_handler and set_exception_handler. I've looked at register_shutdown_function() but it seems to contain no context for the offending die() and exit() calls.

I realize that die() and exit() are bad ways to handle errors. I am not looking to be told not to do this. :) I am creating a generic system and want to be able to gracefully log exit() and die() if for some reason someone (not me) decides this is a good idea to do.

Triumvir answered 1/10, 2010 at 4:23 Comment(0)
T
4

As best as I can tell this is not really possible. Some of the solutions posted here may work but they require a lot of additional work or many dependencies. There is no way to easily and reliable trap the die() and exit() messages.

Triumvir answered 18/10, 2010 at 19:34 Comment(1)
Well, it is possible by rewriting the script while you read it. The following example tokenizes the input and rewrites exit($code) and die $code to php_exit($code): EDIT: stackoverflow doesn't allow me to add code here, see this post for inspiration: #31183468Reyesreykjavik
C
17

Yes you can, but you need ob_start, ob_get_contents, ob_end_clean and register_shutdown_function

function onDie(){
    $message = ob_get_contents(); // Capture 'Doh'
    ob_end_clean(); // Cleans output buffer
    callWhateverYouWant();
}
register_shutdown_function('onDie');
//...
ob_start(); // You need this to turn on output buffering before using die/exit
@$dumbVar = 1000/0 or die('Doh'); // "@" prevent warning/error from php
//...
ob_end_clean(); // Remember clean your buffer before you need to use echo/print
Centrifugate answered 4/6, 2013 at 18:44 Comment(0)
V
7

According to the PHP manual, shutdown functions should still be notified when die() or exit() is called.

Shutdown functions and object destructors will always be executed even if exit() is called.

It doesn't seem to be possible to get the status sent in exit($status). Unless you can use output buffering to capture it, but I'm not sure how you'd know when to call ob_start().

Vincentvincenta answered 1/10, 2010 at 4:31 Comment(2)
I guess the OP's problem is that he would like to be able to retrieve the error message from the shutdown function.Travel
The shutdown function is being called, but I am not sure if/how I can get the exit/die context at that point. For that matter, it wasn't clear if I'd even know if exit/die is the reason for the shutdown function being called. (it looked like the shutdown function gets called on die, but it gets called when the process terminates after a successful run as well...)Triumvir
S
5

Maybe override_function() could be interesting, if APD is available

Stowers answered 1/10, 2010 at 4:46 Comment(3)
This is an interesting idea! Hoping for a better non-APD solution, though.Triumvir
I don't think that will work. die and exit are language constructs, not functions.Hoekstra
@Stowers Can you repost the link again, the old one is 404.Pernas
T
4

As best as I can tell this is not really possible. Some of the solutions posted here may work but they require a lot of additional work or many dependencies. There is no way to easily and reliable trap the die() and exit() messages.

Triumvir answered 18/10, 2010 at 19:34 Comment(1)
Well, it is possible by rewriting the script while you read it. The following example tokenizes the input and rewrites exit($code) and die $code to php_exit($code): EDIT: stackoverflow doesn't allow me to add code here, see this post for inspiration: #31183468Reyesreykjavik
A
0

Why do not use custom error handling instead? If not, you could always use LD_PRELOAD and C Code injection to catch it :) Or recompile php with your customizations :P

Attemper answered 1/10, 2010 at 7:44 Comment(0)
P
0

If you use the single point of entry method. (index.php) I can recommend this for your error handling:

Short version:

ob_start();
register_shutdown_function('shutdownHandler');

include('something');

define(CLEAN_EXIT, true);

function shutdownHandler() {
    if(!defined("CLEAN_EXIT") || !CLEAN_EXIT) {
        $msg = "Script stopped unexpectedly: ".ob_get_contents();
        //Handle premature die()/exit() here
    }
}

Additional steps and more detailed:

Roughly my way of doing it. I have even more going on than I show here (handling database transactions/rollback/sending e-mails/writing logs/displaying friendly error messages/user error reporting/etc), but this is the basic idea behind all of it).
Hope it helps someone.

<?php

    //Some initialization

    //starting output buffering. (fatalErrorHandler is optional, but I recommend using it) 
    ob_start('fatalErrorHandler');

    //Execute code right at the end. Catch exit() and die() here. But also all other terminations inside PHPs control
    register_shutdown_function('shutdownHandler');

    //handling other errors: Also optional
    set_error_handler('errorHandler');



    try {
        //Include of offensive code
        include(...);
    }
    catch (Exception $ex) {
        //Handling exception. Be careful to not raise exceptions here again. As you can end up in a cycle. 
    }

    //Code reached this point, so it was a clean exit.
    define(CLEAN_EXIT, true);


    //Gets called when the script engine shuts down.
    function shutdownHandler() {

        $status = connection_status();

        $statusText = "";

        switch ($status) {
            case 0:
                if (!defined("CLEAN_EXIT") || !CLEAN_EXIT) {
                                    $msg = "Script stopped unexpectedly: ".ob_get_contents();
                    //Handle premature die()/exit() here
                }
                else {
                    //Clean exit. Just return
                    return;
                }
            case 1: $statusText = "ABORTED (1)"; break;
            case 2: $statusText = "TIMEOUT (2)"; break;
            case 3: $statusText = "ABORTED & TIMEOUT (3)"; break;

            default : $statusText = "UNKNOWN ($status)"; break;
        }

        //Handle other exit variants saved in $statusText here ob_get_contents() can have additional useful information here
    }

    // error handler function  (This is optional in your case)
    function errorHandler($errno, $errstr, $errfile, $errline) {

        $msg = "[$errno] $errstr\nOn line $errline in file $errfile";

        switch ($errno) {
            case E_ERROR:               $msg = "[E_ERROR] ".$msg;               break;
            case E_WARNING:             $msg = "[E_WARNING] ".$msg;             break;
            case E_PARSE:               $msg = "[E_PARSE] ".$msg;               break;
            case E_NOTICE:              $msg = "[E_NOTICE] ".$msg;              break;
            case E_CORE_ERROR:          $msg = "[E_CORE_ERROR] ".$msg;          break;
            case E_CORE_WARNING:        $msg = "[E_CORE_WARNING] ".$msg;        break;
            case E_COMPILE_ERROR:       $msg = "[E_COMPILE_ERROR] ".$msg;       break;
            case E_COMPILE_WARNING:     $msg = "[E_COMPILE_WARNING] ".$msg;     break;
            case E_USER_ERROR:          $msg = "[E_USER_ERROR] ".$msg;          break;
            case E_USER_WARNING:        $msg = "[E_USER_WARNING] ".$msg;        break;
            case E_USER_NOTICE:         $msg = "[E_USER_NOTICE] ".$msg;         break;
            case E_STRICT:              $msg = "[E_STRICT] ".$msg;              break;
            case E_RECOVERABLE_ERROR:   $msg = "[E_RECOVERABLE_ERROR] ".$msg;   break;
            case E_DEPRECATED:          $msg = "[E_DEPRECIATED] ".$msg;         break;
            case E_USER_DEPRICIATED:    $msg = "[E_USER_DEPRICIATED] ".$msg;    break;
            default:                    $msg = "[UNKNOWN] ".$msg;               break;
        }

        //Handle Normal error/notice/warning here. 
        $handled = ...

        if ($handled)
            return true; //handled. Proceed execution
        else
            throw Exception($msg); //Be careful. this might quickly become cyclic. Be sure to have code that catches and handles exceptions. Else die() here after logging/reporting the error.

    }

    function fatalErrorHandler(&$buffer) {

        $matches = null;
        //Checking if the output contains a fatal error
        if (preg_match('/<br \/>\s*<b>([^<>].*)error<\/b>:(.*)<br \/>$/', $buffer, $matches) ) {

            $msg = preg_replace('/<.*?>/','',$matches[2]);

            //Handle Fatal error here

            return "There was an unexpected situation that resulted in an error. We have been informed and will look into it."
         }

         //No fatal exception. Return buffer and continue
         return $buffer;
    }
Phonography answered 15/12, 2017 at 18:37 Comment(0)
P
0

Catching exits is useful in automated tests. The way I do it is I throw a special runtime exception instead of calling exit directly.

<?php

class CatchableExit extends RuntimeException
{
}

class Quitter
{
    public function run($i)
    {
        echo "Quitter called with \$i = $i \n";
        throw new CatchableExit();
    }
}

class Runner
{
    public function run()
    {
        trigger_error('I am a harmless warning', E_USER_WARNING);
        trigger_error('And I am a notice', E_USER_NOTICE);
        for ($i = 0; $i < 10; $i++) {
            $q = new Quitter();
            try {
                $q->run($i);
            } catch (CatchableExit $e) {
            }
        }
    }
}

function exception_handler(Throwable $exception)
{
    if ($exception instanceof CatchableExit) {
        exit();
    }
}

set_exception_handler('exception_handler');

$runner = new Runner();
$runner->run();

Pestiferous answered 13/1, 2023 at 9:14 Comment(0)
B
-4

yes: write a function and use that instead.

function kill($msg){
    // Do your logging..
    exit($msg);
}
Burnaby answered 1/10, 2010 at 5:9 Comment(2)
I appreciate the response, but this is not helpful at all. I want to catch exit() and die() in the event that some code I have not written calls them.Triumvir
i know but that is the closest you can get to it, just one new function and a simple search and replace from exit and die to kill..Burnaby

© 2022 - 2024 — McMap. All rights reserved.