2

When using php redis as a session handler with session locking if there is an error where a lock cannot happen a php notice is thrown and the script continues. Is there a way to configure it for a fatal error and the script NOT process at all?

Chris Muench
  • 17,444
  • 70
  • 209
  • 362
  • you could use [control structures](https://www.php.net/manual/en/language.control-structures.php)/[conditionals](https://www.php.net/manual/en/control-structures.if.php) to check the session locking, if it fails, `throw new Exception()` – treyBake Feb 03 '20 at 16:40
  • I don't see a way to do this because the locking happens outside of my application via php redis module – Chris Muench Feb 03 '20 at 19:23
  • Aside from editing the package code where the error handling occurs, this may help you: https://stackoverflow.com/questions/51364016/is-there-any-way-to-notice-e-notice-in-php-code – SJacks Sep 21 '20 at 18:36
  • 1
    This is exactly what was I was looking for. Thanks! – Chris Muench Sep 21 '20 at 18:51

2 Answers2

1

Make sure that you follow this documentation statement:

Locking feature is currently only supported for Redis setup with single master instance

If you still have some problems then a quick fix for whole application could be set your own error handler with set_error_handler that takes care for that particular problem. An example handler below that reacts only to using not declared variable. Change the regex pattern so it matches your Redis error msg.

<?php

error_reporting(E_ALL);

$redisNoticeFunction = function($errno, $errstr, $errfile, $errline, array $errcontext) {

    // is the error E_NOTICE?
    if ($errno === \E_NOTICE) {
            
        // if error was suppressed with the @-operator
        if (0 === \error_reporting()) {
            // do nothing, continue execution of script
            return;
        }
        
        // check if notice is about Redis
        $pattern = '/.*Undefined variable.*/'; // <== change pattern so it matches notice about Redis
        $subject = $errstr;
        $status = \preg_match($pattern, $subject);
        
        // if there was a problem with regex
        if (false === $status) {
            $msg = 'Could not perform preg_math with pattern: ' . $pattern . ' and subject: ' . $subject . \PHP_EOL;
            // exit by throwing an exception
            throw new \InvalidArgumentException($msg);
        }

        // if notice was about about Redis
        if ($status) {
            $error_msg = 'Redis locking problem with notice msg: ' . $errstr;
            $error_msg .= ' at ' . $errfile . ':' . $errline . \PHP_EOL;
            
            // signal fatal error
            $error_type = \E_USER_ERROR;
            \trigger_error ($error_msg, $error_type);
            
            // or throw exception (comment above line with \trigger_error())
            throw new \RuntimeException($error_msg);
        }

        // the notice was not related to the Redis
        // echo its message and continue script
        echo $errstr . \PHP_EOL;
        return;
    }

    // the error was not the E_NOTICE
    // do other error handling if needed

    // or just end the script
    die;

};

set_error_handler($redisNoticeFunction);    

echo @ $notDeclared; // does not end script

echo $notDeclared; // end script or throws an exception

the last line

echo $notDeclared; // end script or throws an exception

ends script with info:

Fatal error: Redis locking problem with notice msg: Undefined variable: notDeclared at /tmp/index.php:59

set_error_handler should be used at the beginning of your php script (perfectly inside a common file that is required everywhere like a bootstrap.php)

The downside of using your own error handler is that if you work with a framework or other wrapping code it might already set its own error handler that provides some useful features like debugging or maybe even that error handler is essential to that framework error handling, logging etc.

If you wish to restore to original error handler (that default from PHP or the one that was set before your own) use restore_error_handler

Jimmix
  • 5,644
  • 6
  • 44
  • 71
0

In case anyone wants to know the exact code for turning session_lock notice into fatal error; here it is. (I couldn't have done it without the other answer, but this is exactly what I used)

function redisNoticeFunction($errno, $errstr, $errfile, $errline, array $errcontext) 
{
    // is the error E_NOTICE?
    if ($errno === E_NOTICE) 
    {
        // if error was suppressed with the @-operator
        if (0 === error_reporting()) 
        {
            // do nothing, continue execution of script
            return;
        }
    
        // if notice was about about Redis locking
        if ($errstr == 'session_start(): Acquire of session lock was not successful') 
        {
            $error_msg = 'Redis locking problem with notice msg: ' . $errstr;
            $error_msg .= ' at ' . $errfile . ':' . $errline . \PHP_EOL;

            // signal fatal error
            $error_type = E_USER_ERROR;
            trigger_error ($error_msg, $error_type);
        }
        return;
    }
}

$current_error_reporting = error_reporting();
error_reporting(E_ALL);
set_error_handler('redisNoticeFunction');


session_start();
//Use regular error handling if session_start() does not end in a lock
restore_error_handler();
error_reporting($current_error_reporting);
Chris Muench
  • 17,444
  • 70
  • 209
  • 362