Enjoy unlimited access to all forum features for FREE! Optional upgrade available for extra perks.

Shutdown function php

Discussion in 'Scripts and Coding' started by Skinner, Feb 5, 2014.

Thread Status:
Not open for further replies.
  1. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    I've run into a slight problem, and wondered if anyone here had any suggestions or idea's.

    I use register_shutdown_function() to catch unexpected errors and perform house cleaning (deleting temp files, unlocking databases, etc) but I'm having problems catching a 500 error.

    When the script times out, I want to perform house cleaning, but as yet nothing seems to work, an option would be to create a 500.php file so when it served it could check the referer then perform defined housecleaning but this would mean maintaining a 500 catch/clean system.

    Does anyone know if you can actually catch a 500 error ?

    My code is as simple as

    PHP:
    register_shutdown_function"house_cleaning" );

    function 
    house_cleaning() {
     
    // perform required tasks
     // possibly restart process if incomplete
    }
    I assume from within the shutdown function, I can access the resources from the main script ?
     
    Last edited: Feb 5, 2014
  2. DaveP United Kingdom

    DaveP Well-Known Member

    Joined:
    Mar 2010
    Posts:
    2,941
    Likes Received:
    56
    register_shutdown_function does catch fatal errors, and since 500 is considered a fatal error, the following code should suffice?

    PHP:
    register_shutdown_function('my_shutdown_function');

    // .. do your stuff ..

    function my_shutdown_function()
    {
        
    $err error_get_last();
        
    // Error of type 1 is a fatal error
        
    if($err['type'] == 1) { 
            
    // .. do your stuff ..
        
    }
    }
    Or am I misunderstanding you?
     
  3. tifosi United Kingdom

    tifosi Well-Known Member

    Joined:
    Oct 2004
    Posts:
    3,414
    Likes Received:
    55
    500 is a pre-processing error to say the php parser could not process the php file and convert it into object code. So essentially the code doesn't run.
     
  4. DaveP United Kingdom

    DaveP Well-Known Member

    Joined:
    Mar 2010
    Posts:
    2,941
    Likes Received:
    56
    Well 500 is also raised when an unhandled exception is thrown too, which does raise another point that the unhandled exception should be handled. :D
     
  5. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    The problem is a "request timeout", which occurs due some queries / operations being too big.

    On my local machine / dedi the timeout isn't an issue and the script works fine but on shared hosting, its problematic.
     
  6. Aegean Greece

    Aegean Active Member

    Joined:
    Feb 2011
    Posts:
    740
    Likes Received:
    16
    If you are using 5.1+ and can run a preliminary test without damage, try this in the main index.php for most errors: (without the code tags of course):

    Code:
    
    <?php
    
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR | 
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);
    
    define('ENV', 'dev');
    
    //Custom error handling vars
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);
    
    register_shutdown_function('shut');
    
    set_error_handler('handler');
    
    //Function to catch no user error handler function errors...
    function shut(){
    
        $error = error_get_last();
    
        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }
    
    }
    
    function handler( $errno, $errstr, $errfile, $errline ) {
    
        switch ($errno){
    
            case E_ERROR: // 1 //
                $typestr = 'E_ERROR'; break;
            case E_WARNING: // 2 //
                $typestr = 'E_WARNING'; break;
            case E_PARSE: // 4 //
                $typestr = 'E_PARSE'; break;
            case E_NOTICE: // 8 //
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR: // 16 //
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING: // 32 //
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR: // 64 //
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_CORE_WARNING: // 128 //
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR: // 256 //
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING: // 512 //
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: // 1024 //
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT: // 2048 //
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: // 8192 //
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: // 16384 //
                $typestr = 'E_USER_DEPRECATED'; break;
    
        }
    
        $message = '<b>'.$typestr.': </b>'.$errstr.' in <b>'.$errfile.'</b> on line <b>'.$errline.'</b><br/>';
    
        if(($errno & E_FATAL) && ENV === 'production'){
    
            header('Location: 500.html');
            header('Status: 500 Internal Server Error');
    
        }
    
        if(!($errno & ERROR_REPORTING))
            return;
    
        if(DISPLAY_ERRORS)
            printf('%s', $message);
    
        //Logging error on php file error log...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    
    }
    
    ob_start();
    
    @include 'content.php';
    
    ob_end_flush();
    ?>
    
    
    
    Apologies if I have the wrong end of the stick reg your problem.
     
  7. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    I'll give it a try later on, thanks :)

    This particular problem is operating on a 12gb database, some searches can take upto 8 minutes inc processing. Most queries complete within the time allowed those that don't lock the database.

    All I really need to do is detect when the request timesout / 500 happens, then at least unlock the database, or ideally run the query (which is cached) so its ready to be run later then unlock but I'd settle for simply unlocking the database.
     
  8. monaghan United Kingdom

    monaghan Well-Known Member

    Joined:
    May 2007
    Posts:
    2,126
    Likes Received:
    78
    Perhaps you need to look at optimising the database, 8 minutes for a query is daft, I used to have databases 3 or 4 times that size in a corporate environment and my team would never have had any peace and quite if any query took that long.
     
  9. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    The 8 minutes includes post processing/acting on the data as well, but the main query is using the lions share.

    Its wildcard queries which cause most of the problems, and I'm not sure of any way to easily speed these up aside from more resources.
     
  10. DaveP United Kingdom

    DaveP Well-Known Member

    Joined:
    Mar 2010
    Posts:
    2,941
    Likes Received:
    56
    Cache it.. ;) http://redis.io/

    But still something seems amiss, 8 minutes is a lot of time imo I'm with Monaghan on this one mate.
     
  11. monaghan United Kingdom

    monaghan Well-Known Member

    Joined:
    May 2007
    Posts:
    2,126
    Likes Received:
    78
    Hmm, perhaps you need to be reviewing the design, this is a long time. Perhaps web/PHP is not the best way of doing things if you need to wildcard search over such a huge data-set.

    I'm guessing the "customer" is not sitting in front of the browser waiting 8 minutes, so could you build the system to queue these searches and build a SQL stored procedure to run these queries and store the results for later collection?

    Alternatively, run a 16Gb RAM box and push the data into an in-memory table to gain the necessary speed increase.
     
  12. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    Most queries come in, in under a minute or two but wildcards are a nightmare.

    80% of wildcard searches are done on a unique field, so I cant reduce that, so far just been trying to nail down the where clause to reduce set as much as possible.

    Before I start mega hassle, the shutdown func is easier route for now, all else fails ill disable wildcards until I have a solution.
     
  13. monaghan United Kingdom

    monaghan Well-Known Member

    Joined:
    May 2007
    Posts:
    2,126
    Likes Received:
    78
    even a minute or 2 is huge, I'd be tempted to build a lean table to search and then to pull the main data by key.

    I presume if using PHP the database is likely to be MySQL, so if you must have such a long running query, look at putting the search table into a memory table.

    Database optimisation really seems to be the key to what you need to do rather than attempt to tidy up after failed reads / writes.
     
  14. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    I was thinking about a table which contained just the unique column which is problematic, purely for the wildcard search on that dataset. However modifying all the relevant parts in other files to insert data into both tables (to keep them synced) isn't possible.

    I just ran a query based on 1 column, nearly 500,000 results returned out of 150m and took about 8 seconds to run, a wildcard search returning just over 80,000 rows took 63 seconds, then less than 2 seconds from the cached query.

    If I have no luck with the clean up idea, I'll have to try and tackle it from another angle.

    Was hoping was something I was missing, like the missing ; and 's.
     
  15. monaghan United Kingdom

    monaghan Well-Known Member

    Joined:
    May 2007
    Posts:
    2,126
    Likes Received:
    78
    You don't need to re-code your inserts, just use a trigger to pop the new data into your search table
     
  16. Skinner

    Skinner Well-Known Member

    Joined:
    Jul 2008
    Posts:
    4,616
    Likes Received:
    140
    I didn't even think of triggers.

    I am wondering if the fact the shutdown won't trigger means there could be a bigger issue somewhere in the code or even with the php install. Kinda locked on trying to solve this problem (of why shutdown won't fire) to see the forest :)
     
Thread Status:
Not open for further replies.