9

In PHP 7.4.0 I see the following warning:
Deprecated: Array and string offset access syntax with curly braces is deprecated in ...
My error / exception handlers are not able to catch and log them.

Example:

<?php
    set_error_handler(function ($errNo, $errStr) {
        echo "set_error_handler: " . $errStr;
    });

    set_exception_handler(function ($exception) {
        echo "set_exception_handler: " . $exception->getMessage();
    });

    $b = 'test';
    $a = $b{1};

The warning is still displayed in the normal output and none of the two handlers is called.

I want to log all errors, exceptions and warnings in my own log but this warning is not being catched by the handlers. Is there a reason for this or a solution to catch and log everything PHP is complaining about (have no access to the servers Apache/PHP log)?

Dave
  • 93
  • 1
  • 5
  • 1
    Maybe because this is detected during parsing, not at runtime. – Barmar Jan 06 '20 at 21:40
  • [Related question](https://stackoverflow.com/questions/7406737/why-dont-php-deprecated-features-as-opposed-to-deprecated-functions-call-the) (not marking as dupe just in case since 0 upvote + didn't personally try the approach given in the answer) – Jeto Jan 06 '20 at 21:54
  • Does this answer your question? [Array and string offset access syntax with curly braces is deprecated](https://stackoverflow.com/questions/59158548/array-and-string-offset-access-syntax-with-curly-braces-is-deprecated) – Martin Zeitler Jan 06 '20 at 22:44
  • @Jeto: The linked answer does not work, since the handler is never called in my case / example. – Dave Jan 06 '20 at 23:25
  • @MartinZeitler: I know it is deprecated but I did not ask for a solution to the deprecation warning. I asked about a possibility to catch and log such a warning. The problem is the normal ways of doing that do not work in this case / with this particular warning. – Dave Jan 06 '20 at 23:27
  • @Barmar: Possibly, but is there no possibility to catch such a warning? I don't want the customer to see the message and I want to have it in my error log. I don't have access to the log on the server, that's why I catch all errors and warnings directly in PHP and log them in my own log file. – Dave Jan 06 '20 at 23:31
  • @Dave You must've missed the part where he uses `include` to include the file raising the warning, after setting the error handler on the parent script. It seems to work (tested locally) but is inconsistent (doesn't trigger the error handler on subsequent executions, as if PHP kept a local cache or something - edit: [it does](https://stackoverflow.com/a/44972855/965834)) and kind of a gimmick anyway. Not sure there's anything else you can do about it. – Jeto Jan 06 '20 at 23:31
  • @Jeto: Does not make a difference. If i create the two handlers in one file and then include antoher file holding `$b = 'test'; $a = $b{1};`, the warning is still the same. The only difference is the file and line of the finding. – Dave Jan 06 '20 at 23:37
  • @Dave Well, it should work, if PHP doesn't have the included file in cache already. It does for me anyway (at least from CLI, I don't have a PHP 7.4 compatible webserver atm). Anyway, that isn't a consistent solution, as mentioned above. – Jeto Jan 06 '20 at 23:40
  • 1
    There are lots of errors that can't be caught with `set_error_handler()`: **The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.** So you shouldn't depend on it as a way of hiding errors from users. – Barmar Jan 07 '20 at 00:39
  • Instead, you should configure errors to go to the log file instead of being output to the user. – Barmar Jan 07 '20 at 00:40

1 Answers1

6

set_error_handler catches messages emitted at run-time, per the docs:

This function can be used for defining your own way of handling errors during runtime

The deprecation warning you're seeing is implemented at compile-time, which by definition occurs before run-time:

static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
    if (ast->attr == ZEND_DIM_ALTERNATIVE_SYNTAX) {
        zend_error(E_DEPRECATED, "Array and string offset access syntax with curly braces is deprecated");
    }
    ...

You might think of it as a "soft syntax error": it's discovered while parsing, during the compilation step, but rather than being a hard fatal, it's a warning of future doom. Like syntax errors, you have two ways of handling them: prepending and shutdown handling.

Prepending

$ cat error-handler.php
<?php
set_error_handler(fn(...$args) => var_dump($args));
$ cat try.php
<?php
$b = 'test';
$a = $b{1};
$ php -d auto_prepend_file=error-handler.php try.php
array(5) {
  [0]=>
  int(8192)
  [1]=>
  string(69) "Array and string offset access syntax with curly braces is deprecated"
  [2]=>
  string(21) "/Users/bishop/try.php"
  [3]=>
  int(3)
  [4]=>
  NULL
}

Shutdown handling

register_shutdown_function(fn() => var_dump(error_get_last()));

$b = 'test';
$a = $b{1};

Outputs:

array(4) {
  ["type"]=>
  int(8192)
  ["message"]=>
  string(69) "Array and string offset access syntax with curly braces is deprecated"
  ["file"]=>
  string(9) "/in/212CF"
  ["line"]=>
  int(7)
}

Which to use?

Choose either or both, depending upon your needs. Personally, I use the prepend approach, as it handles various other White Screen of Death scenarios. If you're doing this in web context, you'll need to arrange your web server to set the auto_prepend_file setting: once your "main" code's running, you can't set prepend and have it work as demonstrated here.

bishop
  • 37,830
  • 11
  • 104
  • 139
  • 2
    Excellent answer; that [prepending flag](https://www.php.net/manual/en/ini.core.php#ini.auto-prepend-file) is pretty good to know about, and it also makes sense. It's also much better than the shutdown callback, which will only "catch" the last error (especially in this case, since those are non-blocking notices). – Jeto Jan 07 '20 at 06:42
  • is this true for trigger_error( E_USER_DEPRECATED )? – Brad Oct 01 '21 at 05:37